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:
Vipin Balachandran 2018-03-02 14:55:43 -08:00
parent e271cd549d
commit f1e21ee252
5 changed files with 104 additions and 37 deletions

View File

@ -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):

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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.