VMware: Option to specify datastore name regex
Adding option 'vmware_datastore_regex' to specify the regex pattern to match the name of datastores where backend volumes are created. Change-Id: Ie95d1551b9fbfd36313fd530f9cf06b83735a26c
This commit is contained in:
parent
e271cd549d
commit
f1e21ee252
@ -17,6 +17,8 @@
|
||||
Unit tests for datastore module.
|
||||
"""
|
||||
|
||||
import re
|
||||
|
||||
import mock
|
||||
from oslo_utils import units
|
||||
|
||||
@ -73,9 +75,10 @@ class DatastoreTest(test.TestCase):
|
||||
def _create_summary(
|
||||
self, ds, free_space=units.Mi, _type=ds_sel.DatastoreType.VMFS,
|
||||
capacity=2 * units.Mi, accessible=True):
|
||||
return mock.Mock(datastore=ds, freeSpace=free_space, type=_type,
|
||||
capacity=capacity, accessible=accessible,
|
||||
name=ds.value)
|
||||
summary = mock.Mock(datastore=ds, freeSpace=free_space, type=_type,
|
||||
capacity=capacity, accessible=accessible)
|
||||
summary.name = ds.value
|
||||
return summary
|
||||
|
||||
def _create_host(self, value):
|
||||
host = mock.Mock(spec=['_type', 'value'], name=value)
|
||||
@ -145,10 +148,15 @@ class DatastoreTest(test.TestCase):
|
||||
'host': host_mounts1}
|
||||
|
||||
# valid datastore
|
||||
ds10 = self._create_datastore('ds-10')
|
||||
ds10_props = {'summary': self._create_summary(ds10),
|
||||
ds1a = self._create_datastore('ds-1a')
|
||||
ds1a_props = {'summary': self._create_summary(ds1a),
|
||||
'host': host_mounts1}
|
||||
filter_by_profile.return_value = {ds1a: ds1a_props}
|
||||
|
||||
# datastore name not matching the regex filter
|
||||
ds1b = self._create_datastore('ds-1b')
|
||||
ds1b_props = {'summary': self._create_summary(ds1b),
|
||||
'host': host_mounts1}
|
||||
filter_by_profile.return_value = {ds10: ds10_props}
|
||||
|
||||
datastores = {ds1: ds1_props,
|
||||
ds2: ds2_props,
|
||||
@ -159,8 +167,10 @@ class DatastoreTest(test.TestCase):
|
||||
ds7: ds7_props,
|
||||
ds8: ds8_props,
|
||||
ds9: ds9_props,
|
||||
ds10: ds10_props}
|
||||
ds1a: ds1a_props,
|
||||
ds1b: ds1b_props}
|
||||
profile_id = mock.sentinel.profile_id
|
||||
self._ds_sel._ds_regex = re.compile("ds-[1-9a]{1,2}$")
|
||||
datastores = self._ds_sel._filter_datastores(
|
||||
datastores,
|
||||
512,
|
||||
@ -169,9 +179,9 @@ class DatastoreTest(test.TestCase):
|
||||
{ds_sel.DatastoreType.VMFS, ds_sel.DatastoreType.NFS},
|
||||
valid_host_refs=[host1, host2])
|
||||
|
||||
self.assertEqual({ds10: ds10_props}, datastores)
|
||||
self.assertEqual({ds1a: ds1a_props}, datastores)
|
||||
filter_by_profile.assert_called_once_with(
|
||||
{ds9: ds9_props, ds10: ds10_props},
|
||||
{ds9: ds9_props, ds1a: ds1a_props},
|
||||
profile_id)
|
||||
|
||||
def test_filter_datastores_with_empty_datastores(self):
|
||||
|
@ -17,6 +17,8 @@
|
||||
Test suite for VMware vCenter VMDK driver.
|
||||
"""
|
||||
|
||||
import re
|
||||
|
||||
import ddt
|
||||
import mock
|
||||
from oslo_utils import units
|
||||
@ -98,6 +100,7 @@ class VMwareVcVmdkDriverTestCase(test.TestCase):
|
||||
self._config.vmware_adapter_type = self.ADAPTER_TYPE
|
||||
self._config.vmware_snapshot_format = self.SNAPSHOT_FORMAT
|
||||
self._config.vmware_lazy_create = True
|
||||
self._config.vmware_datastore_regex = None
|
||||
|
||||
self._db = mock.Mock()
|
||||
self._driver = vmdk.VMwareVcVmdkDriver(configuration=self._config,
|
||||
@ -1680,6 +1683,7 @@ class VMwareVcVmdkDriverTestCase(test.TestCase):
|
||||
label='OpenStack Cinder')
|
||||
|
||||
@mock.patch.object(VMDK_DRIVER, '_validate_params')
|
||||
@mock.patch('re.compile')
|
||||
@mock.patch.object(VMDK_DRIVER, '_get_vc_version')
|
||||
@mock.patch.object(VMDK_DRIVER, '_validate_vcenter_version')
|
||||
@mock.patch('oslo_vmware.pbm.get_pbm_wsdl_location')
|
||||
@ -1690,8 +1694,9 @@ class VMwareVcVmdkDriverTestCase(test.TestCase):
|
||||
@mock.patch.object(VMDK_DRIVER, 'session')
|
||||
def _test_do_setup(
|
||||
self, session, vops, ds_sel_cls, vops_cls, register_extension,
|
||||
get_pbm_wsdl_loc, validate_vc_version, get_vc_version,
|
||||
validate_params, enable_pbm=True):
|
||||
get_pbm_wsdl_loc, validate_vc_version, get_vc_version, re_compile,
|
||||
validate_params, enable_pbm=True, ds_regex_pat=None,
|
||||
invalid_regex=False):
|
||||
if enable_pbm:
|
||||
ver_str = '5.5'
|
||||
pbm_wsdl = mock.sentinel.pbm_wsdl
|
||||
@ -1705,30 +1710,51 @@ class VMwareVcVmdkDriverTestCase(test.TestCase):
|
||||
cluster_refs = {'cls-1': cls_1, 'cls-2': cls_2}
|
||||
vops.get_cluster_refs.return_value = cluster_refs
|
||||
|
||||
self._driver.do_setup(mock.ANY)
|
||||
self._driver.configuration.vmware_datastore_regex = ds_regex_pat
|
||||
ds_regex = None
|
||||
if ds_regex_pat:
|
||||
if invalid_regex:
|
||||
re_compile.side_effect = re.error("error")
|
||||
else:
|
||||
ds_regex = mock.sentinel.ds_regex
|
||||
re_compile.return_value = ds_regex
|
||||
|
||||
validate_params.assert_called_once_with()
|
||||
get_vc_version.assert_called_once_with()
|
||||
validate_vc_version.assert_called_once_with(ver_str)
|
||||
if enable_pbm:
|
||||
get_pbm_wsdl_loc.assert_called_once_with(ver_str)
|
||||
self.assertEqual(pbm_wsdl, self._driver.pbm_wsdl)
|
||||
self.assertEqual(enable_pbm, self._driver._storage_policy_enabled)
|
||||
register_extension.assert_called_once()
|
||||
vops_cls.assert_called_once_with(
|
||||
session, self._driver.configuration.vmware_max_objects_retrieval,
|
||||
vmdk.EXTENSION_KEY, vmdk.EXTENSION_TYPE)
|
||||
self.assertEqual(vops_cls.return_value, self._driver._volumeops)
|
||||
ds_sel_cls.assert_called_once_with(
|
||||
vops,
|
||||
session,
|
||||
self._driver.configuration.vmware_max_objects_retrieval)
|
||||
self.assertEqual(ds_sel_cls.return_value, self._driver._ds_sel)
|
||||
vops.get_cluster_refs.assert_called_once_with(
|
||||
self._driver.configuration.vmware_cluster_name)
|
||||
vops.build_backing_ref_cache.assert_called_once_with()
|
||||
self.assertEqual(list(cluster_refs.values()),
|
||||
list(self._driver._clusters))
|
||||
if ds_regex_pat and invalid_regex:
|
||||
self.assertRaises(cinder_exceptions.InvalidInput,
|
||||
self._driver.do_setup,
|
||||
mock.ANY)
|
||||
validate_params.assert_called_once_with()
|
||||
else:
|
||||
self._driver.do_setup(mock.ANY)
|
||||
|
||||
validate_params.assert_called_once_with()
|
||||
get_vc_version.assert_called_once_with()
|
||||
validate_vc_version.assert_called_once_with(ver_str)
|
||||
if enable_pbm:
|
||||
get_pbm_wsdl_loc.assert_called_once_with(ver_str)
|
||||
self.assertEqual(pbm_wsdl, self._driver.pbm_wsdl)
|
||||
self.assertEqual(enable_pbm, self._driver._storage_policy_enabled)
|
||||
register_extension.assert_called_once()
|
||||
vops_cls.assert_called_once_with(
|
||||
session,
|
||||
self._driver.configuration.vmware_max_objects_retrieval,
|
||||
vmdk.EXTENSION_KEY,
|
||||
vmdk.EXTENSION_TYPE)
|
||||
self.assertEqual(vops_cls.return_value, self._driver._volumeops)
|
||||
ds_sel_cls.assert_called_once_with(
|
||||
vops,
|
||||
session,
|
||||
self._driver.configuration.vmware_max_objects_retrieval,
|
||||
ds_regex=ds_regex)
|
||||
self.assertEqual(ds_sel_cls.return_value, self._driver._ds_sel)
|
||||
vops.get_cluster_refs.assert_called_once_with(
|
||||
self._driver.configuration.vmware_cluster_name)
|
||||
vops.build_backing_ref_cache.assert_called_once_with()
|
||||
self.assertEqual(list(cluster_refs.values()),
|
||||
list(self._driver._clusters))
|
||||
|
||||
if ds_regex_pat:
|
||||
re_compile.assert_called_once_with(ds_regex_pat)
|
||||
|
||||
def test_do_setup(self):
|
||||
self._test_do_setup()
|
||||
@ -1757,6 +1783,12 @@ class VMwareVcVmdkDriverTestCase(test.TestCase):
|
||||
validate_vc_version.assert_called_once_with(ver_str)
|
||||
get_pbm_wsdl_loc.assert_called_once_with(ver_str)
|
||||
|
||||
def test_do_setup_with_ds_regex(self):
|
||||
self._test_do_setup(ds_regex_pat='foo')
|
||||
|
||||
def test_do_setup_with_invalid_ds_regex(self):
|
||||
self._test_do_setup(ds_regex_pat='(foo', invalid_regex=True)
|
||||
|
||||
@mock.patch.object(VMDK_DRIVER, 'volumeops')
|
||||
def test_get_dc(self, vops):
|
||||
dc_1 = mock.sentinel.dc_1
|
||||
|
@ -54,10 +54,11 @@ class DatastoreSelector(object):
|
||||
PROFILE_NAME = "storageProfileName"
|
||||
|
||||
# TODO(vbala) Remove dependency on volumeops.
|
||||
def __init__(self, vops, session, max_objects):
|
||||
def __init__(self, vops, session, max_objects, ds_regex=None):
|
||||
self._vops = vops
|
||||
self._session = session
|
||||
self._max_objects = max_objects
|
||||
self._ds_regex = ds_regex
|
||||
self._profile_id_cache = {}
|
||||
|
||||
@coordination.synchronized('vmware-datastore-profile-{profile_name}')
|
||||
@ -129,6 +130,9 @@ class DatastoreSelector(object):
|
||||
if (summary is None or host_mounts is None):
|
||||
return False
|
||||
|
||||
if self._ds_regex and not self._ds_regex.match(summary.name):
|
||||
return False
|
||||
|
||||
if (hard_anti_affinity_ds and
|
||||
ds_ref.value in hard_anti_affinity_ds):
|
||||
return False
|
||||
|
@ -23,6 +23,7 @@ machine is never powered on and is often referred as the shadow VM.
|
||||
"""
|
||||
|
||||
import math
|
||||
import re
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
@ -150,6 +151,9 @@ vmdk_opts = [
|
||||
' lazily when the volume is created without any source. '
|
||||
'The backend volume is created when the volume is '
|
||||
'attached, uploaded to image service or during backup.'),
|
||||
cfg.StrOpt('vmware_datastore_regex',
|
||||
help='Regular expression pattern to match the name of '
|
||||
'datastores where backend volumes are created.')
|
||||
]
|
||||
|
||||
CONF = cfg.CONF
|
||||
@ -258,7 +262,8 @@ class VMwareVcVmdkDriver(driver.VolumeDriver):
|
||||
# improve scalability of querying volumes in backend (bug 1600754)
|
||||
# 3.1.0 - support adapter type change using retype
|
||||
# 3.2.0 - config option to disable lazy creation of backend volume
|
||||
VERSION = '3.2.0'
|
||||
# 3.3.0 - config option to specify datastore name regex
|
||||
VERSION = '3.3.0'
|
||||
|
||||
# ThirdPartySystems wiki page
|
||||
CI_WIKI_NAME = "VMware_CI"
|
||||
@ -281,6 +286,7 @@ class VMwareVcVmdkDriver(driver.VolumeDriver):
|
||||
self._ds_sel = None
|
||||
self._clusters = None
|
||||
self._dc_cache = {}
|
||||
self._ds_regex = None
|
||||
|
||||
@property
|
||||
def volumeops(self):
|
||||
@ -1825,6 +1831,14 @@ class VMwareVcVmdkDriver(driver.VolumeDriver):
|
||||
"""Any initialization the volume driver does while starting."""
|
||||
self._validate_params()
|
||||
|
||||
regex_pattern = self.configuration.vmware_datastore_regex
|
||||
if regex_pattern:
|
||||
try:
|
||||
self._ds_regex = re.compile(regex_pattern)
|
||||
except re.error:
|
||||
raise exception.InvalidInput(reason=_(
|
||||
"Invalid regular expression: %s.") % regex_pattern)
|
||||
|
||||
# Validate vCenter version.
|
||||
self._vc_version = self._get_vc_version()
|
||||
self._validate_vcenter_version(self._vc_version)
|
||||
@ -1851,7 +1865,7 @@ class VMwareVcVmdkDriver(driver.VolumeDriver):
|
||||
self._volumeops = volumeops.VMwareVolumeOps(
|
||||
self.session, max_objects, EXTENSION_KEY, EXTENSION_TYPE)
|
||||
self._ds_sel = hub.DatastoreSelector(
|
||||
self.volumeops, self.session, max_objects)
|
||||
self.volumeops, self.session, max_objects, ds_regex=self._ds_regex)
|
||||
|
||||
# Get clusters to be used for backing VM creation.
|
||||
cluster_names = self.configuration.vmware_cluster_name
|
||||
|
@ -0,0 +1,7 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
VMware VMDK driver and FCD driver now support a config option
|
||||
``vmware_datastore_regex`` to specify the regular expression
|
||||
pattern to match the name of datastores where backend volumes
|
||||
are created.
|
Loading…
Reference in New Issue
Block a user