NSXv: New way to configure availability zones
Configure availability zones in nsx.ini using a dynamic sections. The previous way of con figuring all in 1 line still works. Example: [nsxv] availability_zones = zone1, zone2 [az:zone1] resource_pool_id = resgroup-28 datastore_id = datastore-21 edge_ha = True ha_datastore_id = datastore-22 [az:zone2] ... Change-Id: I344d2fc1de246fdaf272a6917638716c956e1e23
This commit is contained in:
parent
3756e8bd6f
commit
dd1923005c
@ -451,9 +451,8 @@ nsxv_opts = [
|
|||||||
cfg.ListOpt('availability_zones',
|
cfg.ListOpt('availability_zones',
|
||||||
default=[],
|
default=[],
|
||||||
help=_('Optional parameter defining the availability zones '
|
help=_('Optional parameter defining the availability zones '
|
||||||
'for deploying NSX Edges with the format: <zone name>:'
|
'names for deploying NSX Edges. The configuration of '
|
||||||
'<resource pool id]:<datastore id>:<edge_ha True/False>'
|
'each zone will be under a group names [az:<name>]')),
|
||||||
'<(optional)HA datastore id>.')),
|
|
||||||
cfg.StrOpt('datastore_id',
|
cfg.StrOpt('datastore_id',
|
||||||
deprecated_group="vcns",
|
deprecated_group="vcns",
|
||||||
help=_('Optional parameter identifying the ID of datastore to '
|
help=_('Optional parameter identifying the ID of datastore to '
|
||||||
@ -650,6 +649,22 @@ nsxv_opts = [
|
|||||||
"be associated with all router interfaces.")),
|
"be associated with all router interfaces.")),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# define the configuration of each availability zone.
|
||||||
|
# the list of expected zones in under nsxv group: availability_zones
|
||||||
|
nsxv_az_opts = [
|
||||||
|
cfg.StrOpt('resource_pool_id',
|
||||||
|
help=_('Identifying the ID of resource to deploy NSX Edges')),
|
||||||
|
cfg.StrOpt('datastore_id',
|
||||||
|
help=_('Identifying the ID of datastore to deploy NSX Edges')),
|
||||||
|
cfg.BoolOpt('edge_ha',
|
||||||
|
default=False,
|
||||||
|
help=_("(Optional) Enable HA for NSX Edges.")),
|
||||||
|
cfg.StrOpt('ha_datastore_id',
|
||||||
|
help=_('Optional parameter identifying the ID of datastore to '
|
||||||
|
'deploy NSX Edges in addition to data_store_id in case'
|
||||||
|
'edge_ha is True')),
|
||||||
|
]
|
||||||
|
|
||||||
# Register the configuration options
|
# Register the configuration options
|
||||||
cfg.CONF.register_opts(connection_opts)
|
cfg.CONF.register_opts(connection_opts)
|
||||||
cfg.CONF.register_opts(cluster_opts)
|
cfg.CONF.register_opts(cluster_opts)
|
||||||
@ -659,11 +674,43 @@ cfg.CONF.register_opts(nsxv_opts, group="nsxv")
|
|||||||
cfg.CONF.register_opts(base_opts, group="NSX")
|
cfg.CONF.register_opts(base_opts, group="NSX")
|
||||||
cfg.CONF.register_opts(sync_opts, group="NSX_SYNC")
|
cfg.CONF.register_opts(sync_opts, group="NSX_SYNC")
|
||||||
|
|
||||||
# registser l3_ha config opts. This is due to commit
|
# register l3_ha config opts. This is due to commit
|
||||||
# a7c633dc8e8a67e65e558ecbdf9ea8efc5468251
|
# a7c633dc8e8a67e65e558ecbdf9ea8efc5468251
|
||||||
cfg.CONF.register_opts(l3_hamode_db.L3_HA_OPTS)
|
cfg.CONF.register_opts(l3_hamode_db.L3_HA_OPTS)
|
||||||
|
|
||||||
|
|
||||||
|
# register a group for each nsxv availability zones
|
||||||
|
def register_nsxv_azs(conf, availability_zones):
|
||||||
|
# first verify that the availability zones are in the format of a
|
||||||
|
# list of names. The old format was a list of values for each az,
|
||||||
|
# separated with ':'
|
||||||
|
if not availability_zones or len(availability_zones[0].split(':')) > 1:
|
||||||
|
return
|
||||||
|
|
||||||
|
for az in availability_zones:
|
||||||
|
az_group = 'az:%s' % az
|
||||||
|
conf.register_group(cfg.OptGroup(
|
||||||
|
name=az_group,
|
||||||
|
title="Configuration for availability zone %s" % az))
|
||||||
|
conf.register_opts(nsxv_az_opts, group=az_group)
|
||||||
|
|
||||||
|
|
||||||
|
register_nsxv_azs(cfg.CONF, cfg.CONF.nsxv.availability_zones)
|
||||||
|
|
||||||
|
|
||||||
|
def get_nsxv_az_opts(az):
|
||||||
|
az_info = dict()
|
||||||
|
group = 'az:%s' % az
|
||||||
|
if group not in cfg.CONF:
|
||||||
|
raise nsx_exc.NsxInvalidConfiguration(
|
||||||
|
opt_name=group,
|
||||||
|
opt_value='None',
|
||||||
|
reason=(_("Configuration group \'%s\' must be defined") % group))
|
||||||
|
for opt in nsxv_az_opts:
|
||||||
|
az_info[opt.name] = cfg.CONF[group][opt.name]
|
||||||
|
return az_info
|
||||||
|
|
||||||
|
|
||||||
def validate_nsxv_config_options():
|
def validate_nsxv_config_options():
|
||||||
if (cfg.CONF.nsxv.manager_uri is None or
|
if (cfg.CONF.nsxv.manager_uri is None or
|
||||||
cfg.CONF.nsxv.user is None or
|
cfg.CONF.nsxv.user is None or
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
|
|
||||||
from vmware_nsx._i18n import _
|
from vmware_nsx._i18n import _
|
||||||
|
from vmware_nsx.common import config
|
||||||
from vmware_nsx.common import exceptions as nsx_exc
|
from vmware_nsx.common import exceptions as nsx_exc
|
||||||
|
|
||||||
DEFAULT_NAME = 'default'
|
DEFAULT_NAME = 'default'
|
||||||
@ -24,7 +25,9 @@ DEFAULT_NAME = 'default'
|
|||||||
class ConfiguredAvailabilityZone(object):
|
class ConfiguredAvailabilityZone(object):
|
||||||
|
|
||||||
def __init__(self, config_line):
|
def __init__(self, config_line):
|
||||||
if config_line:
|
if config_line and ':' in config_line:
|
||||||
|
# Older configuration - each line contains all the relevant
|
||||||
|
# values for one availability zones, separated by ':'
|
||||||
values = config_line.split(':')
|
values = config_line.split(':')
|
||||||
if len(values) < 4 or len(values) > 5:
|
if len(values) < 4 or len(values) > 5:
|
||||||
raise nsx_exc.NsxInvalidConfiguration(
|
raise nsx_exc.NsxInvalidConfiguration(
|
||||||
@ -63,6 +66,29 @@ 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
|
||||||
|
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
|
||||||
|
az_info = config.get_nsxv_az_opts(self.name)
|
||||||
|
self.resource_pool = az_info.get('resource_pool_id')
|
||||||
|
if not self.resource_pool:
|
||||||
|
raise nsx_exc.NsxInvalidConfiguration(
|
||||||
|
opt_name="resource_pool_id",
|
||||||
|
opt_value='None',
|
||||||
|
reason=(_("resource_pool_id for availability zone %s "
|
||||||
|
"must be defined") % self.name))
|
||||||
|
self.datastore_id = az_info.get('datastore_id')
|
||||||
|
if not self.datastore_id:
|
||||||
|
raise nsx_exc.NsxInvalidConfiguration(
|
||||||
|
opt_name="datastore_id",
|
||||||
|
opt_value='None',
|
||||||
|
reason=(_("datastore_id for availability zone %s "
|
||||||
|
"must be defined") % self.name))
|
||||||
|
self.edge_ha = az_info.get('edge_ha', False)
|
||||||
|
# The HA datastore can be empty
|
||||||
|
self.ha_datastore_id = (az_info.get('ha_datastore_id')
|
||||||
|
if self.edge_ha else None)
|
||||||
else:
|
else:
|
||||||
# use the default configuration
|
# use the default configuration
|
||||||
self.name = DEFAULT_NAME
|
self.name = DEFAULT_NAME
|
||||||
|
@ -18,6 +18,7 @@ from oslo_config import cfg
|
|||||||
from neutron import context as neutron_context
|
from neutron import context as neutron_context
|
||||||
from neutron.db import common_db_mixin as common_db
|
from neutron.db import common_db_mixin as common_db
|
||||||
|
|
||||||
|
from vmware_nsx.common import config
|
||||||
from vmware_nsx import plugin
|
from vmware_nsx import plugin
|
||||||
from vmware_nsx.plugins.nsx_v.vshield import vcns
|
from vmware_nsx.plugins.nsx_v.vshield import vcns
|
||||||
|
|
||||||
@ -38,6 +39,11 @@ class NeutronDbClient(common_db.CommonDbMixin):
|
|||||||
|
|
||||||
|
|
||||||
class NsxVPluginWrapper(plugin.NsxVPlugin):
|
class NsxVPluginWrapper(plugin.NsxVPlugin):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
config.register_nsxv_azs(cfg.CONF, cfg.CONF.nsxv.availability_zones)
|
||||||
|
super(NsxVPluginWrapper, self).__init__()
|
||||||
|
|
||||||
def _start_rpc_listeners(self):
|
def _start_rpc_listeners(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -13,14 +13,98 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
|
from oslo_config import cfg
|
||||||
|
|
||||||
from neutron.tests import base
|
from neutron.tests import base
|
||||||
|
|
||||||
|
from vmware_nsx.common import config
|
||||||
from vmware_nsx.common import exceptions as nsx_exc
|
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
|
||||||
|
|
||||||
|
|
||||||
class NsxvAvailabilityZonesTestCase(base.BaseTestCase):
|
class NsxvAvailabilityZonesTestCase(base.BaseTestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(NsxvAvailabilityZonesTestCase, self).setUp()
|
||||||
|
self.az_name = 'zone1'
|
||||||
|
self.group_name = 'az:%s' % self.az_name
|
||||||
|
config.register_nsxv_azs(cfg.CONF, [self.az_name])
|
||||||
|
|
||||||
|
def _config_az(self, resource_pool_id="respool", datastore_id="datastore",
|
||||||
|
edge_ha=True, ha_datastore_id="hastore"):
|
||||||
|
cfg.CONF.set_override("resource_pool_id", resource_pool_id,
|
||||||
|
group=self.group_name)
|
||||||
|
cfg.CONF.set_override("datastore_id", datastore_id,
|
||||||
|
group=self.group_name)
|
||||||
|
if edge_ha is not None:
|
||||||
|
cfg.CONF.set_override("edge_ha", edge_ha,
|
||||||
|
group=self.group_name)
|
||||||
|
cfg.CONF.set_override("ha_datastore_id", ha_datastore_id,
|
||||||
|
group=self.group_name)
|
||||||
|
|
||||||
|
def test_simple_availability_zone(self):
|
||||||
|
self._config_az()
|
||||||
|
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)
|
||||||
|
|
||||||
|
def test_availability_zone_no_edge_ha(self):
|
||||||
|
self._config_az(edge_ha=False)
|
||||||
|
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(False, az.edge_ha)
|
||||||
|
self.assertEqual(None, az.ha_datastore_id)
|
||||||
|
|
||||||
|
def test_availability_zone_no_ha_datastore(self):
|
||||||
|
self._config_az(ha_datastore_id=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(None, az.ha_datastore_id)
|
||||||
|
|
||||||
|
def test_missing_group_section(self):
|
||||||
|
self.assertRaises(
|
||||||
|
nsx_exc.NsxInvalidConfiguration,
|
||||||
|
nsx_az.ConfiguredAvailabilityZone,
|
||||||
|
"doesnt_exist")
|
||||||
|
|
||||||
|
def test_availability_zone_missing_respool(self):
|
||||||
|
self._config_az(resource_pool_id=None)
|
||||||
|
self.assertRaises(
|
||||||
|
nsx_exc.NsxInvalidConfiguration,
|
||||||
|
nsx_az.ConfiguredAvailabilityZone,
|
||||||
|
self.az_name)
|
||||||
|
|
||||||
|
def test_availability_zone_missing_datastore(self):
|
||||||
|
self._config_az(datastore_id=None)
|
||||||
|
self.assertRaises(
|
||||||
|
nsx_exc.NsxInvalidConfiguration,
|
||||||
|
nsx_az.ConfiguredAvailabilityZone,
|
||||||
|
self.az_name)
|
||||||
|
|
||||||
|
def test_availability_zone_missing_edge_ha(self):
|
||||||
|
self._config_az(edge_ha=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(False, az.edge_ha)
|
||||||
|
self.assertEqual(None, az.ha_datastore_id)
|
||||||
|
|
||||||
|
|
||||||
|
class NsxvAvailabilityZonesOldTestCase(base.BaseTestCase):
|
||||||
|
"""Test old way of configuring the availability zones
|
||||||
|
|
||||||
|
using a one-line configuration instead of different dynamic sections
|
||||||
|
"""
|
||||||
|
|
||||||
def test_simple_availability_zone(self):
|
def test_simple_availability_zone(self):
|
||||||
az = nsx_az.ConfiguredAvailabilityZone(
|
az = nsx_az.ConfiguredAvailabilityZone(
|
||||||
"name:respool:datastore:true:hastore")
|
"name:respool:datastore:true:hastore")
|
||||||
|
@ -54,6 +54,7 @@ import six
|
|||||||
import webob.exc
|
import webob.exc
|
||||||
|
|
||||||
from vmware_nsx._i18n import _
|
from vmware_nsx._i18n import _
|
||||||
|
from vmware_nsx.common import config
|
||||||
from vmware_nsx.common import exceptions as nsxv_exc
|
from vmware_nsx.common import exceptions as nsxv_exc
|
||||||
from vmware_nsx.common import nsx_constants
|
from vmware_nsx.common import nsx_constants
|
||||||
from vmware_nsx.common import utils as c_utils
|
from vmware_nsx.common import utils as c_utils
|
||||||
@ -88,6 +89,22 @@ PLUGIN_NAME = 'vmware_nsx.plugin.NsxVPlugin'
|
|||||||
_uuid = uuidutils.generate_uuid
|
_uuid = uuidutils.generate_uuid
|
||||||
|
|
||||||
|
|
||||||
|
def set_az_in_config(name, resource_pool_id="respool-7",
|
||||||
|
datastore_id="datastore-7",
|
||||||
|
edge_ha=False, ha_datastore_id=None):
|
||||||
|
group_name = 'az:%s' % name
|
||||||
|
cfg.CONF.set_override('availability_zones', [name], group="nsxv")
|
||||||
|
config.register_nsxv_azs(cfg.CONF, [name])
|
||||||
|
cfg.CONF.set_override("resource_pool_id", resource_pool_id,
|
||||||
|
group=group_name)
|
||||||
|
cfg.CONF.set_override("datastore_id", datastore_id,
|
||||||
|
group=group_name)
|
||||||
|
cfg.CONF.set_override("edge_ha", edge_ha,
|
||||||
|
group=group_name)
|
||||||
|
cfg.CONF.set_override("ha_datastore_id", ha_datastore_id,
|
||||||
|
group=group_name)
|
||||||
|
|
||||||
|
|
||||||
class NsxVPluginV2TestCase(test_plugin.NeutronDbPluginV2TestCase):
|
class NsxVPluginV2TestCase(test_plugin.NeutronDbPluginV2TestCase):
|
||||||
|
|
||||||
def _create_network(self, fmt, name, admin_state_up,
|
def _create_network(self, fmt, name, admin_state_up,
|
||||||
@ -143,6 +160,7 @@ class NsxVPluginV2TestCase(test_plugin.NeutronDbPluginV2TestCase):
|
|||||||
self.default_res_pool = 'respool-28'
|
self.default_res_pool = 'respool-28'
|
||||||
cfg.CONF.set_override("resource_pool_id", self.default_res_pool,
|
cfg.CONF.set_override("resource_pool_id", self.default_res_pool,
|
||||||
group="nsxv")
|
group="nsxv")
|
||||||
|
set_az_in_config('az7')
|
||||||
if service_plugins is not None:
|
if service_plugins is not None:
|
||||||
# override the service plugins only if specified directly
|
# override the service plugins only if specified directly
|
||||||
super(NsxVPluginV2TestCase, self).setUp(
|
super(NsxVPluginV2TestCase, self).setUp(
|
||||||
@ -646,8 +664,7 @@ class TestNetworksV2(test_plugin.TestNetworksV2, NsxVPluginV2TestCase):
|
|||||||
|
|
||||||
def test_create_network_with_az_hint(self):
|
def test_create_network_with_az_hint(self):
|
||||||
az_name = 'az7'
|
az_name = 'az7'
|
||||||
az_config = az_name + ':respool-7:datastore-7:False'
|
set_az_in_config(az_name)
|
||||||
cfg.CONF.set_override('availability_zones', [az_config], group="nsxv")
|
|
||||||
p = directory.get_plugin()
|
p = directory.get_plugin()
|
||||||
p._availability_zones_data = nsx_az.ConfiguredAvailabilityZones()
|
p._availability_zones_data = nsx_az.ConfiguredAvailabilityZones()
|
||||||
ctx = context.get_admin_context()
|
ctx = context.get_admin_context()
|
||||||
@ -3158,8 +3175,7 @@ class TestExclusiveRouterTestCase(L3NatTest, L3NatTestCaseBase,
|
|||||||
|
|
||||||
def test_create_router_with_az_hint(self):
|
def test_create_router_with_az_hint(self):
|
||||||
az_name = 'az7'
|
az_name = 'az7'
|
||||||
az_config = az_name + ':respool-7:datastore-7:True'
|
set_az_in_config(az_name)
|
||||||
cfg.CONF.set_override('availability_zones', [az_config], group="nsxv")
|
|
||||||
p = directory.get_plugin()
|
p = directory.get_plugin()
|
||||||
p._availability_zones_data = nsx_az.ConfiguredAvailabilityZones()
|
p._availability_zones_data = nsx_az.ConfiguredAvailabilityZones()
|
||||||
p._get_edge_id_by_rtr_id = p.real_get_edge
|
p._get_edge_id_by_rtr_id = p.real_get_edge
|
||||||
@ -3367,9 +3383,7 @@ class TestVdrTestCase(L3NatTest, L3NatTestCaseBase,
|
|||||||
def setUp(self, plugin=PLUGIN_NAME, ext_mgr=None, service_plugins=None):
|
def setUp(self, plugin=PLUGIN_NAME, ext_mgr=None, service_plugins=None):
|
||||||
# init the availability zones in the configuration of the plugin
|
# init the availability zones in the configuration of the plugin
|
||||||
self.az_name = 'az7'
|
self.az_name = 'az7'
|
||||||
az_config = self.az_name + ':respool-7:datastore-7:False'
|
set_az_in_config(self.az_name)
|
||||||
cfg.CONF.set_override('availability_zones', [az_config], group="nsxv")
|
|
||||||
|
|
||||||
super(TestVdrTestCase, self).setUp(
|
super(TestVdrTestCase, self).setUp(
|
||||||
plugin=plugin, ext_mgr=ext_mgr, service_plugins=service_plugins)
|
plugin=plugin, ext_mgr=ext_mgr, service_plugins=service_plugins)
|
||||||
self.plugin_instance.nsx_v.is_subnet_in_use = mock.Mock()
|
self.plugin_instance.nsx_v.is_subnet_in_use = mock.Mock()
|
||||||
@ -3580,8 +3594,7 @@ class TestVdrTestCase(L3NatTest, L3NatTestCaseBase,
|
|||||||
def _test_create_rotuer_with_az_hint(self, with_hint):
|
def _test_create_rotuer_with_az_hint(self, with_hint):
|
||||||
# init the availability zones in the plugin
|
# init the availability zones in the plugin
|
||||||
az_name = 'az7'
|
az_name = 'az7'
|
||||||
az_config = az_name + ':respool-7:datastore-7:False'
|
set_az_in_config(az_name)
|
||||||
cfg.CONF.set_override('availability_zones', [az_config], group="nsxv")
|
|
||||||
p = directory.get_plugin()
|
p = directory.get_plugin()
|
||||||
p._availability_zones_data = nsx_az.ConfiguredAvailabilityZones()
|
p._availability_zones_data = nsx_az.ConfiguredAvailabilityZones()
|
||||||
|
|
||||||
@ -4837,8 +4850,7 @@ class TestSharedRouterTestCase(L3NatTest, L3NatTestCaseBase,
|
|||||||
def _test_create_rotuer_with_az_hint(self, with_hint):
|
def _test_create_rotuer_with_az_hint(self, with_hint):
|
||||||
# init the availability zones in the plugin
|
# init the availability zones in the plugin
|
||||||
az_name = 'az7'
|
az_name = 'az7'
|
||||||
az_config = az_name + ':respool-7:datastore-7:True'
|
set_az_in_config(az_name)
|
||||||
cfg.CONF.set_override('availability_zones', [az_config], group="nsxv")
|
|
||||||
p = directory.get_plugin()
|
p = directory.get_plugin()
|
||||||
p._availability_zones_data = nsx_az.ConfiguredAvailabilityZones()
|
p._availability_zones_data = nsx_az.ConfiguredAvailabilityZones()
|
||||||
|
|
||||||
@ -4911,8 +4923,7 @@ class TestRouterFlavorTestCase(extension.ExtensionTestCase,
|
|||||||
|
|
||||||
# init the availability zones
|
# init the availability zones
|
||||||
self.az_name = 'az7'
|
self.az_name = 'az7'
|
||||||
az_config = self.az_name + ':respool-7:datastore-7:True'
|
set_az_in_config(self.az_name)
|
||||||
cfg.CONF.set_override('availability_zones', [az_config], group="nsxv")
|
|
||||||
self.plugin._availability_zones_data = (
|
self.plugin._availability_zones_data = (
|
||||||
nsx_az.ConfiguredAvailabilityZones())
|
nsx_az.ConfiguredAvailabilityZones())
|
||||||
self._iteration = 1
|
self._iteration = 1
|
||||||
|
Loading…
x
Reference in New Issue
Block a user