NSXv: Edge random placement

Support randomly selecting which will be the primary datastore and which
will be the secondary one when deplying an edge, in order to balance the
load.
This new option is available globally as well as per availability_zone
via a new configuration parameter edge_placement_random which will be
False by default.

Change-Id: I5bf8f8999100c4c6da4645bda6e74165575c3818
This commit is contained in:
Adit Sarfaty 2017-01-27 22:23:06 +02:00
parent dd1923005c
commit 3ac633ec68
5 changed files with 78 additions and 6 deletions

View File

@ -0,0 +1,9 @@
---
prelude: >
Support randomly selecting which will be the primary datastore and which
will be the secondary one when deplying an edge, in order to balance the load.
This new option is available globally as well as per availability_zone.
features:
- |
Support randomly selecting which will be the primary datastore and which
will be the secondary one when deplying an edge, in order to balance the load.

View File

@ -461,6 +461,12 @@ nsxv_opts = [
help=_('Optional parameter identifying the ID of datastore to '
'deploy NSX Edges in addition to data_store_id in case'
'edge_ha is True')),
cfg.BoolOpt('ha_placement_random',
default=False,
help=_('When True and in case edge_ha is True, half of the '
'edges will be placed in the primary datastore as '
'active and the other half will be placed in the '
'ha_datastore')),
cfg.StrOpt('external_network',
deprecated_group="vcns",
help=_('(Required) Network ID for physical network '
@ -663,6 +669,12 @@ nsxv_az_opts = [
help=_('Optional parameter identifying the ID of datastore to '
'deploy NSX Edges in addition to data_store_id in case'
'edge_ha is True')),
cfg.BoolOpt('ha_placement_random',
help=_('When True and in case edge_ha is True, half of the '
'edges will be placed in the primary datastore as '
'active and the other half will be placed in the '
'ha_datastore. If this value is not set, the global '
'one will be used')),
]
# Register the configuration options

View File

@ -66,6 +66,8 @@ class ConfiguredAvailabilityZone(object):
"enabled"))
self.ha_datastore_id = values[4] if len(values) == 5 else None
# Use the global configuration for ha_placement_random
self.ha_placement_random = cfg.CONF.nsxv.ha_placement_random
elif config_line:
# Newer configuration - the name of the availability zone can be
# used to get the rest of the configuration for this AZ
@ -89,6 +91,12 @@ class ConfiguredAvailabilityZone(object):
# The HA datastore can be empty
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
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)
else:
# use the default configuration
self.name = DEFAULT_NAME
@ -96,6 +104,7 @@ class ConfiguredAvailabilityZone(object):
self.datastore_id = cfg.CONF.nsxv.datastore_id
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
class ConfiguredAvailabilityZones(object):

View File

@ -13,6 +13,7 @@
# under the License.
from distutils import version
import random
import time
from neutron.plugins.common import constants as plugin_const
@ -45,6 +46,7 @@ class EdgeApplianceDriver(object):
'nat': {},
'route': {},
}
random.seed()
def _assemble_edge(self, name, appliance_size="compact",
deployment_container_id=None, datacenter_moid=None,
@ -95,16 +97,34 @@ class EdgeApplianceDriver(object):
return edge
def _select_datastores(self, availability_zone):
primary_ds = availability_zone.datastore_id
secondary_ds = availability_zone.ha_datastore_id
if availability_zone.ha_placement_random:
# we want to switch primary and secondary datastores
# half of the times, to balance it
if random.random() > 0.5:
primary_ds = availability_zone.ha_datastore_id
secondary_ds = availability_zone.datastore_id
return primary_ds, secondary_ds
def _assemble_edge_appliances(self, availability_zone):
appliances = []
if availability_zone.datastore_id:
if availability_zone.ha_datastore_id and availability_zone.edge_ha:
# create appliance with HA
primary_ds, secondary_ds = self._select_datastores(
availability_zone)
appliances.append(self._assemble_edge_appliance(
availability_zone.resource_pool,
primary_ds))
appliances.append(self._assemble_edge_appliance(
availability_zone.resource_pool,
secondary_ds))
elif availability_zone.datastore_id:
# Single datastore
appliances.append(self._assemble_edge_appliance(
availability_zone.resource_pool,
availability_zone.datastore_id))
if availability_zone.ha_datastore_id and availability_zone.edge_ha:
appliances.append(self._assemble_edge_appliance(
availability_zone.resource_pool,
availability_zone.ha_datastore_id))
return appliances
def _assemble_edge_appliance(self, resource_pool_id, datastore_id):

View File

@ -29,9 +29,11 @@ class NsxvAvailabilityZonesTestCase(base.BaseTestCase):
self.az_name = 'zone1'
self.group_name = 'az:%s' % self.az_name
config.register_nsxv_azs(cfg.CONF, [self.az_name])
cfg.CONF.set_override("ha_placement_random", True, group="nsxv")
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):
cfg.CONF.set_override("resource_pool_id", resource_pool_id,
group=self.group_name)
cfg.CONF.set_override("datastore_id", datastore_id,
@ -41,6 +43,10 @@ class NsxvAvailabilityZonesTestCase(base.BaseTestCase):
group=self.group_name)
cfg.CONF.set_override("ha_datastore_id", ha_datastore_id,
group=self.group_name)
if ha_placement_random is not None:
cfg.CONF.set_override("ha_placement_random",
ha_placement_random,
group=self.group_name)
def test_simple_availability_zone(self):
self._config_az()
@ -50,6 +56,7 @@ class NsxvAvailabilityZonesTestCase(base.BaseTestCase):
self.assertEqual("datastore", az.datastore_id)
self.assertEqual(True, az.edge_ha)
self.assertEqual("hastore", az.ha_datastore_id)
self.assertEqual(False, az.ha_placement_random)
def test_availability_zone_no_edge_ha(self):
self._config_az(edge_ha=False)
@ -59,6 +66,7 @@ class NsxvAvailabilityZonesTestCase(base.BaseTestCase):
self.assertEqual("datastore", az.datastore_id)
self.assertEqual(False, az.edge_ha)
self.assertEqual(None, az.ha_datastore_id)
self.assertEqual(False, az.ha_placement_random)
def test_availability_zone_no_ha_datastore(self):
self._config_az(ha_datastore_id=None)
@ -68,6 +76,7 @@ class NsxvAvailabilityZonesTestCase(base.BaseTestCase):
self.assertEqual("datastore", az.datastore_id)
self.assertEqual(True, az.edge_ha)
self.assertEqual(None, az.ha_datastore_id)
self.assertEqual(False, az.ha_placement_random)
def test_missing_group_section(self):
self.assertRaises(
@ -97,6 +106,18 @@ class NsxvAvailabilityZonesTestCase(base.BaseTestCase):
self.assertEqual("datastore", az.datastore_id)
self.assertEqual(False, az.edge_ha)
self.assertEqual(None, az.ha_datastore_id)
self.assertEqual(False, az.ha_placement_random)
def test_availability_zone_missing_edge_placement(self):
self._config_az(ha_placement_random=None)
az = nsx_az.ConfiguredAvailabilityZone(self.az_name)
self.assertEqual(self.az_name, az.name)
self.assertEqual("respool", az.resource_pool)
self.assertEqual("datastore", az.datastore_id)
self.assertEqual(True, az.edge_ha)
self.assertEqual("hastore", az.ha_datastore_id)
# ha_placement_random should have the global value
self.assertEqual(True, az.ha_placement_random)
class NsxvAvailabilityZonesOldTestCase(base.BaseTestCase):
@ -113,6 +134,7 @@ class NsxvAvailabilityZonesOldTestCase(base.BaseTestCase):
self.assertEqual("datastore", az.datastore_id)
self.assertEqual(True, az.edge_ha)
self.assertEqual("hastore", az.ha_datastore_id)
self.assertEqual(False, az.ha_placement_random)
def test_availability_zone_without_ha_datastore(self):
az = nsx_az.ConfiguredAvailabilityZone(