NSXv: Add backup pools ranges to each AZ config

A new optional parameter was added to the availability zone section:
backup_edge_pool.
If not set - the global one will be used.

Change-Id: Ib6cad8cf0b31128fe7e68eaecc902e17303efd41
This commit is contained in:
Adit Sarfaty 2017-01-31 12:44:24 +02:00 committed by garyk
parent c4354d31b2
commit a4ed08ec1a
5 changed files with 93 additions and 27 deletions

View File

@ -659,6 +659,8 @@ nsxv_opts = [
# define the configuration of each availability zone. # define the configuration of each availability zone.
# the list of expected zones in under nsxv group: availability_zones # 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 = [ nsxv_az_opts = [
cfg.StrOpt('resource_pool_id', cfg.StrOpt('resource_pool_id',
help=_('Identifying the ID of resource to deploy NSX Edges')), 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 ' 'active and the other half will be placed in the '
'ha_datastore. If this value is not set, the global ' 'ha_datastore. If this value is not set, the global '
'one will be used')), '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 # Register the configuration options

View File

@ -66,12 +66,22 @@ class ConfiguredAvailabilityZone(object):
"enabled")) "enabled"))
self.ha_datastore_id = values[4] if len(values) == 5 else None 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.ha_placement_random = cfg.CONF.nsxv.ha_placement_random
self.backup_edge_pool = cfg.CONF.nsxv.backup_edge_pool
elif config_line: elif config_line:
# Newer configuration - the name of the availability zone can be # Newer configuration - the name of the availability zone can be
# used to get the rest of the configuration for this AZ # used to get the rest of the configuration for this AZ
self.name = config_line 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) az_info = config.get_nsxv_az_opts(self.name)
self.resource_pool = az_info.get('resource_pool_id') self.resource_pool = az_info.get('resource_pool_id')
if not self.resource_pool: if not self.resource_pool:
@ -92,11 +102,17 @@ class ConfiguredAvailabilityZone(object):
self.ha_datastore_id = (az_info.get('ha_datastore_id') self.ha_datastore_id = (az_info.get('ha_datastore_id')
if self.edge_ha else None) 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') self.ha_placement_random = az_info.get('ha_placement_random')
if self.ha_placement_random is None: if self.ha_placement_random is None:
self.ha_placement_random = ( self.ha_placement_random = (
cfg.CONF.nsxv.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: else:
# use the default configuration # use the default configuration
self.name = DEFAULT_NAME self.name = DEFAULT_NAME
@ -105,6 +121,7 @@ class ConfiguredAvailabilityZone(object):
self.edge_ha = cfg.CONF.nsxv.edge_ha self.edge_ha = cfg.CONF.nsxv.edge_ha
self.ha_datastore_id = cfg.CONF.nsxv.ha_datastore_id self.ha_datastore_id = cfg.CONF.nsxv.ha_datastore_id
self.ha_placement_random = cfg.CONF.nsxv.ha_placement_random self.ha_placement_random = cfg.CONF.nsxv.ha_placement_random
self.backup_edge_pool = cfg.CONF.nsxv.backup_edge_pool
class ConfiguredAvailabilityZones(object): class ConfiguredAvailabilityZones(object):

View File

@ -122,9 +122,9 @@ def is_overlapping_reserved_subnets(cidr, reserved_subnets):
return False return False
def parse_backup_edge_pool_opt(): def parse_backup_edge_pool_opt_per_az(az):
"""Parse edge pool opts and returns result.""" """Parse edge pool opts per AZ and returns result."""
edge_pool_opts = cfg.CONF.nsxv.backup_edge_pool edge_pool_opts = az.backup_edge_pool
res = [] res = []
for edge_pool_def in edge_pool_opts: for edge_pool_def in edge_pool_opts:
split = edge_pool_def.split(':') split = edge_pool_def.split(':')
@ -132,20 +132,25 @@ def parse_backup_edge_pool_opt():
(edge_type, edge_size, minimum_pooled_edges, (edge_type, edge_size, minimum_pooled_edges,
maximum_pooled_edges) = split[:4] maximum_pooled_edges) = split[:4]
except ValueError: 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: if edge_type not in vcns_const.ALLOWED_EDGE_TYPES:
msg = (_("edge type '%(edge_type)s' is not allowed, " 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, {'edge_type': edge_type,
'allowed': vcns_const.ALLOWED_EDGE_TYPES}) 'allowed': vcns_const.ALLOWED_EDGE_TYPES,
'name': az.name})
LOG.error(msg) LOG.error(msg)
raise n_exc.Invalid(msg) raise n_exc.Invalid(msg)
edge_size = edge_size or nsxv_constants.COMPACT edge_size = edge_size or nsxv_constants.COMPACT
if edge_size not in vcns_const.ALLOWED_EDGE_SIZES: if edge_size not in vcns_const.ALLOWED_EDGE_SIZES:
msg = (_("edge size '%(edge_size)s' is not allowed, " 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, {'edge_type': edge_size,
'allowed': vcns_const.ALLOWED_EDGE_SIZES}) 'allowed': vcns_const.ALLOWED_EDGE_SIZES,
'name': az.name})
LOG.error(msg) LOG.error(msg)
raise n_exc.Invalid(msg) raise n_exc.Invalid(msg)
res.append({'edge_type': edge_type, res.append({'edge_type': edge_type,
@ -159,7 +164,8 @@ def parse_backup_edge_pool_opt():
for r in res: for r in res:
edge_pool_dict = edge_pool_dicts[r['edge_type']] edge_pool_dict = edge_pool_dicts[r['edge_type']]
if r['edge_size'] in edge_pool_dict.keys(): 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: else:
edge_pool_dict[r['edge_size']] = { edge_pool_dict[r['edge_size']] = {
'minimum_pooled_edges': r['minimum_pooled_edges'], 'minimum_pooled_edges': r['minimum_pooled_edges'],
@ -179,14 +185,26 @@ class EdgeManager(object):
self._worker_pool = None self._worker_pool = None
self.nsxv_manager = nsxv_manager self.nsxv_manager = nsxv_manager
self.dvs_id = cfg.CONF.nsxv.dvs_id 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.nsxv_plugin = nsxv_manager.callbacks.plugin
self.plugin = plugin self.plugin = plugin
self._availability_zones = nsx_az.ConfiguredAvailabilityZones()
self.per_interface_rp_filter = self._get_per_edge_rp_filter_state() self.per_interface_rp_filter = self._get_per_edge_rp_filter_state()
self._check_backup_edge_pools() self._check_backup_edge_pools()
self._validate_new_features() 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): def _get_worker_pool(self):
if self._worker_pool_pid != os.getpid(): if self._worker_pool_pid != os.getpid():
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(): for az_name in self._availability_zones.list_availability_zones():
az = self._availability_zones.get_availability_zone(az_name) az = self._availability_zones.get_availability_zone(az_name)
self._clean_all_error_edge_bindings(admin_ctx, az) 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: for edge_size in vcns_const.ALLOWED_EDGE_SIZES:
if edge_size in v.keys(): if edge_size in v.keys():
edge_pool_range = v[edge_size] edge_pool_range = v[edge_size]
@ -626,7 +644,8 @@ class EdgeManager(object):
nsxv_constants.SERVICE_EDGE) nsxv_constants.SERVICE_EDGE)
lrouter = {'id': resource_id, lrouter = {'id': resource_id,
'name': name} '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: if edge_pool_range is None:
nsxv_db.add_nsxv_router_binding( nsxv_db.add_nsxv_router_binding(
context.session, resource_id, None, None, context.session, resource_id, None, None,
@ -717,7 +736,8 @@ class EdgeManager(object):
edge_id = binding['edge_id'] edge_id = binding['edge_id']
availability_zone_name = nsxv_db.get_edge_availability_zone( availability_zone_name = nsxv_db.get_edge_availability_zone(
context.session, edge_id) 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']) binding['appliance_size'])
nsxv_db.delete_nsxv_router_binding( nsxv_db.delete_nsxv_router_binding(

View File

@ -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 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): class NsxvAvailabilityZonesTestCase(base.BaseTestCase):
def setUp(self): def setUp(self):
@ -33,7 +37,8 @@ class NsxvAvailabilityZonesTestCase(base.BaseTestCase):
def _config_az(self, resource_pool_id="respool", datastore_id="datastore", def _config_az(self, resource_pool_id="respool", datastore_id="datastore",
edge_ha=True, ha_datastore_id="hastore", 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, cfg.CONF.set_override("resource_pool_id", resource_pool_id,
group=self.group_name) group=self.group_name)
cfg.CONF.set_override("datastore_id", datastore_id, cfg.CONF.set_override("datastore_id", datastore_id,
@ -47,6 +52,9 @@ class NsxvAvailabilityZonesTestCase(base.BaseTestCase):
cfg.CONF.set_override("ha_placement_random", cfg.CONF.set_override("ha_placement_random",
ha_placement_random, ha_placement_random,
group=self.group_name) 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): def test_simple_availability_zone(self):
self._config_az() self._config_az()
@ -57,6 +65,7 @@ class NsxvAvailabilityZonesTestCase(base.BaseTestCase):
self.assertEqual(True, az.edge_ha) self.assertEqual(True, az.edge_ha)
self.assertEqual("hastore", az.ha_datastore_id) self.assertEqual("hastore", az.ha_datastore_id)
self.assertEqual(False, az.ha_placement_random) self.assertEqual(False, az.ha_placement_random)
self.assertEqual(DEF_AZ_POOL, az.backup_edge_pool)
def test_availability_zone_no_edge_ha(self): def test_availability_zone_no_edge_ha(self):
self._config_az(edge_ha=False) self._config_az(edge_ha=False)
@ -119,6 +128,13 @@ class NsxvAvailabilityZonesTestCase(base.BaseTestCase):
# ha_placement_random should have the global value # ha_placement_random should have the global value
self.assertEqual(True, az.ha_placement_random) 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): class NsxvAvailabilityZonesOldTestCase(base.BaseTestCase):
"""Test old way of configuring the availability zones """Test old way of configuring the availability zones
@ -135,6 +151,7 @@ class NsxvAvailabilityZonesOldTestCase(base.BaseTestCase):
self.assertEqual(True, az.edge_ha) self.assertEqual(True, az.edge_ha)
self.assertEqual("hastore", az.ha_datastore_id) self.assertEqual("hastore", az.ha_datastore_id)
self.assertEqual(False, az.ha_placement_random) self.assertEqual(False, az.ha_placement_random)
self.assertEqual(DEF_GLOBAL_POOL, az.backup_edge_pool)
def test_availability_zone_without_ha_datastore(self): def test_availability_zone_without_ha_datastore(self):
az = nsx_az.ConfiguredAvailabilityZone( az = nsx_az.ConfiguredAvailabilityZone(

View File

@ -327,18 +327,18 @@ class EdgeManagerTestCase(EdgeUtilsTestCaseMixin):
self.check = mock.patch.object(self.edge_manager, self.check = mock.patch.object(self.edge_manager,
'check_edge_active_at_backend').start() 'check_edge_active_at_backend').start()
self.check.side_effect = self.check_edge_active_at_backend 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.SERVICE_EDGE: {
nsxv_constants.LARGE: {'minimum_pooled_edges': 1, nsxv_constants.LARGE: {'minimum_pooled_edges': 1,
'maximum_pooled_edges': 3}, 'maximum_pooled_edges': 3},
nsxv_constants.COMPACT: {'minimum_pooled_edges': 1, nsxv_constants.COMPACT: {'minimum_pooled_edges': 1,
'maximum_pooled_edges': 3}}, 'maximum_pooled_edges': 3}},
nsxv_constants.VDR_EDGE: {}} nsxv_constants.VDR_EDGE: {}}}
self.vdr_edge_pool_dicts = { self.vdr_edge_pool_dicts = {'default': {
nsxv_constants.SERVICE_EDGE: {}, nsxv_constants.SERVICE_EDGE: {},
nsxv_constants.VDR_EDGE: { nsxv_constants.VDR_EDGE: {
nsxv_constants.LARGE: {'minimum_pooled_edges': 1, nsxv_constants.LARGE: {'minimum_pooled_edges': 1,
'maximum_pooled_edges': 3}}} 'maximum_pooled_edges': 3}}}}
def check_edge_active_at_backend(self, edge_id): def check_edge_active_at_backend(self, edge_id):
# workaround to let edge_id None pass since we wrapped router binding # 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', cfg.CONF.set_override('backup_edge_pool',
['service:large:1:3', 'service:compact:1:3'], ['service:large:1:3', 'service:compact:1:3'],
'nsxv') 'nsxv')
edge_pool_dicts = edge_utils.parse_backup_edge_pool_opt() az = nsx_az.ConfiguredAvailabilityZone(None)
self.assertEqual(self.default_edge_pool_dicts, edge_pool_dicts) 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): def test_backup_edge_pool_with_empty_conf(self):
cfg.CONF.set_override('backup_edge_pool', [], 'nsxv') 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 = { expect_edge_pool_dicts = {
nsxv_constants.SERVICE_EDGE: {}, nsxv_constants.SERVICE_EDGE: {},
nsxv_constants.VDR_EDGE: {}} nsxv_constants.VDR_EDGE: {}}
@ -365,15 +368,18 @@ class EdgeManagerTestCase(EdgeUtilsTestCaseMixin):
def test_backup_edge_pool_with_vdr_conf(self): def test_backup_edge_pool_with_vdr_conf(self):
cfg.CONF.set_override('backup_edge_pool', ['vdr:large:1:3'], 'nsxv') cfg.CONF.set_override('backup_edge_pool', ['vdr:large:1:3'], 'nsxv')
edge_pool_dicts = edge_utils.parse_backup_edge_pool_opt() az = nsx_az.ConfiguredAvailabilityZone(None)
expect_edge_pool_dicts = self.vdr_edge_pool_dicts 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) self.assertEqual(expect_edge_pool_dicts, edge_pool_dicts)
def test_backup_edge_pool_with_duplicate_conf(self): def test_backup_edge_pool_with_duplicate_conf(self):
cfg.CONF.set_override('backup_edge_pool', cfg.CONF.set_override('backup_edge_pool',
['service:compact:1:3', 'service::3:4'], ['service:compact:1:3', 'service::3:4'],
'nsxv') '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, def _create_router_bindings(self, num, status, id_prefix, size,
edge_type, availability_zone): edge_type, availability_zone):