diff --git a/vmware_nsx/common/config.py b/vmware_nsx/common/config.py index c27843a79f..57feab4465 100644 --- a/vmware_nsx/common/config.py +++ b/vmware_nsx/common/config.py @@ -659,6 +659,8 @@ nsxv_opts = [ # define the configuration of each availability zone. # the list of expected zones in under nsxv group: availability_zones +# Note: if any of the optional arguments is missing - the global one will be +# used instead. nsxv_az_opts = [ cfg.StrOpt('resource_pool_id', help=_('Identifying the ID of resource to deploy NSX Edges')), @@ -677,6 +679,10 @@ nsxv_az_opts = [ 'active and the other half will be placed in the ' 'ha_datastore. If this value is not set, the global ' 'one will be used')), + cfg.ListOpt('backup_edge_pool', + help=_("(Optional) Defines edge pool's management range for " + "the availability zone. If not defined, the global one " + "will be used")), ] # Register the configuration options diff --git a/vmware_nsx/plugins/nsx_v/availability_zones.py b/vmware_nsx/plugins/nsx_v/availability_zones.py index 941e1bf119..1e28f60aa8 100644 --- a/vmware_nsx/plugins/nsx_v/availability_zones.py +++ b/vmware_nsx/plugins/nsx_v/availability_zones.py @@ -66,12 +66,22 @@ class ConfiguredAvailabilityZone(object): "enabled")) self.ha_datastore_id = values[4] if len(values) == 5 else None - # Use the global configuration for ha_placement_random + + # Some parameters are not supported in this format. + # using the global ones instead. self.ha_placement_random = cfg.CONF.nsxv.ha_placement_random + self.backup_edge_pool = cfg.CONF.nsxv.backup_edge_pool elif config_line: # Newer configuration - the name of the availability zone can be # used to get the rest of the configuration for this AZ self.name = config_line + # field name size in the DB is 36 + if len(self.name) > 36: + raise nsx_exc.NsxInvalidConfiguration( + opt_name="availability_zones", + opt_value=config_line, + reason=_("Maximum name length is 36")) + az_info = config.get_nsxv_az_opts(self.name) self.resource_pool = az_info.get('resource_pool_id') if not self.resource_pool: @@ -92,11 +102,17 @@ class ConfiguredAvailabilityZone(object): self.ha_datastore_id = (az_info.get('ha_datastore_id') if self.edge_ha else None) - # Use the global config for ha_placement_random if not set + # The optional parameters will get the global values if not + # defined for this AZ self.ha_placement_random = az_info.get('ha_placement_random') if self.ha_placement_random is None: self.ha_placement_random = ( cfg.CONF.nsxv.ha_placement_random) + + self.backup_edge_pool = az_info.get('backup_edge_pool', []) + if not self.backup_edge_pool: + self.backup_edge_pool = cfg.CONF.nsxv.backup_edge_pool + else: # use the default configuration self.name = DEFAULT_NAME @@ -105,6 +121,7 @@ class ConfiguredAvailabilityZone(object): self.edge_ha = cfg.CONF.nsxv.edge_ha self.ha_datastore_id = cfg.CONF.nsxv.ha_datastore_id self.ha_placement_random = cfg.CONF.nsxv.ha_placement_random + self.backup_edge_pool = cfg.CONF.nsxv.backup_edge_pool class ConfiguredAvailabilityZones(object): diff --git a/vmware_nsx/plugins/nsx_v/vshield/edge_utils.py b/vmware_nsx/plugins/nsx_v/vshield/edge_utils.py index 8f0e393305..9e9707e8c1 100644 --- a/vmware_nsx/plugins/nsx_v/vshield/edge_utils.py +++ b/vmware_nsx/plugins/nsx_v/vshield/edge_utils.py @@ -122,9 +122,9 @@ def is_overlapping_reserved_subnets(cidr, reserved_subnets): return False -def parse_backup_edge_pool_opt(): - """Parse edge pool opts and returns result.""" - edge_pool_opts = cfg.CONF.nsxv.backup_edge_pool +def parse_backup_edge_pool_opt_per_az(az): + """Parse edge pool opts per AZ and returns result.""" + edge_pool_opts = az.backup_edge_pool res = [] for edge_pool_def in edge_pool_opts: split = edge_pool_def.split(':') @@ -132,20 +132,25 @@ def parse_backup_edge_pool_opt(): (edge_type, edge_size, minimum_pooled_edges, maximum_pooled_edges) = split[:4] except ValueError: - raise n_exc.Invalid(_("Invalid edge pool format")) + raise n_exc.Invalid(_("Invalid edge pool format for availability" + " zone %s") % az.name) if edge_type not in vcns_const.ALLOWED_EDGE_TYPES: msg = (_("edge type '%(edge_type)s' is not allowed, " - "allowed types: %(allowed)s") % + "allowed types: %(allowed)s for availability zone " + "%(name)s") % {'edge_type': edge_type, - 'allowed': vcns_const.ALLOWED_EDGE_TYPES}) + 'allowed': vcns_const.ALLOWED_EDGE_TYPES, + 'name': az.name}) LOG.error(msg) raise n_exc.Invalid(msg) edge_size = edge_size or nsxv_constants.COMPACT if edge_size not in vcns_const.ALLOWED_EDGE_SIZES: msg = (_("edge size '%(edge_size)s' is not allowed, " - "allowed types: %(allowed)s") % + "allowed types: %(allowed)s for availability zone " + "%(name)s") % {'edge_type': edge_size, - 'allowed': vcns_const.ALLOWED_EDGE_SIZES}) + 'allowed': vcns_const.ALLOWED_EDGE_SIZES, + 'name': az.name}) LOG.error(msg) raise n_exc.Invalid(msg) res.append({'edge_type': edge_type, @@ -159,7 +164,8 @@ def parse_backup_edge_pool_opt(): for r in res: edge_pool_dict = edge_pool_dicts[r['edge_type']] if r['edge_size'] in edge_pool_dict.keys(): - raise n_exc.Invalid(_("Duplicate edge pool configuration")) + raise n_exc.Invalid(_("Duplicate edge pool configuration for " + "availability zone %s") % az.name) else: edge_pool_dict[r['edge_size']] = { 'minimum_pooled_edges': r['minimum_pooled_edges'], @@ -179,14 +185,26 @@ class EdgeManager(object): self._worker_pool = None self.nsxv_manager = nsxv_manager self.dvs_id = cfg.CONF.nsxv.dvs_id - self.edge_pool_dicts = parse_backup_edge_pool_opt() + self._availability_zones = nsx_az.ConfiguredAvailabilityZones() + self.edge_pool_dicts = self._parse_backup_edge_pool_opt() self.nsxv_plugin = nsxv_manager.callbacks.plugin self.plugin = plugin - self._availability_zones = nsx_az.ConfiguredAvailabilityZones() self.per_interface_rp_filter = self._get_per_edge_rp_filter_state() self._check_backup_edge_pools() self._validate_new_features() + def _parse_backup_edge_pool_opt(self): + """Parse edge pool opts for all availability zones.""" + az_list = self._availability_zones.list_availability_zones() + az_pools = {} + for az_name in az_list: + az = self._availability_zones.get_availability_zone(az_name) + az_pools[az.name] = parse_backup_edge_pool_opt_per_az(az) + return az_pools + + def _get_az_pool(self, az_name): + return self.edge_pool_dicts[az_name] + def _get_worker_pool(self): if self._worker_pool_pid != os.getpid(): self._worker_pool_pid = os.getpid() @@ -337,7 +355,7 @@ class EdgeManager(object): for az_name in self._availability_zones.list_availability_zones(): az = self._availability_zones.get_availability_zone(az_name) self._clean_all_error_edge_bindings(admin_ctx, az) - for edge_type, v in self.edge_pool_dicts.items(): + for edge_type, v in self._get_az_pool(az.name).items(): for edge_size in vcns_const.ALLOWED_EDGE_SIZES: if edge_size in v.keys(): edge_pool_range = v[edge_size] @@ -626,7 +644,8 @@ class EdgeManager(object): nsxv_constants.SERVICE_EDGE) lrouter = {'id': resource_id, 'name': name} - edge_pool_range = self.edge_pool_dicts[edge_type].get(appliance_size) + az_pool = self._get_az_pool(availability_zone.name) + edge_pool_range = az_pool[edge_type].get(appliance_size) if edge_pool_range is None: nsxv_db.add_nsxv_router_binding( context.session, resource_id, None, None, @@ -717,7 +736,8 @@ class EdgeManager(object): edge_id = binding['edge_id'] availability_zone_name = nsxv_db.get_edge_availability_zone( context.session, edge_id) - edge_pool_range = self.edge_pool_dicts[binding['edge_type']].get( + az_pool = self._get_az_pool(availability_zone_name) + edge_pool_range = az_pool[binding['edge_type']].get( binding['appliance_size']) nsxv_db.delete_nsxv_router_binding( diff --git a/vmware_nsx/tests/unit/nsx_v/test_availability_zones.py b/vmware_nsx/tests/unit/nsx_v/test_availability_zones.py index 622c21b405..f32837ff4b 100644 --- a/vmware_nsx/tests/unit/nsx_v/test_availability_zones.py +++ b/vmware_nsx/tests/unit/nsx_v/test_availability_zones.py @@ -22,6 +22,10 @@ from vmware_nsx.common import exceptions as nsx_exc from vmware_nsx.plugins.nsx_v import availability_zones as nsx_az +DEF_AZ_POOL = ['service:compact:1:2', 'vdr:compact:1:2'] +DEF_GLOBAL_POOL = ['service:compact:4:10', 'vdr:compact:4:10'] + + class NsxvAvailabilityZonesTestCase(base.BaseTestCase): def setUp(self): @@ -33,7 +37,8 @@ class NsxvAvailabilityZonesTestCase(base.BaseTestCase): def _config_az(self, resource_pool_id="respool", datastore_id="datastore", edge_ha=True, ha_datastore_id="hastore", - ha_placement_random=False): + ha_placement_random=False, + backup_edge_pool=DEF_AZ_POOL): cfg.CONF.set_override("resource_pool_id", resource_pool_id, group=self.group_name) cfg.CONF.set_override("datastore_id", datastore_id, @@ -47,6 +52,9 @@ class NsxvAvailabilityZonesTestCase(base.BaseTestCase): cfg.CONF.set_override("ha_placement_random", ha_placement_random, group=self.group_name) + if backup_edge_pool is not None: + cfg.CONF.set_override("backup_edge_pool", backup_edge_pool, + group=self.group_name) def test_simple_availability_zone(self): self._config_az() @@ -57,6 +65,7 @@ class NsxvAvailabilityZonesTestCase(base.BaseTestCase): self.assertEqual(True, az.edge_ha) self.assertEqual("hastore", az.ha_datastore_id) self.assertEqual(False, az.ha_placement_random) + self.assertEqual(DEF_AZ_POOL, az.backup_edge_pool) def test_availability_zone_no_edge_ha(self): self._config_az(edge_ha=False) @@ -119,6 +128,13 @@ class NsxvAvailabilityZonesTestCase(base.BaseTestCase): # ha_placement_random should have the global value self.assertEqual(True, az.ha_placement_random) + def test_availability_zone_missing_backup_pool(self): + self._config_az(backup_edge_pool=None) + az = nsx_az.ConfiguredAvailabilityZone(self.az_name) + self.assertEqual(self.az_name, az.name) + # Should use the global configuration instead + self.assertEqual(DEF_GLOBAL_POOL, az.backup_edge_pool) + class NsxvAvailabilityZonesOldTestCase(base.BaseTestCase): """Test old way of configuring the availability zones @@ -135,6 +151,7 @@ class NsxvAvailabilityZonesOldTestCase(base.BaseTestCase): self.assertEqual(True, az.edge_ha) self.assertEqual("hastore", az.ha_datastore_id) self.assertEqual(False, az.ha_placement_random) + self.assertEqual(DEF_GLOBAL_POOL, az.backup_edge_pool) def test_availability_zone_without_ha_datastore(self): az = nsx_az.ConfiguredAvailabilityZone( diff --git a/vmware_nsx/tests/unit/nsx_v/vshield/test_edge_utils.py b/vmware_nsx/tests/unit/nsx_v/vshield/test_edge_utils.py index 4982d7e9f4..41643e0670 100644 --- a/vmware_nsx/tests/unit/nsx_v/vshield/test_edge_utils.py +++ b/vmware_nsx/tests/unit/nsx_v/vshield/test_edge_utils.py @@ -327,18 +327,18 @@ class EdgeManagerTestCase(EdgeUtilsTestCaseMixin): self.check = mock.patch.object(self.edge_manager, 'check_edge_active_at_backend').start() self.check.side_effect = self.check_edge_active_at_backend - self.default_edge_pool_dicts = { + self.default_edge_pool_dicts = {'default': { nsxv_constants.SERVICE_EDGE: { nsxv_constants.LARGE: {'minimum_pooled_edges': 1, 'maximum_pooled_edges': 3}, nsxv_constants.COMPACT: {'minimum_pooled_edges': 1, 'maximum_pooled_edges': 3}}, - nsxv_constants.VDR_EDGE: {}} - self.vdr_edge_pool_dicts = { + nsxv_constants.VDR_EDGE: {}}} + self.vdr_edge_pool_dicts = {'default': { nsxv_constants.SERVICE_EDGE: {}, nsxv_constants.VDR_EDGE: { nsxv_constants.LARGE: {'minimum_pooled_edges': 1, - 'maximum_pooled_edges': 3}}} + 'maximum_pooled_edges': 3}}}} def check_edge_active_at_backend(self, edge_id): # workaround to let edge_id None pass since we wrapped router binding @@ -352,12 +352,15 @@ class EdgeManagerTestCase(EdgeUtilsTestCaseMixin): cfg.CONF.set_override('backup_edge_pool', ['service:large:1:3', 'service:compact:1:3'], 'nsxv') - edge_pool_dicts = edge_utils.parse_backup_edge_pool_opt() - self.assertEqual(self.default_edge_pool_dicts, edge_pool_dicts) + az = nsx_az.ConfiguredAvailabilityZone(None) + edge_pool_dicts = edge_utils.parse_backup_edge_pool_opt_per_az(az) + self.assertEqual(self.default_edge_pool_dicts['default'], + edge_pool_dicts) def test_backup_edge_pool_with_empty_conf(self): cfg.CONF.set_override('backup_edge_pool', [], 'nsxv') - edge_pool_dicts = edge_utils.parse_backup_edge_pool_opt() + az = nsx_az.ConfiguredAvailabilityZone(None) + edge_pool_dicts = edge_utils.parse_backup_edge_pool_opt_per_az(az) expect_edge_pool_dicts = { nsxv_constants.SERVICE_EDGE: {}, nsxv_constants.VDR_EDGE: {}} @@ -365,15 +368,18 @@ class EdgeManagerTestCase(EdgeUtilsTestCaseMixin): def test_backup_edge_pool_with_vdr_conf(self): cfg.CONF.set_override('backup_edge_pool', ['vdr:large:1:3'], 'nsxv') - edge_pool_dicts = edge_utils.parse_backup_edge_pool_opt() - expect_edge_pool_dicts = self.vdr_edge_pool_dicts + az = nsx_az.ConfiguredAvailabilityZone(None) + edge_pool_dicts = edge_utils.parse_backup_edge_pool_opt_per_az(az) + expect_edge_pool_dicts = self.vdr_edge_pool_dicts['default'] self.assertEqual(expect_edge_pool_dicts, edge_pool_dicts) def test_backup_edge_pool_with_duplicate_conf(self): cfg.CONF.set_override('backup_edge_pool', ['service:compact:1:3', 'service::3:4'], 'nsxv') - self.assertRaises(n_exc.Invalid, edge_utils.parse_backup_edge_pool_opt) + az = nsx_az.ConfiguredAvailabilityZone(None) + self.assertRaises(n_exc.Invalid, + edge_utils.parse_backup_edge_pool_opt_per_az, az) def _create_router_bindings(self, num, status, id_prefix, size, edge_type, availability_zone):