Merge "NSXv3: Router GW support"
This commit is contained in:
commit
cd11717917
|
@ -16,6 +16,8 @@
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
from oslo_log import log
|
from oslo_log import log
|
||||||
|
|
||||||
|
from neutron.i18n import _LW
|
||||||
|
|
||||||
from vmware_nsx.neutron.plugins.vmware.common import exceptions as nsx_exc
|
from vmware_nsx.neutron.plugins.vmware.common import exceptions as nsx_exc
|
||||||
from vmware_nsx.neutron.plugins.vmware.common import nsx_constants
|
from vmware_nsx.neutron.plugins.vmware.common import nsx_constants
|
||||||
from vmware_nsx.neutron.plugins.vmware.common import utils
|
from vmware_nsx.neutron.plugins.vmware.common import utils
|
||||||
|
@ -23,6 +25,16 @@ from vmware_nsx.neutron.plugins.vmware.nsxlib.v3 import client
|
||||||
|
|
||||||
LOG = log.getLogger(__name__)
|
LOG = log.getLogger(__name__)
|
||||||
|
|
||||||
|
# TODO(berlin): move them to nsx_constants file
|
||||||
|
# Router logical port types
|
||||||
|
LROUTERPORT_UPLINK = "LogicalRouterUplinkPort"
|
||||||
|
LROUTERPORT_DOWNLINK = "LogicalRouterDownLinkPort"
|
||||||
|
LROUTERPORT_LINK = "LogicalRouterLinkPort"
|
||||||
|
|
||||||
|
LROUTER_TYPES = [LROUTERPORT_UPLINK,
|
||||||
|
LROUTERPORT_DOWNLINK,
|
||||||
|
LROUTERPORT_LINK]
|
||||||
|
|
||||||
ROUTER_INTF_PORT_NAME = "Tier1-RouterDownLinkPort"
|
ROUTER_INTF_PORT_NAME = "Tier1-RouterDownLinkPort"
|
||||||
|
|
||||||
|
|
||||||
|
@ -39,6 +51,32 @@ def update_resource_with_retry(resource, payload):
|
||||||
return client.update_resource(resource, revised_payload)
|
return client.update_resource(resource, revised_payload)
|
||||||
|
|
||||||
|
|
||||||
|
def delete_resource_by_values(resource, res_id='id', skip_not_found=True,
|
||||||
|
**kwargs):
|
||||||
|
resources_get = client.get_resource(resource)
|
||||||
|
for res in resources_get['results']:
|
||||||
|
is_matched = True
|
||||||
|
for (key, value) in kwargs.items():
|
||||||
|
if res.get(key) != value:
|
||||||
|
is_matched = False
|
||||||
|
break
|
||||||
|
if is_matched:
|
||||||
|
LOG.debug("Deleting %s from resource %s", res, resource)
|
||||||
|
delete_resource = resource + "/" + str(res[res_id])
|
||||||
|
client.delete_resource(delete_resource)
|
||||||
|
return
|
||||||
|
if skip_not_found:
|
||||||
|
LOG.warning(_LW("No resource in %(res)s matched for values: "
|
||||||
|
"%(values)s"), {'res': resource,
|
||||||
|
'values': kwargs})
|
||||||
|
else:
|
||||||
|
err_msg = (_("No resource in %(res)s matched for values: %(values)s") %
|
||||||
|
{'res': resource,
|
||||||
|
'values': kwargs})
|
||||||
|
raise nsx_exc.ResourceNotFound(manager=client._get_manager_ip(),
|
||||||
|
operation=err_msg)
|
||||||
|
|
||||||
|
|
||||||
def create_logical_switch(display_name, transport_zone_id, tags,
|
def create_logical_switch(display_name, transport_zone_id, tags,
|
||||||
replication_mode=nsx_constants.MTEP,
|
replication_mode=nsx_constants.MTEP,
|
||||||
admin_state=True, vlan_id=None):
|
admin_state=True, vlan_id=None):
|
||||||
|
@ -181,6 +219,8 @@ def update_logical_router(lrouter_id, **kwargs):
|
||||||
|
|
||||||
|
|
||||||
def delete_logical_router(lrouter_id):
|
def delete_logical_router(lrouter_id):
|
||||||
|
# TODO(berlin): need to verify whether cascade prefix is valid to delete
|
||||||
|
# router link port and its relative nat rules
|
||||||
resource = 'logical-routers/%s/' % lrouter_id
|
resource = 'logical-routers/%s/' % lrouter_id
|
||||||
|
|
||||||
# TODO(salv-orlando): Must handle connection exceptions
|
# TODO(salv-orlando): Must handle connection exceptions
|
||||||
|
@ -213,8 +253,8 @@ def create_logical_router_port_by_ls_id(logical_router_id,
|
||||||
except nsx_exc.ResourceNotFound:
|
except nsx_exc.ResourceNotFound:
|
||||||
return create_logical_router_port(logical_router_id,
|
return create_logical_router_port(logical_router_id,
|
||||||
ROUTER_INTF_PORT_NAME,
|
ROUTER_INTF_PORT_NAME,
|
||||||
logical_switch_port_id,
|
|
||||||
resource_type,
|
resource_type,
|
||||||
|
logical_switch_port_id,
|
||||||
address_groups)
|
address_groups)
|
||||||
else:
|
else:
|
||||||
return update_logical_router_port(port['id'], subnets=address_groups)
|
return update_logical_router_port(port['id'], subnets=address_groups)
|
||||||
|
@ -222,15 +262,23 @@ def create_logical_router_port_by_ls_id(logical_router_id,
|
||||||
|
|
||||||
def create_logical_router_port(logical_router_id,
|
def create_logical_router_port(logical_router_id,
|
||||||
display_name,
|
display_name,
|
||||||
logical_switch_port_id,
|
|
||||||
resource_type,
|
resource_type,
|
||||||
address_groups):
|
logical_port_id,
|
||||||
|
address_groups,
|
||||||
|
edge_cluster_member_index=None):
|
||||||
resource = 'logical-router-ports'
|
resource = 'logical-router-ports'
|
||||||
body = {'display_name': display_name,
|
body = {'display_name': display_name,
|
||||||
'resource_type': resource_type,
|
'resource_type': resource_type,
|
||||||
'logical_router_id': logical_router_id,
|
'logical_router_id': logical_router_id}
|
||||||
'subnets': address_groups,
|
if address_groups:
|
||||||
'linked_logical_switch_port_id': logical_switch_port_id}
|
body['subnets'] = address_groups
|
||||||
|
if resource_type in [LROUTERPORT_UPLINK,
|
||||||
|
LROUTERPORT_DOWNLINK]:
|
||||||
|
body['linked_logical_switch_port_id'] = logical_port_id
|
||||||
|
elif logical_port_id:
|
||||||
|
body['linked_logical_router_port_id'] = logical_port_id
|
||||||
|
if edge_cluster_member_index:
|
||||||
|
body['edge_cluster_member_index'] = edge_cluster_member_index
|
||||||
|
|
||||||
return client.create_resource(resource, body)
|
return client.create_resource(resource, body)
|
||||||
|
|
||||||
|
@ -256,6 +304,56 @@ def delete_logical_router_port(logical_port_id):
|
||||||
client.delete_resource(resource)
|
client.delete_resource(resource)
|
||||||
|
|
||||||
|
|
||||||
|
def get_logical_router_ports_by_router_id(logical_router_id):
|
||||||
|
resource = 'logical-router-ports'
|
||||||
|
logical_router_ports = client.get_resource(
|
||||||
|
resource, logical_router_id=logical_router_id)
|
||||||
|
return logical_router_ports['results']
|
||||||
|
|
||||||
|
|
||||||
|
def get_tier1_logical_router_link_port(logical_router_id):
|
||||||
|
logical_router_ports = get_logical_router_ports_by_router_id(
|
||||||
|
logical_router_id)
|
||||||
|
for port in logical_router_ports:
|
||||||
|
if port['resource_type'] == LROUTERPORT_LINK:
|
||||||
|
return port
|
||||||
|
raise nsx_exc.ResourceNotFound(
|
||||||
|
manager=client._get_manager_ip(),
|
||||||
|
operation="get router link port")
|
||||||
|
|
||||||
|
|
||||||
|
def add_nat_rule(logical_router_id, action, translated_network,
|
||||||
|
source_net=None, dest_net=None,
|
||||||
|
enabled=True, rule_priority=None):
|
||||||
|
resource = 'logical-routers/%s/nat/rules' % logical_router_id
|
||||||
|
body = {'action': action,
|
||||||
|
'enabled': enabled,
|
||||||
|
'translated_network': translated_network}
|
||||||
|
if source_net:
|
||||||
|
body['match_source_network'] = source_net
|
||||||
|
if dest_net:
|
||||||
|
body['match_destination_network'] = dest_net
|
||||||
|
if rule_priority:
|
||||||
|
body['rule_priority'] = rule_priority
|
||||||
|
return client.create_resource(resource, body)
|
||||||
|
|
||||||
|
|
||||||
|
def delete_nat_rule(logical_router_id, nat_rule_id):
|
||||||
|
resource = 'logical-routers/%s/nat/rules/%s' % (logical_router_id,
|
||||||
|
nat_rule_id)
|
||||||
|
client.delete_resource(resource)
|
||||||
|
|
||||||
|
|
||||||
|
def delete_nat_rule_by_values(logical_router_id, **kwargs):
|
||||||
|
resource = 'logical-routers/%s/nat/rules' % logical_router_id
|
||||||
|
return delete_resource_by_values(resource, res_id='rule_id', **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
def update_logical_router_advertisement(logical_router_id, **kwargs):
|
||||||
|
resource = 'logical-routers/%s/routing/advertisement' % logical_router_id
|
||||||
|
return update_resource_with_retry(resource, kwargs)
|
||||||
|
|
||||||
|
|
||||||
def create_qos_switching_profile(tags, qos_marking=None, dscp=None, name=None,
|
def create_qos_switching_profile(tags, qos_marking=None, dscp=None, name=None,
|
||||||
description=None):
|
description=None):
|
||||||
resource = 'switching-profiles'
|
resource = 'switching-profiles'
|
||||||
|
|
|
@ -65,13 +65,14 @@ def _validate_result(result, expected, operation):
|
||||||
raise manager_error(manager=manager_ip, operation=operation)
|
raise manager_error(manager=manager_ip, operation=operation)
|
||||||
|
|
||||||
|
|
||||||
def get_resource(resource):
|
def get_resource(resource, **params):
|
||||||
manager, user, password, verify = _get_manager_endpoint()
|
manager, user, password, verify = _get_manager_endpoint()
|
||||||
url = manager + "/api/v1/%s" % resource
|
url = manager + "/api/v1/%s" % resource
|
||||||
headers = {'Accept': 'application/json'}
|
headers = {'Accept': 'application/json'}
|
||||||
result = requests.get(url, auth=auth.HTTPBasicAuth(user, password),
|
result = requests.get(url, auth=auth.HTTPBasicAuth(user, password),
|
||||||
verify=verify, headers=headers,
|
verify=verify, headers=headers,
|
||||||
cert=cfg.CONF.nsx_v3.ca_file)
|
cert=cfg.CONF.nsx_v3.ca_file,
|
||||||
|
params=params)
|
||||||
_validate_result(result, [requests.codes.ok],
|
_validate_result(result, [requests.codes.ok],
|
||||||
_("reading resource: %s") % resource)
|
_("reading resource: %s") % resource)
|
||||||
return result.json()
|
return result.json()
|
||||||
|
|
|
@ -0,0 +1,128 @@
|
||||||
|
# Copyright 2015 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.
|
||||||
|
|
||||||
|
"""
|
||||||
|
NSX-V3 Plugin router module
|
||||||
|
"""
|
||||||
|
|
||||||
|
import random
|
||||||
|
|
||||||
|
from neutron.common import exceptions as n_exc
|
||||||
|
from neutron.i18n import _LW
|
||||||
|
from oslo_log import log
|
||||||
|
|
||||||
|
from vmware_nsx.neutron.plugins.vmware.common import exceptions as nsx_exc
|
||||||
|
from vmware_nsx.neutron.plugins.vmware.nsxlib import v3 as nsxlib
|
||||||
|
|
||||||
|
LOG = log.getLogger(__name__)
|
||||||
|
|
||||||
|
# TODO(berlin): Remove this when we merges the edge node auto
|
||||||
|
# placement feature.
|
||||||
|
MIN_EDGE_NODE_NUM = 1
|
||||||
|
|
||||||
|
TIER0_ROUTER_LINK_PORT_NAME = "TIER0-RouterLinkPort"
|
||||||
|
TIER1_ROUTER_LINK_PORT_NAME = "TIER1-RouterLinkPort"
|
||||||
|
|
||||||
|
|
||||||
|
def validate_tier0(tier0_groups_dict, tier0_uuid):
|
||||||
|
if tier0_uuid in tier0_groups_dict:
|
||||||
|
return
|
||||||
|
err_msg = None
|
||||||
|
try:
|
||||||
|
lrouter = nsxlib.get_logical_router(tier0_uuid)
|
||||||
|
except nsx_exc.ResourceNotFound:
|
||||||
|
err_msg = _("Failed to validate tier0 router %s since it is "
|
||||||
|
"not found at the backend") % tier0_uuid
|
||||||
|
else:
|
||||||
|
edge_cluster_uuid = lrouter.get('edge_cluster_id')
|
||||||
|
if not edge_cluster_uuid:
|
||||||
|
err_msg = _("Failed to get edge cluster uuid from tier0 "
|
||||||
|
"router %s at the backend") % lrouter
|
||||||
|
else:
|
||||||
|
edge_cluster = nsxlib.get_edge_cluster(edge_cluster_uuid)
|
||||||
|
member_index_list = [member['member_index']
|
||||||
|
for member in edge_cluster['members']]
|
||||||
|
if len(member_index_list) < MIN_EDGE_NODE_NUM:
|
||||||
|
err_msg = _("%(act_num)s edge members found in "
|
||||||
|
"edge_cluster %(cluster_id)s, however we "
|
||||||
|
"require at least %(exp_num)s edge nodes "
|
||||||
|
"in edge cluster for HA use.") % {
|
||||||
|
'act_num': len(member_index_list),
|
||||||
|
'exp_num': MIN_EDGE_NODE_NUM,
|
||||||
|
'cluster_id': edge_cluster_uuid}
|
||||||
|
if err_msg:
|
||||||
|
raise n_exc.InvalidInput(error_message=err_msg)
|
||||||
|
else:
|
||||||
|
tier0_groups_dict[tier0_uuid] = {
|
||||||
|
'edge_cluster_uuid': edge_cluster_uuid,
|
||||||
|
'member_index_list': member_index_list}
|
||||||
|
|
||||||
|
|
||||||
|
def add_router_link_port(tier1_uuid, tier0_uuid, edge_members):
|
||||||
|
# Create Tier0 logical router link port
|
||||||
|
tier0_link_port = nsxlib.create_logical_router_port(
|
||||||
|
tier0_uuid, display_name=TIER0_ROUTER_LINK_PORT_NAME,
|
||||||
|
resource_type=nsxlib.LROUTERPORT_LINK,
|
||||||
|
logical_port_id=None,
|
||||||
|
address_groups=None)
|
||||||
|
linked_logical_port_id = tier0_link_port['id']
|
||||||
|
|
||||||
|
edge_cluster_member_index = random.sample(
|
||||||
|
edge_members, MIN_EDGE_NODE_NUM)
|
||||||
|
# Create Tier1 logical router link port
|
||||||
|
nsxlib.create_logical_router_port(
|
||||||
|
tier1_uuid, display_name=TIER1_ROUTER_LINK_PORT_NAME,
|
||||||
|
resource_type=nsxlib.LROUTERPORT_LINK,
|
||||||
|
logical_port_id=linked_logical_port_id,
|
||||||
|
address_groups=None,
|
||||||
|
edge_cluster_member_index=edge_cluster_member_index)
|
||||||
|
|
||||||
|
|
||||||
|
def remove_router_link_port(tier1_uuid, tier0_uuid):
|
||||||
|
try:
|
||||||
|
tier1_link_port = nsxlib.get_tier1_logical_router_link_port(
|
||||||
|
tier1_uuid)
|
||||||
|
except nsx_exc.ResourceNotFound:
|
||||||
|
LOG.warning(_LW("Logical router link port for tier1 router: %s "
|
||||||
|
"not found at the backend"), tier1_uuid)
|
||||||
|
return
|
||||||
|
tier1_link_port_id = tier1_link_port['id']
|
||||||
|
tier0_link_port_id = tier1_link_port['linked_logical_router_port_id']
|
||||||
|
nsxlib.delete_logical_router_port(tier1_link_port_id)
|
||||||
|
nsxlib.delete_logical_router_port(tier0_link_port_id)
|
||||||
|
|
||||||
|
|
||||||
|
def update_advertisement(logical_router_id, advertise_route_nat,
|
||||||
|
advertise_route_connected):
|
||||||
|
return nsxlib.update_logical_router_advertisement(
|
||||||
|
logical_router_id,
|
||||||
|
advertise_nat_routes=advertise_route_nat,
|
||||||
|
advertise_connected_routes=advertise_route_connected)
|
||||||
|
|
||||||
|
|
||||||
|
def delete_gw_snat_rule(logical_router_id, gw_ip):
|
||||||
|
return nsxlib.delete_nat_rule_by_values(logical_router_id,
|
||||||
|
translated_network=gw_ip)
|
||||||
|
|
||||||
|
|
||||||
|
def add_gw_snat_rule(logical_router_id, gw_ip):
|
||||||
|
return nsxlib.add_nat_rule(logical_router_id, action="SNAT",
|
||||||
|
translated_network=gw_ip,
|
||||||
|
rule_priority=1000)
|
||||||
|
|
||||||
|
|
||||||
|
def update_router_edge_cluster(nsx_router_id, edge_cluster_uuid):
|
||||||
|
return nsxlib.update_logical_router(nsx_router_id,
|
||||||
|
edge_cluster_id=edge_cluster_uuid)
|
|
@ -63,6 +63,7 @@ from vmware_nsx.neutron.plugins.vmware.common import utils
|
||||||
from vmware_nsx.neutron.plugins.vmware.dbexts import db as nsx_db
|
from vmware_nsx.neutron.plugins.vmware.dbexts import db as nsx_db
|
||||||
from vmware_nsx.neutron.plugins.vmware.nsxlib import v3 as nsxlib
|
from vmware_nsx.neutron.plugins.vmware.nsxlib import v3 as nsxlib
|
||||||
from vmware_nsx.neutron.plugins.vmware.nsxlib.v3 import dfw_api as firewall
|
from vmware_nsx.neutron.plugins.vmware.nsxlib.v3 import dfw_api as firewall
|
||||||
|
from vmware_nsx.neutron.plugins.vmware.nsxlib.v3 import router as routerlib
|
||||||
from vmware_nsx.neutron.plugins.vmware.nsxlib.v3 import security
|
from vmware_nsx.neutron.plugins.vmware.nsxlib.v3 import security
|
||||||
|
|
||||||
LOG = log.getLogger(__name__)
|
LOG = log.getLogger(__name__)
|
||||||
|
@ -191,35 +192,11 @@ class NsxV3Plugin(db_base_plugin_v2.NeutronDbPluginV2,
|
||||||
|
|
||||||
return net_type, physical_net, vlan_id
|
return net_type, physical_net, vlan_id
|
||||||
|
|
||||||
def _validate_tier0(self, tier0_uuid):
|
def _get_edge_cluster_and_members(self, tier0_uuid):
|
||||||
if tier0_uuid in self.tier0_groups_dict:
|
routerlib.validate_tier0(self.tier0_groups_dict, tier0_uuid)
|
||||||
return
|
tier0_info = self.tier0_groups_dict[tier0_uuid]
|
||||||
err_msg = None
|
return (tier0_info['edge_cluster_uuid'],
|
||||||
try:
|
tier0_info['member_index_list'])
|
||||||
lrouter = nsxlib.get_logical_router(tier0_uuid)
|
|
||||||
except nsx_exc.ResourceNotFound:
|
|
||||||
err_msg = _("Failed to validate tier0 router %s since it is "
|
|
||||||
"not found at the backend") % tier0_uuid
|
|
||||||
else:
|
|
||||||
edge_cluster_uuid = lrouter.get('edge_cluster_id')
|
|
||||||
if not edge_cluster_uuid:
|
|
||||||
err_msg = _("Failed to get edge cluster uuid from tier0 "
|
|
||||||
"router %s at the backend") % lrouter
|
|
||||||
else:
|
|
||||||
edge_cluster = nsxlib.get_edge_cluster(edge_cluster_uuid)
|
|
||||||
member_index_list = [member['member_index']
|
|
||||||
for member in edge_cluster['members']]
|
|
||||||
if not member_index_list:
|
|
||||||
err_msg = _("No edge members found in edge_cluster "
|
|
||||||
"%(cluster)s from tier0 router %(tier0)s") % {
|
|
||||||
'cluster': edge_cluster_uuid,
|
|
||||||
'tier0': tier0_uuid}
|
|
||||||
if err_msg:
|
|
||||||
raise n_exc.InvalidInput(error_message=err_msg)
|
|
||||||
else:
|
|
||||||
self.tier0_groups_dict[tier0_uuid] = {
|
|
||||||
'edge_cluster_uuid': edge_cluster_uuid,
|
|
||||||
'member_index_list': member_index_list}
|
|
||||||
|
|
||||||
def _validate_external_net_create(self, net_data):
|
def _validate_external_net_create(self, net_data):
|
||||||
is_provider_net = False
|
is_provider_net = False
|
||||||
|
@ -228,7 +205,7 @@ class NsxV3Plugin(db_base_plugin_v2.NeutronDbPluginV2,
|
||||||
else:
|
else:
|
||||||
tier0_uuid = net_data[pnet.PHYSICAL_NETWORK]
|
tier0_uuid = net_data[pnet.PHYSICAL_NETWORK]
|
||||||
is_provider_net = True
|
is_provider_net = True
|
||||||
self._validate_tier0(tier0_uuid)
|
routerlib.validate_tier0(self.tier0_groups_dict, tier0_uuid)
|
||||||
return (is_provider_net, utils.NetworkTypes.L3_EXT, tier0_uuid, 0)
|
return (is_provider_net, utils.NetworkTypes.L3_EXT, tier0_uuid, 0)
|
||||||
|
|
||||||
def _create_network_at_the_backend(self, context, net_data):
|
def _create_network_at_the_backend(self, context, net_data):
|
||||||
|
@ -593,6 +570,129 @@ class NsxV3Plugin(db_base_plugin_v2.NeutronDbPluginV2,
|
||||||
raise n_exc.BadRequest(resource='router', msg=msg)
|
raise n_exc.BadRequest(resource='router', msg=msg)
|
||||||
return gw_info
|
return gw_info
|
||||||
|
|
||||||
|
def _get_external_attachment_info(self, context, router):
|
||||||
|
gw_port = router.gw_port
|
||||||
|
ipaddress = None
|
||||||
|
netmask = None
|
||||||
|
nexthop = None
|
||||||
|
|
||||||
|
if gw_port:
|
||||||
|
# gw_port may have multiple IPs, only configure the first one
|
||||||
|
if gw_port.get('fixed_ips'):
|
||||||
|
ipaddress = gw_port['fixed_ips'][0]['ip_address']
|
||||||
|
|
||||||
|
network_id = gw_port.get('network_id')
|
||||||
|
if network_id:
|
||||||
|
ext_net = self._get_network(context, network_id)
|
||||||
|
if not ext_net.external:
|
||||||
|
msg = (_("Network '%s' is not a valid external "
|
||||||
|
"network") % network_id)
|
||||||
|
raise n_exc.BadRequest(resource='router', msg=msg)
|
||||||
|
if ext_net.subnets:
|
||||||
|
ext_subnet = ext_net.subnets[0]
|
||||||
|
netmask = str(netaddr.IPNetwork(ext_subnet.cidr).netmask)
|
||||||
|
nexthop = ext_subnet.gateway_ip
|
||||||
|
|
||||||
|
return (ipaddress, netmask, nexthop)
|
||||||
|
|
||||||
|
def _get_tier0_uuid_by_net(self, context, network_id):
|
||||||
|
if not network_id:
|
||||||
|
return
|
||||||
|
network = self.get_network(context, network_id)
|
||||||
|
if not network.get(pnet.PHYSICAL_NETWORK):
|
||||||
|
return cfg.CONF.nsx_v3.default_tier0_router_uuid
|
||||||
|
else:
|
||||||
|
return network.get(pnet.PHYSICAL_NETWORK)
|
||||||
|
|
||||||
|
def _update_router_gw_info(self, context, router_id, info):
|
||||||
|
router = self._get_router(context, router_id)
|
||||||
|
org_ext_net_id = router.gw_port_id and router.gw_port.network_id
|
||||||
|
org_tier0_uuid = self._get_tier0_uuid_by_net(context, org_ext_net_id)
|
||||||
|
org_enable_snat = router.enable_snat
|
||||||
|
new_ext_net_id = info and info.get('network_id')
|
||||||
|
orgaddr, orgmask, _orgnexthop = (
|
||||||
|
self._get_external_attachment_info(
|
||||||
|
context, router))
|
||||||
|
|
||||||
|
# TODO(berlin): For nonat user case, we actually don't need a gw port
|
||||||
|
# which consumes one external ip. But after looking at the DB logic
|
||||||
|
# and we need to make a big change so don't touch it at present.
|
||||||
|
super(NsxV3Plugin, self)._update_router_gw_info(
|
||||||
|
context, router_id, info, router=router)
|
||||||
|
|
||||||
|
new_ext_net_id = router.gw_port_id and router.gw_port.network_id
|
||||||
|
new_tier0_uuid = self._get_tier0_uuid_by_net(context, new_ext_net_id)
|
||||||
|
new_enable_snat = router.enable_snat
|
||||||
|
newaddr, newmask, _newnexthop = (
|
||||||
|
self._get_external_attachment_info(
|
||||||
|
context, router))
|
||||||
|
nsx_router_id = nsx_db.get_nsx_router_id(context.session, router_id)
|
||||||
|
|
||||||
|
# Remove router link port between tier1 and tier0 if tier0 router link
|
||||||
|
# is removed or changed
|
||||||
|
remove_router_link_port = (org_tier0_uuid and
|
||||||
|
(not new_tier0_uuid or
|
||||||
|
org_tier0_uuid != new_tier0_uuid))
|
||||||
|
|
||||||
|
# Remove SNAT rules for gw ip if gw ip is deleted/changed or
|
||||||
|
# enable_snat is updated from True to False
|
||||||
|
remove_snat_rules = (org_enable_snat and orgaddr and
|
||||||
|
(newaddr != orgaddr or
|
||||||
|
not new_enable_snat))
|
||||||
|
|
||||||
|
# Revocate bgp announce for nonat subnets if tier0 router link is
|
||||||
|
# changed or enable_snat is updated from False to True
|
||||||
|
revocate_bgp_announce = (not org_enable_snat and org_tier0_uuid and
|
||||||
|
(new_tier0_uuid != org_tier0_uuid or
|
||||||
|
new_enable_snat))
|
||||||
|
|
||||||
|
# Add router link port between tier1 and tier0 if tier0 router link is
|
||||||
|
# added or changed to a new one
|
||||||
|
add_router_link_port = (new_tier0_uuid and
|
||||||
|
(not org_tier0_uuid or
|
||||||
|
org_tier0_uuid != new_tier0_uuid))
|
||||||
|
|
||||||
|
# Add SNAT rules for gw ip if gw ip is add/changed or
|
||||||
|
# enable_snat is updated from False to True
|
||||||
|
add_snat_rules = (new_enable_snat and newaddr and
|
||||||
|
(newaddr != orgaddr or
|
||||||
|
not org_enable_snat))
|
||||||
|
|
||||||
|
# Bgp announce for nonat subnets if tier0 router link is changed or
|
||||||
|
# enable_snat is updated from True to False
|
||||||
|
bgp_announce = (not new_enable_snat and new_tier0_uuid and
|
||||||
|
(new_tier0_uuid != org_tier0_uuid or
|
||||||
|
not org_enable_snat))
|
||||||
|
|
||||||
|
advertise_route_nat_flag = True if new_enable_snat else False
|
||||||
|
advertise_route_connected_flag = True if not new_enable_snat else False
|
||||||
|
|
||||||
|
if revocate_bgp_announce:
|
||||||
|
# TODO(berlin): revocate bgp announce on org tier0 router
|
||||||
|
pass
|
||||||
|
if remove_snat_rules:
|
||||||
|
routerlib.delete_gw_snat_rule(nsx_router_id, orgaddr)
|
||||||
|
if remove_router_link_port:
|
||||||
|
routerlib.remove_router_link_port(nsx_router_id, org_tier0_uuid)
|
||||||
|
if add_router_link_port:
|
||||||
|
# First update edge cluster info for router
|
||||||
|
edge_cluster_uuid, members = self._get_edge_cluster_and_members(
|
||||||
|
new_tier0_uuid)
|
||||||
|
routerlib.update_router_edge_cluster(
|
||||||
|
nsx_router_id, edge_cluster_uuid)
|
||||||
|
routerlib.add_router_link_port(nsx_router_id, new_tier0_uuid,
|
||||||
|
members)
|
||||||
|
if add_snat_rules:
|
||||||
|
routerlib.add_gw_snat_rule(nsx_router_id, newaddr)
|
||||||
|
if bgp_announce:
|
||||||
|
# TODO(berlin): bgp announce on new tier0 router
|
||||||
|
pass
|
||||||
|
|
||||||
|
if remove_snat_rules or add_snat_rules:
|
||||||
|
routerlib.update_advertisement(nsx_router_id,
|
||||||
|
advertise_route_nat_flag,
|
||||||
|
advertise_route_connected_flag)
|
||||||
|
|
||||||
def create_router(self, context, router):
|
def create_router(self, context, router):
|
||||||
# TODO(berlin): admin_state_up support
|
# TODO(berlin): admin_state_up support
|
||||||
gw_info = self._extract_external_gw(context, router, is_extract=True)
|
gw_info = self._extract_external_gw(context, router, is_extract=True)
|
||||||
|
@ -624,6 +724,9 @@ class NsxV3Plugin(db_base_plugin_v2.NeutronDbPluginV2,
|
||||||
return self.get_router(context, router['id'])
|
return self.get_router(context, router['id'])
|
||||||
|
|
||||||
def delete_router(self, context, router_id):
|
def delete_router(self, context, router_id):
|
||||||
|
router = self.get_router(context, router_id)
|
||||||
|
if router.get(l3.EXTERNAL_GW_INFO):
|
||||||
|
self._update_router_gw_info(context, router_id, {})
|
||||||
nsx_router_id = nsx_db.get_nsx_router_id(context.session,
|
nsx_router_id = nsx_db.get_nsx_router_id(context.session,
|
||||||
router_id)
|
router_id)
|
||||||
ret_val = super(NsxV3Plugin, self).delete_router(context,
|
ret_val = super(NsxV3Plugin, self).delete_router(context,
|
||||||
|
|
|
@ -210,6 +210,7 @@ class NsxV3Mock(object):
|
||||||
self.logical_routers = {}
|
self.logical_routers = {}
|
||||||
self.logical_router_ports = {}
|
self.logical_router_ports = {}
|
||||||
self.logical_ports = {}
|
self.logical_ports = {}
|
||||||
|
self.logical_router_nat_rules = {}
|
||||||
if default_tier0_router_uuid:
|
if default_tier0_router_uuid:
|
||||||
self.create_logical_router(
|
self.create_logical_router(
|
||||||
DEFAULT_TIER0_ROUTER_UUID, None,
|
DEFAULT_TIER0_ROUTER_UUID, None,
|
||||||
|
@ -268,10 +269,11 @@ class NsxV3Mock(object):
|
||||||
def get_logical_router_port_by_ls_id(self, logical_switch_id):
|
def get_logical_router_port_by_ls_id(self, logical_switch_id):
|
||||||
router_ports = []
|
router_ports = []
|
||||||
for router_port in self.logical_router_ports.values():
|
for router_port in self.logical_router_ports.values():
|
||||||
ls_port_id = router_port['linked_logical_switch_port_id']
|
ls_port_id = router_port.get('linked_logical_switch_port_id')
|
||||||
port = self.get_logical_port(ls_port_id)
|
if ls_port_id:
|
||||||
if port['logical_switch_id'] == logical_switch_id:
|
port = self.get_logical_port(ls_port_id)
|
||||||
router_ports.append(router_port)
|
if port['logical_switch_id'] == logical_switch_id:
|
||||||
|
router_ports.append(router_port)
|
||||||
if len(router_ports) >= 2:
|
if len(router_ports) >= 2:
|
||||||
raise nsx_exc.NsxPluginException(
|
raise nsx_exc.NsxPluginException(
|
||||||
err_msg=_("Can't support more than one logical router ports "
|
err_msg=_("Can't support more than one logical router ports "
|
||||||
|
@ -305,18 +307,34 @@ class NsxV3Mock(object):
|
||||||
raise nsx_exc.ResourceNotFound(
|
raise nsx_exc.ResourceNotFound(
|
||||||
manager=FAKE_MANAGER, operation="get_logical_port")
|
manager=FAKE_MANAGER, operation="get_logical_port")
|
||||||
|
|
||||||
|
def get_logical_router_ports_by_router_id(self, logical_router_id):
|
||||||
|
logical_router_ports = []
|
||||||
|
for port_id in self.logical_router_ports.keys():
|
||||||
|
if (self.logical_router_ports[port_id]['logical_router_id'] ==
|
||||||
|
logical_router_id):
|
||||||
|
logical_router_ports.append(self.logical_router_ports[port_id])
|
||||||
|
return logical_router_ports
|
||||||
|
|
||||||
def create_logical_router_port(self, logical_router_id,
|
def create_logical_router_port(self, logical_router_id,
|
||||||
display_name,
|
display_name,
|
||||||
logical_switch_port_id,
|
|
||||||
resource_type,
|
resource_type,
|
||||||
address_groups):
|
logical_port_id,
|
||||||
|
address_groups,
|
||||||
|
edge_cluster_member_index=None):
|
||||||
fake_router_port_uuid = uuidutils.generate_uuid()
|
fake_router_port_uuid = uuidutils.generate_uuid()
|
||||||
body = {'id': fake_router_port_uuid,
|
body = {'display_name': display_name,
|
||||||
'display_name': display_name,
|
|
||||||
'resource_type': resource_type,
|
'resource_type': resource_type,
|
||||||
'logical_router_id': logical_router_id,
|
'logical_router_id': logical_router_id}
|
||||||
'subnets': address_groups,
|
if address_groups:
|
||||||
'linked_logical_switch_port_id': logical_switch_port_id}
|
body['subnets'] = address_groups
|
||||||
|
if resource_type in ["LogicalRouterUplinkPort",
|
||||||
|
"LogicalRouterDownLinkPort"]:
|
||||||
|
body['linked_logical_switch_port_id'] = logical_port_id
|
||||||
|
elif logical_port_id:
|
||||||
|
body['linked_logical_router_port_id'] = logical_port_id
|
||||||
|
if edge_cluster_member_index:
|
||||||
|
body['edge_cluster_member_index'] = edge_cluster_member_index
|
||||||
|
body['id'] = fake_router_port_uuid
|
||||||
self.logical_router_ports[fake_router_port_uuid] = body
|
self.logical_router_ports[fake_router_port_uuid] = body
|
||||||
return body
|
return body
|
||||||
|
|
||||||
|
@ -335,3 +353,58 @@ class NsxV3Mock(object):
|
||||||
else:
|
else:
|
||||||
raise nsx_exc.ResourceNotFound(
|
raise nsx_exc.ResourceNotFound(
|
||||||
manager=FAKE_MANAGER, operation="update_logical_router_port")
|
manager=FAKE_MANAGER, operation="update_logical_router_port")
|
||||||
|
|
||||||
|
def add_nat_rule(self, logical_router_id, action, translated_network,
|
||||||
|
source_net=None, dest_net=None, enabled=True,
|
||||||
|
rule_priority=None):
|
||||||
|
fake_rule_id = uuidutils.generate_uuid()
|
||||||
|
if logical_router_id not in self.logical_routers.keys():
|
||||||
|
raise nsx_exc.ResourceNotFound(
|
||||||
|
manager=FAKE_MANAGER, operation="get_logical_router")
|
||||||
|
body = {'action': action,
|
||||||
|
'enabled': enabled,
|
||||||
|
'translated_network': translated_network}
|
||||||
|
if source_net:
|
||||||
|
body['match_source_network'] = source_net
|
||||||
|
if dest_net:
|
||||||
|
body['match_destination_network'] = dest_net
|
||||||
|
if rule_priority:
|
||||||
|
body['rule_priority'] = rule_priority
|
||||||
|
body['rule_id'] = fake_rule_id
|
||||||
|
if self.logical_router_nat_rules.get(logical_router_id):
|
||||||
|
self.logical_router_nat_rules[logical_router_id][fake_rule_id] = (
|
||||||
|
body)
|
||||||
|
else:
|
||||||
|
self.logical_router_nat_rules[logical_router_id] = {
|
||||||
|
fake_rule_id: body}
|
||||||
|
return body
|
||||||
|
|
||||||
|
def delete_nat_rule(self, logical_router_id, nat_rule_id):
|
||||||
|
if (self.logical_router_nat_rules.get(logical_router_id) and
|
||||||
|
self.logical_router_nat_rules[logical_router_id].get(nat_rule_id)):
|
||||||
|
del self.logical_router_nat_rules[logical_router_id][nat_rule_id]
|
||||||
|
else:
|
||||||
|
raise nsx_exc.ResourceNotFound(
|
||||||
|
manager=FAKE_MANAGER, operation="delete_nat_rule")
|
||||||
|
|
||||||
|
def delete_nat_rule_by_values(self, logical_router_id, **kwargs):
|
||||||
|
if self.logical_router_nat_rules.get(logical_router_id):
|
||||||
|
nat_rules = self.logical_router_nat_rules[logical_router_id]
|
||||||
|
remove_nat_rule_ids = []
|
||||||
|
for nat_id, nat_body in nat_rules.items():
|
||||||
|
remove_flag = True
|
||||||
|
for k, v in kwargs.items():
|
||||||
|
if nat_body[k] != v:
|
||||||
|
remove_flag = False
|
||||||
|
break
|
||||||
|
if remove_flag:
|
||||||
|
remove_nat_rule_ids.append(nat_id)
|
||||||
|
for nat_id in remove_nat_rule_ids:
|
||||||
|
del nat_rules[nat_id]
|
||||||
|
else:
|
||||||
|
raise nsx_exc.ResourceNotFound(
|
||||||
|
manager=FAKE_MANAGER, operation="delete_nat_rule_by_values")
|
||||||
|
|
||||||
|
def update_logical_router_advertisement(self, logical_router_id, **kwargs):
|
||||||
|
# TODO(berlin): implement this latter.
|
||||||
|
pass
|
||||||
|
|
|
@ -205,6 +205,14 @@ class L3NatTest(test_l3_plugin.L3BaseForIntTests, NsxPluginV3TestCase):
|
||||||
self.v3_mock.update_logical_router_port)
|
self.v3_mock.update_logical_router_port)
|
||||||
nsxlib.delete_logical_router_port = (
|
nsxlib.delete_logical_router_port = (
|
||||||
self.v3_mock.delete_logical_router_port)
|
self.v3_mock.delete_logical_router_port)
|
||||||
|
nsxlib.add_nat_rule = self.v3_mock.add_nat_rule
|
||||||
|
nsxlib.delete_nat_rule = self.v3_mock.delete_nat_rule
|
||||||
|
nsxlib.delete_nat_rule_by_values = (
|
||||||
|
self.v3_mock.delete_nat_rule_by_values)
|
||||||
|
nsxlib.get_logical_router_ports_by_router_id = (
|
||||||
|
self.v3_mock.get_logical_router_ports_by_router_id)
|
||||||
|
nsxlib.update_logical_router_advertisement = (
|
||||||
|
self.v3_mock.update_logical_router_advertisement)
|
||||||
|
|
||||||
def _create_l3_ext_network(
|
def _create_l3_ext_network(
|
||||||
self, physical_network=nsx_v3_mocks.DEFAULT_TIER0_ROUTER_UUID):
|
self, physical_network=nsx_v3_mocks.DEFAULT_TIER0_ROUTER_UUID):
|
||||||
|
|
Loading…
Reference in New Issue