diff --git a/vmware_nsx/common/utils.py b/vmware_nsx/common/utils.py index 683ef5b34d..552a0f0240 100644 --- a/vmware_nsx/common/utils.py +++ b/vmware_nsx/common/utils.py @@ -110,6 +110,11 @@ def is_nsxv_version_6_3(nsx_version): version.LooseVersion('6.3')) +def is_nsxv_version_6_4_6(nsx_version): + return (version.LooseVersion(nsx_version) >= + version.LooseVersion('6.4.6')) + + def is_nsxv_dhcp_binding_supported(nsx_version): return ((version.LooseVersion(nsx_version) >= version.LooseVersion('6.3.3')) or diff --git a/vmware_nsx/plugins/nsx_v/availability_zones.py b/vmware_nsx/plugins/nsx_v/availability_zones.py index 2fbfdd2973..b0dff3da7f 100644 --- a/vmware_nsx/plugins/nsx_v/availability_zones.py +++ b/vmware_nsx/plugins/nsx_v/availability_zones.py @@ -14,13 +14,16 @@ # under the License. from oslo_config import cfg +from oslo_log import log as logging from vmware_nsx._i18n import _ from vmware_nsx.common import availability_zones as common_az from vmware_nsx.common import config from vmware_nsx.common import exceptions as nsx_exc +from vmware_nsx.common import utils as c_utils DEFAULT_NAME = common_az.DEFAULT_NAME +LOG = logging.getLogger(__name__) class NsxVAvailabilityZone(common_az.ConfiguredAvailabilityZone): @@ -207,6 +210,92 @@ class NsxVAvailabilityZone(common_az.ConfiguredAvailabilityZone): # If False - it uses the global metadata (if defined) return self.az_metadata_support + def _validate_opt_connectivity(self, cluster_info, cluster_field, + az_value): + for obj in cluster_info.get(cluster_field, []): + if obj['id'] == az_value: + return True + return False + + def validate_az_connectivity(self, vcns): + info = vcns.get_tz_connectivity_info(self.vdn_scope_id) + if not info or not info.get('clustersInfo'): + LOG.warning("Couldn't get TZ %s connectivity information to " + "validate the configuration", self.vdn_scope_id) + return + + LOG.info("Validating connectivity of availability zone %s With TZ %s, " + "clusters %s, DVS %s external net %s and mdproxy net %s", + self.name, self.vdn_scope_id, cfg.CONF.nsxv.cluster_moid, + self.dvs_id, self.external_network, self.mgt_net_moid) + + # Look for each configured cluster + for configured_cluster in cfg.CONF.nsxv.cluster_moid: + found_cluster = False + for cluster_info in info['clustersInfo']: + if cluster_info.get('clusterId') == configured_cluster: + found_cluster = True + # Validate the external network: + external_net_standard = self._validate_opt_connectivity( + cluster_info, 'standardNetworks', + self.external_network) + external_net_portgroup = self._validate_opt_connectivity( + cluster_info, 'distributedVirtualPortGroups', + self.external_network) + if (not external_net_standard and + not external_net_portgroup): + raise nsx_exc.NsxInvalidConfiguration( + opt_name='external_network', + opt_value=self.external_network, + reason=(_("Edge cluster %(ec)s in not connected " + "to external network %(val)s in AZ " + "%(az)s") % { + 'ec': configured_cluster, + 'val': self.external_network, + 'az': self.name})) + + # Validate mgt_net_moid + if self.mgt_net_moid: + mgt_net_standard = self._validate_opt_connectivity( + cluster_info, 'standardNetworks', + self.mgt_net_moid) + mgt_net_portgroup = self._validate_opt_connectivity( + cluster_info, 'distributedVirtualPortGroups', + self.mgt_net_moid) + if not mgt_net_standard and not mgt_net_portgroup: + raise nsx_exc.NsxInvalidConfiguration( + opt_name='mgt_net_moid', + opt_value=self.mgt_net_moid, + reason=(_("Edge cluster %(ec)s in not " + "connected to mgt_net_moid %(val)s " + "in AZ %(az)s") % { + 'ec': configured_cluster, + 'val': self.mgt_net_moid, + 'az': self.name})) + + # Validate DVS + if self.dvs_id and not self._validate_opt_connectivity( + cluster_info, 'distributedVirtualSwitches', + self.dvs_id): + raise nsx_exc.NsxInvalidConfiguration( + opt_name='dvs_id', opt_value=self.dvs_id, + reason=(_("Edge cluster %(ec)s in not connected " + "to dvs_id %(val)s in AZ %(az)s") % { + 'ec': configured_cluster, + 'val': self.dvs_id, + 'az': self.name})) + break + + # Didn't find the edge cluster + if not found_cluster: + raise nsx_exc.NsxInvalidConfiguration( + opt_name='vdn_scope_id', opt_value=self.vdn_scope_id, + reason=(_("Edge cluster %(ec)s in not connected " + "to vdn_scope_id %(val)s in AZ %(az)s") % { + 'ec': configured_cluster, + 'val': self.vdn_scope_id, + 'az': self.name})) + class NsxVAvailabilityZones(common_az.ConfiguredAvailabilityZones): @@ -266,3 +355,11 @@ class NsxVAvailabilityZones(common_az.ConfiguredAvailabilityZones): def get_additional_dvs_ids(self): return self.get_unique_non_default_param("dvs_id") + + def validate_connectivity(self, vcns): + if (not c_utils.is_nsxv_version_6_4_6(vcns.get_version()) or + not cfg.CONF.nsxv.cluster_moid): + return + + for az in self.list_availability_zones_objects(): + az.validate_az_connectivity(vcns) diff --git a/vmware_nsx/plugins/nsx_v/plugin.py b/vmware_nsx/plugins/nsx_v/plugin.py index 9327fc0d52..b3d06c6627 100644 --- a/vmware_nsx/plugins/nsx_v/plugin.py +++ b/vmware_nsx/plugins/nsx_v/plugin.py @@ -5047,6 +5047,9 @@ class NsxVPluginV2(addr_pair_db.AllowedAddressPairsMixin, if cfg.CONF.nsxv.vdr_transit_network: edge_utils.validate_vdr_transit_network() + # Validate configuration connectivity per AZ + self._availability_zones_data.validate_connectivity(self.nsx_v.vcns) + def _nsx_policy_is_hidden(self, policy): for attrib in policy.get('extendedAttributes', []): if (attrib['name'].lower() == 'ishidden' and diff --git a/vmware_nsx/plugins/nsx_v/vshield/vcns.py b/vmware_nsx/plugins/nsx_v/vshield/vcns.py index f0187d0164..207d875191 100644 --- a/vmware_nsx/plugins/nsx_v/vshield/vcns.py +++ b/vmware_nsx/plugins/nsx_v/vshield/vcns.py @@ -51,6 +51,7 @@ EXCLUDELIST_PREFIX = '/api/2.1/app/excludelist' SERVICE_INSERTION_PROFILE_PREFIX = '/api/2.0/si/serviceprofile' SECURITY_POLICY_PREFIX = '/api/2.0/services/policy/securitypolicy' APPLICATION_PREFIX = '%s/%s' % (SERVICES_PREFIX, 'application') +TZ_CONNECTIVITY_PREFIX = '/api/4.0/edges/transportzonenetworks' #LbaaS Constants LOADBALANCER_SERVICE = "loadbalancer/config" @@ -1193,3 +1194,8 @@ class Vcns(object): def get_application_id(self, name): return self._globalobjects_lookup(name, use_cache=True) + + def get_tz_connectivity_info(self, vdn_scope_id): + uri = '%s/%s' % (TZ_CONNECTIVITY_PREFIX, vdn_scope_id) + h, info = self.do_request(HTTP_GET, uri, decode=True) + return info diff --git a/vmware_nsx/tests/unit/nsx_v/vshield/fake_vcns.py b/vmware_nsx/tests/unit/nsx_v/vshield/fake_vcns.py index 11e8dae199..9eed5eafab 100644 --- a/vmware_nsx/tests/unit/nsx_v/vshield/fake_vcns.py +++ b/vmware_nsx/tests/unit/nsx_v/vshield/fake_vcns.py @@ -1201,7 +1201,7 @@ class FakeVcns(object): return True def get_version(self): - return '6.2.3' + return '6.4.6' def get_tuning_configuration(self): return { @@ -1608,3 +1608,11 @@ class FakeVcns(object): def get_application_id(self, name): return 'application-123' + + def get_tz_connectivity_info(self, vdn_scope_id): + return {'clustersInfo': [{ + 'clusterId': 'fake_cluster_moid', + 'standardNetworks': [{'id': 'fake_net'}], + 'distributedVirtualPortGroups': [{'id': 'net-1'}], + 'distributedVirtualSwitches': [{'id': 'fake_dvs_id'}], + }]} diff --git a/vmware_nsx/tests/unit/test_utils.py b/vmware_nsx/tests/unit/test_utils.py index cd4ab289d0..6863d47360 100644 --- a/vmware_nsx/tests/unit/test_utils.py +++ b/vmware_nsx/tests/unit/test_utils.py @@ -30,6 +30,8 @@ def override_nsx_ini_test(): cfg.CONF.set_override("vdn_scope_id", "fake_vdn_scope_id", group="nsxv") cfg.CONF.set_override("dvs_id", "fake_dvs_id", group="nsxv") + cfg.CONF.set_override("cluster_moid", "fake_cluster_moid", group="nsxv") + cfg.CONF.set_override("external_network", "fake_net", group="nsxv") def override_nsx_ini_full_test():