diff --git a/vmware_nsx/plugins/common_v3/plugin.py b/vmware_nsx/plugins/common_v3/plugin.py index 5577f2d6fb..bc55e35934 100644 --- a/vmware_nsx/plugins/common_v3/plugin.py +++ b/vmware_nsx/plugins/common_v3/plugin.py @@ -961,10 +961,7 @@ class NsxPluginV3Base(agentschedulers_db.AZDhcpAgentSchedulerDbMixin, def _default_physical_net(self, physical_net): return physical_net is None or physical_net == 'default' - def _validate_provider_create(self, context, network_data, - default_vlan_tz_uuid, - default_overlay_tz_uuid, - mdproxy_uuid, + def _validate_provider_create(self, context, network_data, az, nsxlib_tz, nsxlib_network, transparent_vlan=False): """Validate the parameters of a new provider network @@ -977,6 +974,11 @@ class NsxPluginV3Base(agentschedulers_db.AZDhcpAgentSchedulerDbMixin, - vlan_id: vlan tag, 0 or None - switch_mode: standard or ENS """ + # initialize the relevant parameters from the AZ + default_vlan_tz_uuid = az._default_vlan_tz_uuid + default_overlay_tz_uuid = az._default_overlay_tz_uuid + mdproxy_uuid = az._native_md_proxy_uuid + is_provider_net = any( validators.is_attr_set(network_data.get(f)) for f in (pnet.NETWORK_TYPE, @@ -1107,9 +1109,10 @@ class NsxPluginV3Base(agentschedulers_db.AZDhcpAgentSchedulerDbMixin, switch_mode = nsxlib_tz.get_host_switch_mode(physical_net) # validate the mdproxy TZ matches this one. - if (not err_msg and physical_net and self.nsxlib and + if (not err_msg and physical_net and self._has_native_dhcp_metadata()): - if not self._validate_net_mdproxy_tz(physical_net, mdproxy_uuid): + if not self._validate_net_mdproxy_tz( + az, physical_net, mdproxy_uuid): err_msg = (_('Network TZ %(tz)s does not match MD proxy ' '%(md)s edge cluster') % {'tz': physical_net, 'md': mdproxy_uuid}) @@ -1129,19 +1132,11 @@ class NsxPluginV3Base(agentschedulers_db.AZDhcpAgentSchedulerDbMixin, 'vlan_id': vlan_id, 'switch_mode': switch_mode} - def _validate_net_mdproxy_tz(self, tz_uuid, mdproxy_uuid): - """Validate that the network TZ matches the mdproxy edge cluster""" - # TODO(asarfaty): separate the validation when using policy mdproxy - mdproxy_obj = self.nsxlib.native_md_proxy.get(mdproxy_uuid) - ec_id = mdproxy_obj['edge_cluster_id'] - ec_nodes = self.nsxlib.edge_cluster.get_transport_nodes(ec_id) - ec_tzs = [] - for tn_uuid in ec_nodes: - ec_tzs.extend(self.nsxlib.transport_node.get_transport_zones( - tn_uuid)) - if tz_uuid not in ec_tzs: - return False - return True + def _validate_net_mdproxy_tz(self, az, tz_uuid, mdproxy_uuid): + """Validate that the network TZ matches the mdproxy edge cluster + Should be implemented by each plugin. + """ + pass def _network_is_nsx_net(self, context, network_id): bindings = nsx_db.get_network_bindings(context.session, network_id) @@ -1944,18 +1939,15 @@ class NsxPluginV3Base(agentschedulers_db.AZDhcpAgentSchedulerDbMixin, {'router': {az_def.AZ_HINTS: az_hints}}) def get_network_availability_zones(self, net_db): - if self._has_native_dhcp_metadata(): - hints = az_validator.convert_az_string_to_list( - net_db[az_def.AZ_HINTS]) - # When using the configured AZs, the az will always be the same - # as the hint (or default if none) - if hints: - az_name = hints[0] - else: - az_name = self.get_default_az().name - return [az_name] + hints = az_validator.convert_az_string_to_list( + net_db[az_def.AZ_HINTS]) + # When using the configured AZs, the az will always be the same + # as the hint (or default if none) + if hints: + az_name = hints[0] else: - return [] + az_name = self.get_default_az().name + return [az_name] def _get_router_az_obj(self, router): l3_attrs_db.ExtraAttributesMixin._extend_extra_router_dict( @@ -1973,11 +1965,6 @@ class NsxPluginV3Base(agentschedulers_db.AZDhcpAgentSchedulerDbMixin, force=True) def _list_availability_zones(self, context, filters=None): - # If no native_dhcp_metadata - use neutron AZs - if not self._has_native_dhcp_metadata(): - return super(NsxPluginV3Base, self)._list_availability_zones( - context, filters=filters) - result = {} for az in self._availability_zones_data.list_availability_zones(): # Add this availability zone as a network & router resource @@ -1996,10 +1983,6 @@ class NsxPluginV3Base(agentschedulers_db.AZDhcpAgentSchedulerDbMixin, if self._is_sub_plugin and not force: # validation should be done together for both plugins return - # If no native_dhcp_metadata - use neutron AZs - if not self._has_native_dhcp_metadata(): - return super(NsxPluginV3Base, self).validate_availability_zones( - context, resource_type, availability_zones) # Validate against the configured AZs return self.validate_obj_azs(availability_zones) @@ -2692,7 +2675,8 @@ class NsxPluginV3Base(agentschedulers_db.AZDhcpAgentSchedulerDbMixin, net_name or 'network'), net_id) - def _create_net_mdproxy_port(self, context, network, az, nsx_net_id): + def _create_net_mp_mdproxy_port(self, context, network, az, nsx_net_id): + """Add MD proxy on the MP logical-switch by creating a logical port""" if (not self.nsxlib or not self._has_native_dhcp_metadata()): return diff --git a/vmware_nsx/plugins/nsx_p/availability_zones.py b/vmware_nsx/plugins/nsx_p/availability_zones.py index 70687f6657..577a4068bc 100644 --- a/vmware_nsx/plugins/nsx_p/availability_zones.py +++ b/vmware_nsx/plugins/nsx_p/availability_zones.py @@ -21,6 +21,7 @@ from vmware_nsx.common import config from vmware_nsx.common import exceptions as nsx_exc from vmware_nsx.plugins.common_v3 import availability_zones as v3_az from vmware_nsxlib.v3 import exceptions as nsx_lib_exc +from vmware_nsxlib.v3 import nsx_constants from vmware_nsxlib.v3.policy import utils as p_utils LOG = log.getLogger(__name__) @@ -136,13 +137,37 @@ class NsxPAvailabilityZone(v3_az.NsxV3AvailabilityZone): auto_config=True, is_mandatory=False, search_scope=search_scope) + self.use_policy_md = False + if (nsxpolicy.feature_supported( + nsx_constants.FEATURE_NSX_POLICY_MDPROXY)): + # Try to initialize md-proxy from the policy + try: + self._native_md_proxy_uuid = self._init_default_resource( + nsxpolicy, nsxpolicy.md_proxy, 'metadata_proxy', + auto_config=True, is_mandatory=True, + search_scope=search_scope) + LOG.info("NSX-P az using policy MD proxy: %s", + self._native_md_proxy_uuid) + self.use_policy_md = True + except Exception: + LOG.info("NSX-P az could not use policy MD proxy. Using MP " + "one instead") + + if not self.use_policy_md: + # Try to initialize md-proxy from the MP + if nsxlib: + self._translate_metadata_proxy( + nsxlib, search_scope=search_scope) + LOG.info("NSX-P az using MP MD proxy: %s", + self._native_md_proxy_uuid) + else: + self._native_md_proxy_uuid = None + # If passthrough api is supported, also initialize those NSX objects if nsxlib: self._translate_dhcp_profile(nsxlib, search_scope=search_scope) - self._translate_metadata_proxy(nsxlib, search_scope=search_scope) else: self._native_dhcp_profile_uuid = None - self._native_md_proxy_uuid = None def _get_edge_cluster_tzs(self, nsxpolicy, nsxlib, ec_uuid): ec_nodes = nsxpolicy.edge_cluster.get_edge_node_ids(ec_uuid) @@ -207,8 +232,15 @@ class NsxPAvailabilityZone(v3_az.NsxV3AvailabilityZone): dhcp_ec) if self._native_md_proxy_uuid: - md_ec = nsxlib.native_md_proxy.get( - self._native_md_proxy_uuid).get('edge_cluster_id') + # Validate that the edge cluster of the MD proxy (MP or policy one) + # match the configured TZs + if self.use_policy_md: + md_ec_path = nsxpolicy.md_proxy.get( + self._native_md_proxy_uuid).get('edge_cluster_path') + md_ec = p_utils.path_to_id(md_ec_path) + else: + md_ec = nsxlib.native_md_proxy.get( + self._native_md_proxy_uuid).get('edge_cluster_id') if md_ec != tier0_ec_uuid: self._validate_tz(nsxpolicy, nsxlib, 'MD Proxy', self._native_md_proxy_uuid, diff --git a/vmware_nsx/plugins/nsx_p/plugin.py b/vmware_nsx/plugins/nsx_p/plugin.py index 32cdf57fdc..5ee45b91cb 100644 --- a/vmware_nsx/plugins/nsx_p/plugin.py +++ b/vmware_nsx/plugins/nsx_p/plugin.py @@ -98,6 +98,7 @@ from vmware_nsxlib.v3 import exceptions as nsx_lib_exc from vmware_nsxlib.v3 import nsx_constants as nsxlib_consts from vmware_nsxlib.v3.policy import constants as policy_constants from vmware_nsxlib.v3.policy import core_defs as policy_defs +from vmware_nsxlib.v3.policy import utils as p_utils from vmware_nsxlib.v3 import security from vmware_nsxlib.v3 import utils as nsxlib_utils @@ -586,7 +587,7 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base): def _create_network_on_backend(self, context, net_data, transparent_vlan, - provider_data): + provider_data, az): net_data['id'] = net_data.get('id') or uuidutils.generate_uuid() # update the network name to indicate the neutron id too. @@ -612,13 +613,18 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base): else: vlan_ids = None + kwargs = { + 'segment_id': net_data['id'], + 'description': net_data.get('description'), + 'vlan_ids': vlan_ids, + 'transport_zone_id': provider_data['physical_net'], + 'tags': tags} + + if az.use_policy_md: + kwargs['metadata_proxy_id'] = az._native_md_proxy_uuid + self.nsxpolicy.segment.create_or_overwrite( - net_name, - segment_id=net_data['id'], - description=net_data.get('description'), - vlan_ids=vlan_ids, - transport_zone_id=provider_data['physical_net'], - tags=tags) + net_name, **kwargs) if not admin_state and cfg.CONF.nsx_p.allow_passthrough: # This api uses the passthrough api @@ -691,10 +697,7 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base): is_backend_network = False else: provider_data = self._validate_provider_create( - context, net_data, - az._default_vlan_tz_uuid, - az._default_overlay_tz_uuid, - az._native_md_proxy_uuid, + context, net_data, az, self.nsxpolicy.transport_zone, self.nsxpolicy.segment, transparent_vlan=vlt) @@ -739,7 +742,7 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base): if is_backend_network: try: self._create_network_on_backend( - context, created_net, vlt, provider_data) + context, created_net, vlt, provider_data, az) except Exception as e: LOG.exception("Failed to create NSX network network: %s", e) with excutils.save_and_reraise_exception(): @@ -752,12 +755,13 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base): resource_extend.apply_funcs('networks', created_net, net_model) # MD Proxy is currently supported by the passthrough api only - if is_backend_network and cfg.CONF.nsx_p.allow_passthrough: + if (is_backend_network and not az.use_policy_md and + cfg.CONF.nsx_p.allow_passthrough): try: # The new segment was not realized yet. Waiting for a bit. time.sleep(cfg.CONF.nsx_p.realization_wait_sec) nsx_net_id = self._get_network_nsx_id(context, net_id) - self._create_net_mdproxy_port( + self._create_net_mp_mdproxy_port( context, created_net, az, nsx_net_id) except Exception as e: LOG.exception("Failed to create mdproxy port for network %s: " @@ -787,9 +791,13 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base): # checks on active ports self._retry_delete_network(context, network_id) - # MD Proxy is currently supported by the passthrough api only. - # Use it to delete mdproxy ports - if not is_external_net and cfg.CONF.nsx_p.allow_passthrough: + # Delete MD proxy port. This is relevant only if the plugin used + # MP MD proxy when this network is created. + # If not - the port will not be found, and it is ok. + # Note(asarfaty): In the future this code can be removed. + if (not is_external_net and cfg.CONF.nsx_p.allow_passthrough and + not self.nsxpolicy.feature_supported( + nsxlib_consts.FEATURE_NSX_POLICY_MDPROXY)): self._delete_nsx_port_by_network(network_id) # Delete the network segment from the backend @@ -3116,3 +3124,28 @@ class NsxPolicyPlugin(nsx_plugin_common.NsxPluginV3Base): extra_rules.extend(vpn_rules) return extra_rules + + def _validate_net_mdproxy_tz(self, az, tz_uuid, mdproxy_uuid): + """Validate that the network TZ matches the mdproxy edge cluster""" + if not self.nsxlib: + # No passthrough api support + return True + + if az.use_policy_md: + # Policy obj + md_ec_path = self.nsxpolicy.md_proxy.get( + mdproxy_uuid).get('edge_cluster_path') + md_ec = p_utils.path_to_id(md_ec_path) + else: + # MP obj + md_ec = self.nsxlib.native_md_proxy.get( + mdproxy_uuid).get('edge_cluster_id') + + ec_nodes = self.nsxlib.edge_cluster.get_transport_nodes(md_ec) + ec_tzs = [] + for tn_uuid in ec_nodes: + ec_tzs.extend(self.nsxlib.transport_node.get_transport_zones( + tn_uuid)) + if tz_uuid not in ec_tzs: + return False + return True diff --git a/vmware_nsx/plugins/nsx_v3/plugin.py b/vmware_nsx/plugins/nsx_v3/plugin.py index 0edc2fef19..ca9d7c39e9 100644 --- a/vmware_nsx/plugins/nsx_v3/plugin.py +++ b/vmware_nsx/plugins/nsx_v3/plugin.py @@ -815,10 +815,7 @@ class NsxV3Plugin(nsx_plugin_common.NsxPluginV3Base, def _create_network_at_the_backend(self, context, net_data, az, transparent_vlan): provider_data = self._validate_provider_create( - context, net_data, - az._default_vlan_tz_uuid, - az._default_overlay_tz_uuid, - az._native_md_proxy_uuid, + context, net_data, az, self.nsxlib.transport_zone, self.nsxlib.logical_switch, transparent_vlan=transparent_vlan) @@ -978,7 +975,7 @@ class NsxV3Plugin(nsx_plugin_common.NsxPluginV3Base, resource_extend.apply_funcs('networks', created_net, net_model) if is_backend_network: - self._create_net_mdproxy_port( + self._create_net_mp_mdproxy_port( context, created_net, az, nsx_net_id) except Exception: @@ -3442,3 +3439,16 @@ class NsxV3Plugin(nsx_plugin_common.NsxPluginV3Base, if len(port_tags) != orig_len: self.nsxlib.logical_port.update( nsx_lport_id, False, tags=port_tags) + + def _validate_net_mdproxy_tz(self, az, tz_uuid, mdproxy_uuid): + """Validate that the network TZ matches the mdproxy edge cluster""" + mdproxy_obj = self.nsxlib.native_md_proxy.get(mdproxy_uuid) + ec_id = mdproxy_obj['edge_cluster_id'] + ec_nodes = self.nsxlib.edge_cluster.get_transport_nodes(ec_id) + ec_tzs = [] + for tn_uuid in ec_nodes: + ec_tzs.extend(self.nsxlib.transport_node.get_transport_zones( + tn_uuid)) + if tz_uuid not in ec_tzs: + return False + return True diff --git a/vmware_nsx/tests/unit/nsx_p/test_dhcp_metadata.py b/vmware_nsx/tests/unit/nsx_p/test_dhcp_metadata.py index 7e478a2c1c..bcf1ff6ef5 100644 --- a/vmware_nsx/tests/unit/nsx_p/test_dhcp_metadata.py +++ b/vmware_nsx/tests/unit/nsx_p/test_dhcp_metadata.py @@ -919,7 +919,8 @@ class NsxNativeMetadataTestCase(test_plugin.NsxPPluginTestCaseMixin): def test_metadata_proxy_with_create_network(self): # Test if native metadata proxy is enabled on a network when it is - # created. + # created (Using MP MDproxy). + self.plugin._availability_zones_data._default_az.use_policy_md = False with mock.patch.object(nsx_resources.LogicalPort, 'create') as create_logical_port: with self.network() as network: @@ -938,7 +939,9 @@ class NsxNativeMetadataTestCase(test_plugin.NsxPPluginTestCaseMixin): def test_metadata_proxy_with_create_az_network(self): # Test if native metadata proxy is enabled on a network when it is - # created. + # created (Using MP MDproxy). + azs = self.plugin._availability_zones_data.availability_zones + azs[self._az_name].use_policy_md = False with mock.patch.object(nsx_resources.LogicalPort, 'create') as create_logical_port: with self.network( diff --git a/vmware_nsx/tests/unit/nsx_p/test_plugin.py b/vmware_nsx/tests/unit/nsx_p/test_plugin.py index 9f04241254..4eec862904 100644 --- a/vmware_nsx/tests/unit/nsx_p/test_plugin.py +++ b/vmware_nsx/tests/unit/nsx_p/test_plugin.py @@ -100,7 +100,7 @@ class NsxPPluginTestCaseMixin( 'display_name': 'test'}]} mock.patch( "vmware_nsxlib.v3.policy.NsxPolicyLib.get_version", - return_value=nsx_constants.NSX_VERSION_2_5_0).start() + return_value=nsx_constants.NSX_VERSION_3_0_0).start() mock.patch( "vmware_nsxlib.v3.client.RESTClient.get").start() mock.patch( @@ -142,7 +142,7 @@ class NsxPPluginTestCaseMixin( return_value=nsxlib_utils.TagLimits(20, 40, 15)).start() # Add some nsxlib mocks for the passthrough apis mock.patch("vmware_nsxlib.v3.NsxLib.get_version", - return_value=nsx_constants.NSX_VERSION_2_5_0).start() + return_value=nsx_constants.NSX_VERSION_3_0_0).start() mock.patch("vmware_nsxlib.v3.core_resources.NsxLibLogicalRouter." "update").start() mock.patch("vmware_nsxlib.v3.core_resources.NsxLibTransportNode."