994ddd79c7
Python 3 deprecated the logger.warn method, see: https://docs.python.org/3/library/logging.html#logging.warning so we prefer to use warning to avoid DeprecationWarning. In addition to this there will be a hacking rule to prevent this (https://review.openstack.org/#/c/262257/) TrivialFix Change-Id: Iec8499951cd10a910a4d8ecdac95f5bb53a2e86e
709 lines
28 KiB
Python
709 lines
28 KiB
Python
# Copyright 2014 VMware, Inc.
|
|
# All Rights Reserved
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
|
|
from neutron.common import exceptions as exception
|
|
from oslo_config import cfg
|
|
from oslo_log import log
|
|
from oslo_serialization import jsonutils
|
|
from oslo_utils import excutils
|
|
import six
|
|
|
|
from vmware_nsx._i18n import _, _LE, _LI, _LW
|
|
from vmware_nsx.api_client import exception as api_exc
|
|
from vmware_nsx.common import exceptions as nsx_exc
|
|
from vmware_nsx.common import utils
|
|
from vmware_nsx.nsxlib import mh as nsxlib
|
|
from vmware_nsx.nsxlib.mh import switch
|
|
from vmware_nsx.nsxlib.mh import versioning
|
|
|
|
# @versioning.versioned decorator makes the apparent function body
|
|
# totally unrelated to the real function. This confuses pylint :(
|
|
# pylint: disable=assignment-from-no-return
|
|
|
|
HTTP_GET = "GET"
|
|
HTTP_POST = "POST"
|
|
HTTP_DELETE = "DELETE"
|
|
HTTP_PUT = "PUT"
|
|
|
|
LROUTER_RESOURCE = "lrouter"
|
|
LROUTER_RESOURCE = "lrouter"
|
|
LROUTERPORT_RESOURCE = "lport/%s" % LROUTER_RESOURCE
|
|
LROUTERRIB_RESOURCE = "rib/%s" % LROUTER_RESOURCE
|
|
LROUTERNAT_RESOURCE = "nat/lrouter"
|
|
# Constants for NAT rules
|
|
MATCH_KEYS = ["destination_ip_addresses", "destination_port_max",
|
|
"destination_port_min", "source_ip_addresses",
|
|
"source_port_max", "source_port_min", "protocol"]
|
|
|
|
LOG = log.getLogger(__name__)
|
|
|
|
|
|
def _prepare_lrouter_body(name, neutron_router_id, tenant_id,
|
|
router_type, distributed=None, **kwargs):
|
|
body = {
|
|
"display_name": utils.check_and_truncate(name),
|
|
"tags": utils.get_tags(os_tid=tenant_id,
|
|
q_router_id=neutron_router_id),
|
|
"routing_config": {
|
|
"type": router_type
|
|
},
|
|
"type": "LogicalRouterConfig",
|
|
"replication_mode": cfg.CONF.NSX.replication_mode,
|
|
}
|
|
# add the distributed key only if not None (ie: True or False)
|
|
if distributed is not None:
|
|
body['distributed'] = distributed
|
|
if kwargs:
|
|
body["routing_config"].update(kwargs)
|
|
return body
|
|
|
|
|
|
def _create_implicit_routing_lrouter(cluster, neutron_router_id, tenant_id,
|
|
display_name, nexthop, distributed=None):
|
|
implicit_routing_config = {
|
|
"default_route_next_hop": {
|
|
"gateway_ip_address": nexthop,
|
|
"type": "RouterNextHop"
|
|
},
|
|
}
|
|
lrouter_obj = _prepare_lrouter_body(
|
|
display_name, neutron_router_id, tenant_id,
|
|
"SingleDefaultRouteImplicitRoutingConfig",
|
|
distributed=distributed,
|
|
**implicit_routing_config)
|
|
return nsxlib.do_request(HTTP_POST,
|
|
nsxlib._build_uri_path(LROUTER_RESOURCE),
|
|
jsonutils.dumps(lrouter_obj), cluster=cluster)
|
|
|
|
|
|
def create_implicit_routing_lrouter(cluster, neutron_router_id, tenant_id,
|
|
display_name, nexthop):
|
|
"""Create a NSX logical router on the specified cluster.
|
|
|
|
:param cluster: The target NSX cluster
|
|
:param tenant_id: Identifier of the Openstack tenant for which
|
|
the logical router is being created
|
|
:param display_name: Descriptive name of this logical router
|
|
:param nexthop: External gateway IP address for the logical router
|
|
:raise NsxApiException: if there is a problem while communicating
|
|
with the NSX controller
|
|
"""
|
|
return _create_implicit_routing_lrouter(
|
|
cluster, neutron_router_id, tenant_id, display_name, nexthop)
|
|
|
|
|
|
def create_implicit_routing_lrouter_with_distribution(
|
|
cluster, neutron_router_id, tenant_id, display_name,
|
|
nexthop, distributed=None):
|
|
"""Create a NSX logical router on the specified cluster.
|
|
|
|
This function also allows for creating distributed lrouters
|
|
:param cluster: The target NSX cluster
|
|
:param tenant_id: Identifier of the Openstack tenant for which
|
|
the logical router is being created
|
|
:param display_name: Descriptive name of this logical router
|
|
:param nexthop: External gateway IP address for the logical router
|
|
:param distributed: True for distributed logical routers
|
|
:raise NsxApiException: if there is a problem while communicating
|
|
with the NSX controller
|
|
"""
|
|
return _create_implicit_routing_lrouter(
|
|
cluster, neutron_router_id, tenant_id,
|
|
display_name, nexthop, distributed)
|
|
|
|
|
|
def create_explicit_routing_lrouter(cluster, neutron_router_id, tenant_id,
|
|
display_name, nexthop, distributed=None):
|
|
lrouter_obj = _prepare_lrouter_body(
|
|
display_name, neutron_router_id, tenant_id,
|
|
"RoutingTableRoutingConfig", distributed=distributed)
|
|
router = nsxlib.do_request(HTTP_POST,
|
|
nsxlib._build_uri_path(LROUTER_RESOURCE),
|
|
jsonutils.dumps(lrouter_obj), cluster=cluster)
|
|
default_gw = {'prefix': '0.0.0.0/0', 'next_hop_ip': nexthop}
|
|
create_explicit_route_lrouter(cluster, router['uuid'], default_gw)
|
|
return router
|
|
|
|
|
|
def delete_lrouter(cluster, lrouter_id):
|
|
nsxlib.do_request(HTTP_DELETE,
|
|
nsxlib._build_uri_path(LROUTER_RESOURCE,
|
|
resource_id=lrouter_id),
|
|
cluster=cluster)
|
|
|
|
|
|
def get_lrouter(cluster, lrouter_id):
|
|
return nsxlib.do_request(HTTP_GET,
|
|
nsxlib._build_uri_path(
|
|
LROUTER_RESOURCE,
|
|
resource_id=lrouter_id,
|
|
relations='LogicalRouterStatus'),
|
|
cluster=cluster)
|
|
|
|
|
|
def query_lrouters(cluster, fields=None, filters=None):
|
|
return nsxlib.get_all_query_pages(
|
|
nsxlib._build_uri_path(LROUTER_RESOURCE,
|
|
fields=fields,
|
|
relations='LogicalRouterStatus',
|
|
filters=filters),
|
|
cluster)
|
|
|
|
|
|
def get_lrouters(cluster, tenant_id, fields=None, filters=None):
|
|
# FIXME(salv-orlando): Fields parameter is ignored in this routine
|
|
actual_filters = {}
|
|
if filters:
|
|
actual_filters.update(filters)
|
|
if tenant_id:
|
|
actual_filters['tag'] = tenant_id
|
|
actual_filters['tag_scope'] = 'os_tid'
|
|
lrouter_fields = "uuid,display_name,fabric_status,tags"
|
|
return query_lrouters(cluster, lrouter_fields, actual_filters)
|
|
|
|
|
|
def update_implicit_routing_lrouter(cluster, r_id, display_name, nexthop):
|
|
lrouter_obj = get_lrouter(cluster, r_id)
|
|
if not display_name and not nexthop:
|
|
# Nothing to update
|
|
return lrouter_obj
|
|
# It seems that this is faster than the doing an if on display_name
|
|
lrouter_obj["display_name"] = (utils.check_and_truncate(display_name) or
|
|
lrouter_obj["display_name"])
|
|
if nexthop:
|
|
nh_element = lrouter_obj["routing_config"].get(
|
|
"default_route_next_hop")
|
|
if nh_element:
|
|
nh_element["gateway_ip_address"] = nexthop
|
|
return nsxlib.do_request(HTTP_PUT,
|
|
nsxlib._build_uri_path(LROUTER_RESOURCE,
|
|
resource_id=r_id),
|
|
jsonutils.dumps(lrouter_obj),
|
|
cluster=cluster)
|
|
|
|
|
|
def get_explicit_routes_lrouter(cluster, router_id, protocol_type='static'):
|
|
static_filter = {'protocol': protocol_type}
|
|
existing_routes = nsxlib.do_request(
|
|
HTTP_GET,
|
|
nsxlib._build_uri_path(LROUTERRIB_RESOURCE,
|
|
filters=static_filter,
|
|
fields="*",
|
|
parent_resource_id=router_id),
|
|
cluster=cluster)['results']
|
|
return existing_routes
|
|
|
|
|
|
def delete_explicit_route_lrouter(cluster, router_id, route_id):
|
|
nsxlib.do_request(HTTP_DELETE,
|
|
nsxlib._build_uri_path(LROUTERRIB_RESOURCE,
|
|
resource_id=route_id,
|
|
parent_resource_id=router_id),
|
|
cluster=cluster)
|
|
|
|
|
|
def create_explicit_route_lrouter(cluster, router_id, route):
|
|
next_hop_ip = route.get("nexthop") or route.get("next_hop_ip")
|
|
prefix = route.get("destination") or route.get("prefix")
|
|
uuid = nsxlib.do_request(
|
|
HTTP_POST,
|
|
nsxlib._build_uri_path(LROUTERRIB_RESOURCE,
|
|
parent_resource_id=router_id),
|
|
jsonutils.dumps({
|
|
"action": "accept",
|
|
"next_hop_ip": next_hop_ip,
|
|
"prefix": prefix,
|
|
"protocol": "static"
|
|
}),
|
|
cluster=cluster)['uuid']
|
|
return uuid
|
|
|
|
|
|
def update_explicit_routes_lrouter(cluster, router_id, routes):
|
|
# Update in bulk: delete them all, and add the ones specified
|
|
# but keep track of what is been modified to allow roll-backs
|
|
# in case of failures
|
|
nsx_routes = get_explicit_routes_lrouter(cluster, router_id)
|
|
try:
|
|
deleted_routes = []
|
|
added_routes = []
|
|
# omit the default route (0.0.0.0/0) from the processing;
|
|
# this must be handled through the nexthop for the router
|
|
for route in nsx_routes:
|
|
prefix = route.get("destination") or route.get("prefix")
|
|
if prefix != '0.0.0.0/0':
|
|
delete_explicit_route_lrouter(cluster,
|
|
router_id,
|
|
route['uuid'])
|
|
deleted_routes.append(route)
|
|
for route in routes:
|
|
prefix = route.get("destination") or route.get("prefix")
|
|
if prefix != '0.0.0.0/0':
|
|
uuid = create_explicit_route_lrouter(cluster,
|
|
router_id, route)
|
|
added_routes.append(uuid)
|
|
except api_exc.NsxApiException:
|
|
LOG.exception(_LE('Cannot update NSX routes %(routes)s for '
|
|
'router %(router_id)s'),
|
|
{'routes': routes, 'router_id': router_id})
|
|
# Roll back to keep NSX in consistent state
|
|
with excutils.save_and_reraise_exception():
|
|
if nsx_routes:
|
|
if deleted_routes:
|
|
for route in deleted_routes:
|
|
create_explicit_route_lrouter(cluster,
|
|
router_id, route)
|
|
if added_routes:
|
|
for route_id in added_routes:
|
|
delete_explicit_route_lrouter(cluster,
|
|
router_id, route_id)
|
|
return nsx_routes
|
|
|
|
|
|
def get_default_route_explicit_routing_lrouter_v33(cluster, router_id):
|
|
static_filter = {"protocol": "static",
|
|
"prefix": "0.0.0.0/0"}
|
|
default_route = nsxlib.do_request(
|
|
HTTP_GET,
|
|
nsxlib._build_uri_path(LROUTERRIB_RESOURCE,
|
|
filters=static_filter,
|
|
fields="*",
|
|
parent_resource_id=router_id),
|
|
cluster=cluster)["results"][0]
|
|
return default_route
|
|
|
|
|
|
def get_default_route_explicit_routing_lrouter_v32(cluster, router_id):
|
|
# Scan all routes because 3.2 does not support query by prefix
|
|
all_routes = get_explicit_routes_lrouter(cluster, router_id)
|
|
for route in all_routes:
|
|
if route['prefix'] == '0.0.0.0/0':
|
|
return route
|
|
|
|
|
|
def update_default_gw_explicit_routing_lrouter(cluster, router_id, next_hop):
|
|
default_route = get_default_route_explicit_routing_lrouter(cluster,
|
|
router_id)
|
|
if next_hop != default_route["next_hop_ip"]:
|
|
new_default_route = {"action": "accept",
|
|
"next_hop_ip": next_hop,
|
|
"prefix": "0.0.0.0/0",
|
|
"protocol": "static"}
|
|
nsxlib.do_request(HTTP_PUT,
|
|
nsxlib._build_uri_path(
|
|
LROUTERRIB_RESOURCE,
|
|
resource_id=default_route['uuid'],
|
|
parent_resource_id=router_id),
|
|
jsonutils.dumps(new_default_route),
|
|
cluster=cluster)
|
|
|
|
|
|
def update_explicit_routing_lrouter(cluster, router_id,
|
|
display_name, next_hop, routes=None):
|
|
update_implicit_routing_lrouter(cluster, router_id, display_name, next_hop)
|
|
if next_hop:
|
|
update_default_gw_explicit_routing_lrouter(cluster,
|
|
router_id, next_hop)
|
|
if routes is not None:
|
|
return update_explicit_routes_lrouter(cluster, router_id, routes)
|
|
|
|
|
|
def query_lrouter_lports(cluster, lr_uuid, fields="*",
|
|
filters=None, relations=None):
|
|
uri = nsxlib._build_uri_path(LROUTERPORT_RESOURCE,
|
|
parent_resource_id=lr_uuid,
|
|
fields=fields, filters=filters,
|
|
relations=relations)
|
|
return nsxlib.do_request(HTTP_GET, uri, cluster=cluster)['results']
|
|
|
|
|
|
def create_router_lport(cluster, lrouter_uuid, tenant_id, neutron_port_id,
|
|
display_name, admin_status_enabled, ip_addresses,
|
|
mac_address=None):
|
|
"""Creates a logical port on the assigned logical router."""
|
|
lport_obj = dict(
|
|
admin_status_enabled=admin_status_enabled,
|
|
display_name=display_name,
|
|
tags=utils.get_tags(os_tid=tenant_id, q_port_id=neutron_port_id),
|
|
ip_addresses=ip_addresses,
|
|
type="LogicalRouterPortConfig"
|
|
)
|
|
# Only add the mac_address to lport_obj if present. This is because
|
|
# when creating the fake_ext_gw there is no mac_address present.
|
|
if mac_address:
|
|
lport_obj['mac_address'] = mac_address
|
|
path = nsxlib._build_uri_path(LROUTERPORT_RESOURCE,
|
|
parent_resource_id=lrouter_uuid)
|
|
result = nsxlib.do_request(HTTP_POST, path, jsonutils.dumps(lport_obj),
|
|
cluster=cluster)
|
|
|
|
LOG.debug("Created logical port %(lport_uuid)s on "
|
|
"logical router %(lrouter_uuid)s",
|
|
{'lport_uuid': result['uuid'],
|
|
'lrouter_uuid': lrouter_uuid})
|
|
return result
|
|
|
|
|
|
def update_router_lport(cluster, lrouter_uuid, lrouter_port_uuid,
|
|
tenant_id, neutron_port_id, display_name,
|
|
admin_status_enabled, ip_addresses):
|
|
"""Updates a logical port on the assigned logical router."""
|
|
lport_obj = dict(
|
|
admin_status_enabled=admin_status_enabled,
|
|
display_name=display_name,
|
|
tags=utils.get_tags(os_tid=tenant_id, q_port_id=neutron_port_id),
|
|
ip_addresses=ip_addresses,
|
|
type="LogicalRouterPortConfig"
|
|
)
|
|
# Do not pass null items to NSX
|
|
for key in lport_obj.keys():
|
|
if lport_obj[key] is None:
|
|
del lport_obj[key]
|
|
path = nsxlib._build_uri_path(LROUTERPORT_RESOURCE,
|
|
lrouter_port_uuid,
|
|
parent_resource_id=lrouter_uuid)
|
|
result = nsxlib.do_request(HTTP_PUT, path,
|
|
jsonutils.dumps(lport_obj),
|
|
cluster=cluster)
|
|
LOG.debug("Updated logical port %(lport_uuid)s on "
|
|
"logical router %(lrouter_uuid)s",
|
|
{'lport_uuid': lrouter_port_uuid, 'lrouter_uuid': lrouter_uuid})
|
|
return result
|
|
|
|
|
|
def delete_router_lport(cluster, lrouter_uuid, lport_uuid):
|
|
"""Creates a logical port on the assigned logical router."""
|
|
path = nsxlib._build_uri_path(LROUTERPORT_RESOURCE, lport_uuid,
|
|
lrouter_uuid)
|
|
nsxlib.do_request(HTTP_DELETE, path, cluster=cluster)
|
|
LOG.debug("Delete logical router port %(lport_uuid)s on "
|
|
"logical router %(lrouter_uuid)s",
|
|
{'lport_uuid': lport_uuid,
|
|
'lrouter_uuid': lrouter_uuid})
|
|
|
|
|
|
def delete_peer_router_lport(cluster, lr_uuid, ls_uuid, lp_uuid):
|
|
nsx_port = switch.get_port(cluster, ls_uuid, lp_uuid,
|
|
relations="LogicalPortAttachment")
|
|
relations = nsx_port.get('_relations')
|
|
if relations:
|
|
att_data = relations.get('LogicalPortAttachment')
|
|
if att_data:
|
|
lrp_uuid = att_data.get('peer_port_uuid')
|
|
if lrp_uuid:
|
|
delete_router_lport(cluster, lr_uuid, lrp_uuid)
|
|
|
|
|
|
def find_router_gw_port(context, cluster, router_id):
|
|
"""Retrieves the external gateway port for a NSX logical router."""
|
|
|
|
# Find the uuid of nsx ext gw logical router port
|
|
# TODO(salvatore-orlando): Consider storing it in Neutron DB
|
|
results = query_lrouter_lports(
|
|
cluster, router_id,
|
|
relations="LogicalPortAttachment")
|
|
for lport in results:
|
|
if '_relations' in lport:
|
|
attachment = lport['_relations'].get('LogicalPortAttachment')
|
|
if attachment and attachment.get('type') == 'L3GatewayAttachment':
|
|
return lport
|
|
|
|
|
|
def plug_router_port_attachment(cluster, router_id, port_id,
|
|
attachment_uuid, nsx_attachment_type,
|
|
attachment_vlan=None):
|
|
"""Attach a router port to the given attachment.
|
|
|
|
Current attachment types:
|
|
- PatchAttachment [-> logical switch port uuid]
|
|
- L3GatewayAttachment [-> L3GatewayService uuid]
|
|
For the latter attachment type a VLAN ID can be specified as well.
|
|
"""
|
|
uri = nsxlib._build_uri_path(LROUTERPORT_RESOURCE, port_id, router_id,
|
|
is_attachment=True)
|
|
attach_obj = {}
|
|
attach_obj["type"] = nsx_attachment_type
|
|
if nsx_attachment_type == "PatchAttachment":
|
|
attach_obj["peer_port_uuid"] = attachment_uuid
|
|
elif nsx_attachment_type == "L3GatewayAttachment":
|
|
attach_obj["l3_gateway_service_uuid"] = attachment_uuid
|
|
if attachment_vlan:
|
|
attach_obj['vlan_id'] = attachment_vlan
|
|
else:
|
|
raise nsx_exc.InvalidAttachmentType(
|
|
attachment_type=nsx_attachment_type)
|
|
return nsxlib.do_request(
|
|
HTTP_PUT, uri, jsonutils.dumps(attach_obj), cluster=cluster)
|
|
|
|
|
|
def _create_nat_match_obj(**kwargs):
|
|
nat_match_obj = {'ethertype': 'IPv4'}
|
|
delta = set(kwargs.keys()) - set(MATCH_KEYS)
|
|
if delta:
|
|
raise Exception(_("Invalid keys for NAT match: %s"), delta)
|
|
nat_match_obj.update(kwargs)
|
|
return nat_match_obj
|
|
|
|
|
|
def _create_lrouter_nat_rule(cluster, router_id, nat_rule_obj):
|
|
LOG.debug("Creating NAT rule: %s", nat_rule_obj)
|
|
uri = nsxlib._build_uri_path(LROUTERNAT_RESOURCE,
|
|
parent_resource_id=router_id)
|
|
return nsxlib.do_request(HTTP_POST, uri, jsonutils.dumps(nat_rule_obj),
|
|
cluster=cluster)
|
|
|
|
|
|
def _build_snat_rule_obj(min_src_ip, max_src_ip, nat_match_obj):
|
|
return {"to_source_ip_address_min": min_src_ip,
|
|
"to_source_ip_address_max": max_src_ip,
|
|
"type": "SourceNatRule",
|
|
"match": nat_match_obj}
|
|
|
|
|
|
def create_lrouter_nosnat_rule_v2(cluster, _router_id, _match_criteria=None):
|
|
LOG.info(_LI("No SNAT rules cannot be applied as they are not available "
|
|
"in this version of the NSX platform"))
|
|
|
|
|
|
def create_lrouter_nodnat_rule_v2(cluster, _router_id, _match_criteria=None):
|
|
LOG.info(_LI("No DNAT rules cannot be applied as they are not available "
|
|
"in this version of the NSX platform"))
|
|
|
|
|
|
def create_lrouter_snat_rule_v2(cluster, router_id,
|
|
min_src_ip, max_src_ip, match_criteria=None):
|
|
|
|
nat_match_obj = _create_nat_match_obj(**match_criteria)
|
|
nat_rule_obj = _build_snat_rule_obj(min_src_ip, max_src_ip, nat_match_obj)
|
|
return _create_lrouter_nat_rule(cluster, router_id, nat_rule_obj)
|
|
|
|
|
|
def create_lrouter_dnat_rule_v2(cluster, router_id, dst_ip,
|
|
to_dst_port=None, match_criteria=None):
|
|
|
|
nat_match_obj = _create_nat_match_obj(**match_criteria)
|
|
nat_rule_obj = {
|
|
"to_destination_ip_address_min": dst_ip,
|
|
"to_destination_ip_address_max": dst_ip,
|
|
"type": "DestinationNatRule",
|
|
"match": nat_match_obj
|
|
}
|
|
if to_dst_port:
|
|
nat_rule_obj['to_destination_port'] = to_dst_port
|
|
return _create_lrouter_nat_rule(cluster, router_id, nat_rule_obj)
|
|
|
|
|
|
def create_lrouter_nosnat_rule_v3(cluster, router_id, order=None,
|
|
match_criteria=None):
|
|
nat_match_obj = _create_nat_match_obj(**match_criteria)
|
|
nat_rule_obj = {
|
|
"type": "NoSourceNatRule",
|
|
"match": nat_match_obj
|
|
}
|
|
if order:
|
|
nat_rule_obj['order'] = order
|
|
return _create_lrouter_nat_rule(cluster, router_id, nat_rule_obj)
|
|
|
|
|
|
def create_lrouter_nodnat_rule_v3(cluster, router_id, order=None,
|
|
match_criteria=None):
|
|
nat_match_obj = _create_nat_match_obj(**match_criteria)
|
|
nat_rule_obj = {
|
|
"type": "NoDestinationNatRule",
|
|
"match": nat_match_obj
|
|
}
|
|
if order:
|
|
nat_rule_obj['order'] = order
|
|
return _create_lrouter_nat_rule(cluster, router_id, nat_rule_obj)
|
|
|
|
|
|
def create_lrouter_snat_rule_v3(cluster, router_id, min_src_ip, max_src_ip,
|
|
order=None, match_criteria=None):
|
|
nat_match_obj = _create_nat_match_obj(**match_criteria)
|
|
nat_rule_obj = _build_snat_rule_obj(min_src_ip, max_src_ip, nat_match_obj)
|
|
if order:
|
|
nat_rule_obj['order'] = order
|
|
return _create_lrouter_nat_rule(cluster, router_id, nat_rule_obj)
|
|
|
|
|
|
def create_lrouter_dnat_rule_v3(cluster, router_id, dst_ip, to_dst_port=None,
|
|
order=None, match_criteria=None):
|
|
|
|
nat_match_obj = _create_nat_match_obj(**match_criteria)
|
|
nat_rule_obj = {
|
|
"to_destination_ip_address": dst_ip,
|
|
"type": "DestinationNatRule",
|
|
"match": nat_match_obj
|
|
}
|
|
if to_dst_port:
|
|
nat_rule_obj['to_destination_port'] = to_dst_port
|
|
if order:
|
|
nat_rule_obj['order'] = order
|
|
return _create_lrouter_nat_rule(cluster, router_id, nat_rule_obj)
|
|
|
|
|
|
def delete_nat_rules_by_match(cluster, router_id, rule_type,
|
|
max_num_expected,
|
|
min_num_expected=0,
|
|
raise_on_len_mismatch=True,
|
|
**kwargs):
|
|
# remove nat rules
|
|
nat_rules = query_nat_rules(cluster, router_id)
|
|
to_delete_ids = []
|
|
for r in nat_rules:
|
|
if (r['type'] != rule_type):
|
|
continue
|
|
|
|
for key, value in six.iteritems(kwargs):
|
|
if not (key in r['match'] and r['match'][key] == value):
|
|
break
|
|
else:
|
|
to_delete_ids.append(r['uuid'])
|
|
num_rules_to_delete = len(to_delete_ids)
|
|
if (num_rules_to_delete < min_num_expected or
|
|
num_rules_to_delete > max_num_expected):
|
|
if raise_on_len_mismatch:
|
|
raise nsx_exc.NatRuleMismatch(actual_rules=num_rules_to_delete,
|
|
min_rules=min_num_expected,
|
|
max_rules=max_num_expected)
|
|
else:
|
|
LOG.warning(_LW("Found %(actual_rule_num)d matching NAT rules, "
|
|
"which is not in the expected range "
|
|
"(%(min_exp_rule_num)d,%(max_exp_rule_num)d)"),
|
|
{'actual_rule_num': num_rules_to_delete,
|
|
'min_exp_rule_num': min_num_expected,
|
|
'max_exp_rule_num': max_num_expected})
|
|
|
|
for rule_id in to_delete_ids:
|
|
delete_router_nat_rule(cluster, router_id, rule_id)
|
|
# Return number of deleted rules - useful at least for
|
|
# testing purposes
|
|
return num_rules_to_delete
|
|
|
|
|
|
def delete_router_nat_rule(cluster, router_id, rule_id):
|
|
uri = nsxlib._build_uri_path(LROUTERNAT_RESOURCE, rule_id, router_id)
|
|
nsxlib.do_request(HTTP_DELETE, uri, cluster=cluster)
|
|
|
|
|
|
def query_nat_rules(cluster, router_id, fields="*", filters=None):
|
|
uri = nsxlib._build_uri_path(LROUTERNAT_RESOURCE,
|
|
parent_resource_id=router_id,
|
|
fields=fields, filters=filters)
|
|
return nsxlib.get_all_query_pages(uri, cluster)
|
|
|
|
|
|
# NOTE(salvatore-orlando): The following FIXME applies in general to
|
|
# each operation on list attributes.
|
|
# FIXME(salvatore-orlando): need a lock around the list of IPs on an iface
|
|
def update_lrouter_port_ips(cluster, lrouter_id, lport_id,
|
|
ips_to_add, ips_to_remove):
|
|
uri = nsxlib._build_uri_path(LROUTERPORT_RESOURCE, lport_id, lrouter_id)
|
|
try:
|
|
port = nsxlib.do_request(HTTP_GET, uri, cluster=cluster)
|
|
# TODO(salvatore-orlando): Enforce ips_to_add intersection with
|
|
# ips_to_remove is empty
|
|
ip_address_set = set(port['ip_addresses'])
|
|
ip_address_set = ip_address_set - set(ips_to_remove)
|
|
ip_address_set = ip_address_set | set(ips_to_add)
|
|
# Set is not JSON serializable - convert to list
|
|
port['ip_addresses'] = list(ip_address_set)
|
|
nsxlib.do_request(HTTP_PUT, uri, jsonutils.dumps(port),
|
|
cluster=cluster)
|
|
except exception.NotFound:
|
|
# FIXME(salv-orlando):avoid raising different exception
|
|
data = {'lport_id': lport_id, 'lrouter_id': lrouter_id}
|
|
msg = (_("Router Port %(lport_id)s not found on router "
|
|
"%(lrouter_id)s") % data)
|
|
LOG.exception(msg)
|
|
raise nsx_exc.NsxPluginException(err_msg=msg)
|
|
except api_exc.NsxApiException as e:
|
|
msg = _("An exception occurred while updating IP addresses on a "
|
|
"router logical port:%s") % e
|
|
LOG.exception(msg)
|
|
raise nsx_exc.NsxPluginException(err_msg=msg)
|
|
|
|
|
|
ROUTER_FUNC_DICT = {
|
|
'create_lrouter': {
|
|
2: {versioning.DEFAULT_VERSION: create_implicit_routing_lrouter, },
|
|
3: {versioning.DEFAULT_VERSION: create_implicit_routing_lrouter,
|
|
1: create_implicit_routing_lrouter_with_distribution,
|
|
2: create_explicit_routing_lrouter, }, },
|
|
'update_lrouter': {
|
|
2: {versioning.DEFAULT_VERSION: update_implicit_routing_lrouter, },
|
|
3: {versioning.DEFAULT_VERSION: update_implicit_routing_lrouter,
|
|
2: update_explicit_routing_lrouter, }, },
|
|
'create_lrouter_dnat_rule': {
|
|
2: {versioning.DEFAULT_VERSION: create_lrouter_dnat_rule_v2, },
|
|
3: {versioning.DEFAULT_VERSION: create_lrouter_dnat_rule_v3, }, },
|
|
'create_lrouter_snat_rule': {
|
|
2: {versioning.DEFAULT_VERSION: create_lrouter_snat_rule_v2, },
|
|
3: {versioning.DEFAULT_VERSION: create_lrouter_snat_rule_v3, }, },
|
|
'create_lrouter_nosnat_rule': {
|
|
2: {versioning.DEFAULT_VERSION: create_lrouter_nosnat_rule_v2, },
|
|
3: {versioning.DEFAULT_VERSION: create_lrouter_nosnat_rule_v3, }, },
|
|
'create_lrouter_nodnat_rule': {
|
|
2: {versioning.DEFAULT_VERSION: create_lrouter_nodnat_rule_v2, },
|
|
3: {versioning.DEFAULT_VERSION: create_lrouter_nodnat_rule_v3, }, },
|
|
'get_default_route_explicit_routing_lrouter': {
|
|
3: {versioning.DEFAULT_VERSION:
|
|
get_default_route_explicit_routing_lrouter_v32,
|
|
2: get_default_route_explicit_routing_lrouter_v32, }, },
|
|
}
|
|
|
|
|
|
@versioning.versioned(ROUTER_FUNC_DICT)
|
|
def create_lrouter(cluster, *args, **kwargs):
|
|
if kwargs.get('distributed', None):
|
|
v = cluster.api_client.get_version()
|
|
if (v.major, v.minor) < (3, 1):
|
|
raise nsx_exc.InvalidVersion(version=v)
|
|
return v
|
|
|
|
|
|
@versioning.versioned(ROUTER_FUNC_DICT)
|
|
def get_default_route_explicit_routing_lrouter(cluster, *args, **kwargs):
|
|
pass
|
|
|
|
|
|
@versioning.versioned(ROUTER_FUNC_DICT)
|
|
def update_lrouter(cluster, *args, **kwargs):
|
|
if kwargs.get('routes', None):
|
|
v = cluster.api_client.get_version()
|
|
if (v.major, v.minor) < (3, 2):
|
|
raise nsx_exc.InvalidVersion(version=v)
|
|
return v
|
|
|
|
|
|
@versioning.versioned(ROUTER_FUNC_DICT)
|
|
def create_lrouter_dnat_rule(cluster, *args, **kwargs):
|
|
pass
|
|
|
|
|
|
@versioning.versioned(ROUTER_FUNC_DICT)
|
|
def create_lrouter_snat_rule(cluster, *args, **kwargs):
|
|
pass
|
|
|
|
|
|
@versioning.versioned(ROUTER_FUNC_DICT)
|
|
def create_lrouter_nosnat_rule(cluster, *args, **kwargs):
|
|
pass
|
|
|
|
|
|
@versioning.versioned(ROUTER_FUNC_DICT)
|
|
def create_lrouter_nodnat_rule(cluster, *args, **kwargs):
|
|
pass
|