diff --git a/vmware_nsx/common/config.py b/vmware_nsx/common/config.py index b8fbc1c282..d804773d83 100644 --- a/vmware_nsx/common/config.py +++ b/vmware_nsx/common/config.py @@ -378,6 +378,10 @@ nsx_v3_and_p = [ help=_("List of nameservers to configure for the DHCP " "binding entries. These will be used if there are no " "nameservers defined on the subnet.")), + cfg.StrOpt('edge_cluster', + help=_("(Optional) Specifying an edge cluster for Tier1 " + "routers to connect other that the one connected to" + " the Tier0 router")), ] nsx_v3_opts = nsx_v3_and_p + [ @@ -916,6 +920,10 @@ nsxv3_az_opts = [ help=_("Name or UUID of the default tier0 router that will be " "used for connecting to tier1 logical routers and " "configuring external networks")), + cfg.StrOpt('edge_cluster', + help=_("(Optional) Specifying an edge cluster for Tier1 " + "routers to connect other that the one connected to" + " the Tier0 router")), ] nsx_tvd_opts = [ diff --git a/vmware_nsx/plugins/nsx_v3/availability_zones.py b/vmware_nsx/plugins/nsx_v3/availability_zones.py index f1dabc68d8..f79fbc76ed 100644 --- a/vmware_nsx/plugins/nsx_v3/availability_zones.py +++ b/vmware_nsx/plugins/nsx_v3/availability_zones.py @@ -40,6 +40,10 @@ class NsxV3AvailabilityZone(v3_az.NsxV3AvailabilityZone): if dhcp_relay_service: self.dhcp_relay_service = dhcp_relay_service + edge_cluster = az_info.get('edge_cluster') + if edge_cluster: + self.edge_cluster = edge_cluster + def init_defaults(self): # use the default configuration self.metadata_proxy = cfg.CONF.nsx_v3.metadata_proxy @@ -52,10 +56,25 @@ class NsxV3AvailabilityZone(v3_az.NsxV3AvailabilityZone): self.switching_profiles = cfg.CONF.nsx_v3.switching_profiles self.dhcp_relay_service = cfg.CONF.nsx_v3.dhcp_relay_service self.default_tier0_router = cfg.CONF.nsx_v3.default_tier0_router + self.edge_cluster = cfg.CONF.nsx_v3.edge_cluster def translate_configured_names_to_uuids(self, nsxlib): # Mandatory configurations (in AZ or inherited from global values) # Unless this is the default AZ, and metadata is disabled. + if self.edge_cluster: + edge_cluster_uuid = None + if cfg.CONF.nsx_v3.init_objects_by_tags: + # Find the edge cluster by its tag + edge_cluster_uuid = nsxlib.get_id_by_resource_and_tag( + nsxlib.edge_cluster.resource_type, + cfg.CONF.nsx_v3.search_objects_scope, + self.edge_cluster) + if not edge_cluster_uuid: + edge_cluster_uuid = (nsxlib.edge_cluster + .get_id_by_name_or_id(self.edge_cluster)) + self._edge_cluster_uuid = edge_cluster_uuid + else: + self._edge_cluster_uuid = None if self.dhcp_profile: dhcp_id = None if cfg.CONF.nsx_v3.init_objects_by_tags: diff --git a/vmware_nsx/plugins/nsx_v3/plugin.py b/vmware_nsx/plugins/nsx_v3/plugin.py index 58fc606440..14cf817811 100644 --- a/vmware_nsx/plugins/nsx_v3/plugin.py +++ b/vmware_nsx/plugins/nsx_v3/plugin.py @@ -879,7 +879,15 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin, return self.conn.consume_in_threads() - def _get_edge_cluster(self, tier0_uuid): + def _get_router_az_obj(self, router): + l3_attrs_db.ExtraAttributesMixin._extend_extra_router_dict( + router, router) + return self.get_router_az(router) + + def _get_edge_cluster(self, tier0_uuid, router): + az = self._get_router_az_obj(router) + if az and az._edge_cluster_uuid: + return az._edge_cluster_uuid if (not self.tier0_groups_dict.get(tier0_uuid) or not self. tier0_groups_dict[tier0_uuid].get('edge_cluster_uuid')): self.nsxlib.router.validate_tier0(self.tier0_groups_dict, @@ -3216,7 +3224,7 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin, def create_service_router(self, context, router_id): router = self._get_router(context, router_id) tier0_uuid = self._get_tier0_uuid_by_router(context, router) - edge_cluster_uuid = self._get_edge_cluster(tier0_uuid) + edge_cluster_uuid = self._get_edge_cluster(tier0_uuid, router) nsx_router_id = nsx_db.get_nsx_router_id(context.session, router_id) self.nsxlib.router.update_router_edge_cluster(nsx_router_id, @@ -3241,7 +3249,6 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin, router_subnets = self._find_router_subnets( context.elevated(), router_id) - if info and info.get('network_id'): new_tier0_uuid = self._get_tier0_uuid_by_net_id(context.elevated(), info['network_id']) @@ -3482,11 +3489,7 @@ class NsxV3Plugin(agentschedulers_db.AZDhcpAgentSchedulerDbMixin, def get_router_availability_zones(self, router): """Return availability zones which a router belongs to.""" - # add the hints to the structure first - l3_attrs_db.ExtraAttributesMixin._extend_extra_router_dict( - router, router) - # get the availability zones from the hints - return [self.get_router_az(router).name] + return [self._get_router_az_obj(router).name] def _update_router_wrapper(self, context, router_id, router): if cfg.CONF.api_replay_mode: diff --git a/vmware_nsx/tests/unit/nsx_v3/test_plugin.py b/vmware_nsx/tests/unit/nsx_v3/test_plugin.py index 3966de211e..d12c5a6c54 100644 --- a/vmware_nsx/tests/unit/nsx_v3/test_plugin.py +++ b/vmware_nsx/tests/unit/nsx_v3/test_plugin.py @@ -206,11 +206,6 @@ class NsxV3PluginTestCaseMixin(test_plugin.NeutronDbPluginV2TestCase, mock_ensure_global_sg_placeholder = mock.patch.object( nsx_plugin.NsxV3Plugin, '_ensure_global_sg_placeholder') mock_ensure_global_sg_placeholder.start() - - mock_get_edge_cluster = mock.patch.object( - nsx_plugin.NsxV3Plugin, '_get_edge_cluster', - return_value=uuidutils.generate_uuid()) - mock_get_edge_cluster.start() mock.patch( 'neutron_lib.rpc.Connection.consume_in_threads', return_value=[]).start() @@ -223,6 +218,10 @@ class NsxV3PluginTestCaseMixin(test_plugin.NeutronDbPluginV2TestCase, _mock_nsx_backend_calls() self.setup_conf_overrides() + self.mock_get_edge_cluster = mock.patch.object( + nsx_plugin.NsxV3Plugin, '_get_edge_cluster', + return_value=uuidutils.generate_uuid()) + self.mock_get_edge_cluster.start() self.mock_plugin_methods() # ignoring the given plugin and use the nsx-v3 one if not plugin.endswith('NsxTVDPlugin'): @@ -3005,6 +3004,30 @@ class TestL3NatTestCase(L3NatTest, self._test_route_update_illegal('0.0.0.0/0') self._test_route_update_illegal('0.0.0.0/16') + def test_update_router_distinct_edge_cluster(self): + self.mock_get_edge_cluster.stop() + edge_cluster = uuidutils.generate_uuid() + mock.patch( + "vmware_nsxlib.v3.core_resources.NsxLibEdgeCluster." + "get_id_by_name_or_id", + return_value=edge_cluster).start() + cfg.CONF.set_override('edge_cluster', edge_cluster, 'nsx_v3') + with self.address_scope(name='as1') as addr_scope, \ + self._create_l3_ext_network() as ext_net: + ext_subnet = self._prepare_external_subnet_on_address_scope( + ext_net, addr_scope) + + # create a router with this gateway + with self.router() as r, \ + self._mock_add_remove_service_router() as change_sr: + router_id = r['router']['id'] + self.plugin.init_availability_zones() + for az in self.plugin.get_azs_list(): + az.translate_configured_names_to_uuids(self.plugin.nsxlib) + self._add_external_gateway_to_router( + router_id, ext_subnet['network_id']) + change_sr.assert_called_once_with(mock.ANY, edge_cluster) + class ExtGwModeTestCase(test_ext_gw_mode.ExtGwModeIntTestCase, L3NatTest):