diff options
author | Sagar Ratnakara Nikam <nikam@hp.com> | 2014-06-15 19:29:08 +0530 |
---|---|---|
committer | Sagar Ratnakara Nikam <nikam@hp.com> | 2014-07-28 19:15:22 +0530 |
commit | c5de5e7ab166e29304a01d7e310ae6ed32d22090 (patch) | |
tree | 1a572978c35cc3d7017e1e0fb5653e4a39ef860c | |
parent | f7f54ccb20154bd936f8ae62d8479fd7e62ce7ab (diff) | |
download | nova-c5de5e7ab166e29304a01d7e310ae6ed32d22090.tar.gz |
VMWare Driver - Ignore datastore in maintenance mode
A datastore can be part of a datastore cluster. Any
datastore which is part of a cluster can be put in maintenance
mode. Instances cannot reside on a datastore if it is in
maintenance mode.
This fix ignores a datastore in the following scenarios
1. Stats update - Ignore a datastore during stats update.
2. Ignore a datastore during instance spawn.
Change-Id: Id0cb3f5fdb4d0bc4884cf5405fadc433d4a2b6ba
Closes-Bug: #1330065
-rw-r--r-- | nova/tests/virt/vmwareapi/fake.py | 6 | ||||
-rw-r--r-- | nova/tests/virt/vmwareapi/test_ds_util.py | 62 | ||||
-rw-r--r-- | nova/tests/virt/vmwareapi/test_ds_util_datastore_selection.py | 25 | ||||
-rw-r--r-- | nova/tests/virt/vmwareapi/test_vm_util.py | 1 | ||||
-rw-r--r-- | nova/virt/vmwareapi/ds_util.py | 68 |
5 files changed, 124 insertions, 38 deletions
diff --git a/nova/tests/virt/vmwareapi/fake.py b/nova/tests/virt/vmwareapi/fake.py index 81c92402e2..23393e28c4 100644 --- a/nova/tests/virt/vmwareapi/fake.py +++ b/nova/tests/virt/vmwareapi/fake.py @@ -639,13 +639,15 @@ class ClusterComputeResource(ManagedObject): class Datastore(ManagedObject): """Datastore class.""" - def __init__(self, name="fake-ds", capacity=1024, free=500): + def __init__(self, name="fake-ds", capacity=1024, free=500, + accessible=True, maintenance_mode="normal"): super(Datastore, self).__init__("ds") self.set("summary.type", "VMFS") self.set("summary.name", name) self.set("summary.capacity", capacity * units.Gi) self.set("summary.freeSpace", free * units.Gi) - self.set("summary.accessible", True) + self.set("summary.accessible", accessible) + self.set("summary.maintenanceMode", maintenance_mode) self.set("browser", "") diff --git a/nova/tests/virt/vmwareapi/test_ds_util.py b/nova/tests/virt/vmwareapi/test_ds_util.py index 4e158aeeaf..0b8b5ba634 100644 --- a/nova/tests/virt/vmwareapi/test_ds_util.py +++ b/nova/tests/virt/vmwareapi/test_ds_util.py @@ -170,6 +170,10 @@ class DsUtilTestCase(test.NoDBTestCase): def test_get_datastore(self): fake_objects = fake.FakeRetrieveResult() fake_objects.add_object(fake.Datastore()) + fake_objects.add_object(fake.Datastore("fake-ds-2", 2048, 1000, + False, "normal")) + fake_objects.add_object(fake.Datastore("fake-ds-3", 4096, 2000, + True, "inMaintenance")) result = ds_util.get_datastore( fake.FakeObjectRetrievalSession(fake_objects)) @@ -261,6 +265,64 @@ class DsUtilTestCase(test.NoDBTestCase): ds_util.get_datastore, fake.FakeObjectRetrievalSession(fake_objects)) + def test_get_datastore_ds_in_maintenance(self): + data_store = fake.Datastore() + data_store.set("summary.maintenanceMode", "inMaintenance") + + fake_objects = fake.FakeRetrieveResult() + fake_objects.add_object(data_store) + + self.assertRaises(exception.DatastoreNotFound, + ds_util.get_datastore, + fake.FakeObjectRetrievalSession(fake_objects)) + + def _test_is_datastore_valid(self, accessible=True, + maintenance_mode="normal", + type="VMFS", + datastore_regex=None): + propdict = {} + propdict["summary.accessible"] = accessible + propdict["summary.maintenanceMode"] = maintenance_mode + propdict["summary.type"] = type + propdict["summary.name"] = "ds-1" + + return ds_util._is_datastore_valid(propdict, datastore_regex) + + def test_is_datastore_valid(self): + for ds_type in ds_util.ALLOWED_DATASTORE_TYPES: + self.assertTrue(self._test_is_datastore_valid(True, + "normal", + ds_type)) + + def test_is_datastore_valid_inaccessible_ds(self): + self.assertFalse(self._test_is_datastore_valid(False, + "normal", + "VMFS")) + + def test_is_datastore_valid_ds_in_maintenance(self): + self.assertFalse(self._test_is_datastore_valid(True, + "inMaintenance", + "VMFS")) + + def test_is_datastore_valid_ds_type_invalid(self): + self.assertFalse(self._test_is_datastore_valid(True, + "normal", + "vfat")) + + def test_is_datastore_valid_not_matching_regex(self): + datastore_regex = re.compile("ds-2") + self.assertFalse(self._test_is_datastore_valid(True, + "normal", + "VMFS", + datastore_regex)) + + def test_is_datastore_valid_matching_regex(self): + datastore_regex = re.compile("ds-1") + self.assertTrue(self._test_is_datastore_valid(True, + "normal", + "VMFS", + datastore_regex)) + class DatastoreTestCase(test.NoDBTestCase): def test_ds(self): diff --git a/nova/tests/virt/vmwareapi/test_ds_util_datastore_selection.py b/nova/tests/virt/vmwareapi/test_ds_util_datastore_selection.py index c34e76f4c2..ca211902cf 100644 --- a/nova/tests/virt/vmwareapi/test_ds_util_datastore_selection.py +++ b/nova/tests/virt/vmwareapi/test_ds_util_datastore_selection.py @@ -30,10 +30,11 @@ class VMwareDSUtilDatastoreSelectionTestCase(test.NoDBTestCase): def setUp(self): super(VMwareDSUtilDatastoreSelectionTestCase, self).setUp() self.data = [ - ['VMFS', 'os-some-name', True, 987654321, 12346789], - ['NFS', 'another-name', True, 9876543210, 123467890], - ['BAD', 'some-name-bad', True, 98765432100, 1234678900], - ['VMFS', 'some-name-good', False, 987654321, 12346789], + ['VMFS', 'os-some-name', True, 'normal', 987654321, 12346789], + ['NFS', 'another-name', True, 'normal', 9876543210, 123467890], + ['BAD', 'some-name-bad', True, 'normal', 98765432100, 1234678900], + ['VMFS', 'some-name-good', False, 'normal', 987654321, 12346789], + ['VMFS', 'new-name', True, 'inMaintenance', 987654321, 12346789] ] def build_result_set(self, mock_data, name_list=None): @@ -56,7 +57,8 @@ class VMwareDSUtilDatastoreSelectionTestCase(test.NoDBTestCase): @property def propset_name_list(self): return ['summary.type', 'summary.name', 'summary.accessible', - 'summary.capacity', 'summary.freeSpace'] + 'summary.maintenanceMode', 'summary.capacity', + 'summary.freeSpace'] def test_filter_datastores_simple(self): datastores = self.build_result_set(self.data) @@ -95,11 +97,14 @@ class VMwareDSUtilDatastoreSelectionTestCase(test.NoDBTestCase): def test_filter_datastores_specific_match(self): data = [ - ['VMFS', 'os-some-name', True, 987654321, 1234678], - ['NFS', 'another-name', True, 9876543210, 123467890], - ['BAD', 'some-name-bad', True, 98765432100, 1234678900], - ['VMFS', 'some-name-good', True, 987654321, 12346789], - ['VMFS', 'some-other-good', False, 987654321000, 12346789000], + ['VMFS', 'os-some-name', True, 'normal', 987654321, 1234678], + ['NFS', 'another-name', True, 'normal', 9876543210, 123467890], + ['BAD', 'some-name-bad', True, 'normal', 98765432100, 1234678900], + ['VMFS', 'some-name-good', True, 'normal', 987654321, 12346789], + ['VMFS', 'some-other-good', False, 'normal', 987654321000, + 12346789000], + ['VMFS', 'new-name', True, 'inMaintenance', 987654321000, + 12346789000] ] # only the DS some-name-good is accessible and matches the regex datastores = self.build_result_set(data) diff --git a/nova/tests/virt/vmwareapi/test_vm_util.py b/nova/tests/virt/vmwareapi/test_vm_util.py index 62b016696a..9f1f42f5b6 100644 --- a/nova/tests/virt/vmwareapi/test_vm_util.py +++ b/nova/tests/virt/vmwareapi/test_vm_util.py @@ -251,6 +251,7 @@ class VMwareVMUtilTestCase(test.NoDBTestCase): }, 'backing': { 'datastore': { + "summary.maintenanceMode": "normal", "summary.type": "VMFS", "summary.accessible":true, "summary.name": "fake-ds", diff --git a/nova/virt/vmwareapi/ds_util.py b/nova/virt/vmwareapi/ds_util.py index 8db0e665b0..08bf166358 100644 --- a/nova/virt/vmwareapi/ds_util.py +++ b/nova/virt/vmwareapi/ds_util.py @@ -25,6 +25,7 @@ from nova.virt.vmwareapi import vim_util from nova.virt.vmwareapi import vm_util LOG = logging.getLogger(__name__) +ALLOWED_DATASTORE_TYPES = ['VMFS', 'NFS'] class Datastore(object): @@ -200,33 +201,51 @@ def _select_datastore(data_stores, best_match, datastore_regex=None): continue propdict = vm_util.propset_dict(obj_content.propSet) - # Local storage identifier vSphere doesn't support CIFS or - # vfat for datastores, therefore filtered - ds_type = propdict['summary.type'] - ds_name = propdict['summary.name'] - if ((ds_type == 'VMFS' or ds_type == 'NFS') and - propdict.get('summary.accessible')): - if datastore_regex is None or datastore_regex.match(ds_name): - new_ds = Datastore( + if _is_datastore_valid(propdict, datastore_regex): + new_ds = Datastore( ref=obj_content.obj, - name=ds_name, + name=propdict['summary.name'], capacity=propdict['summary.capacity'], freespace=propdict['summary.freeSpace']) - # favor datastores with more free space - if (best_match is None or - new_ds.freespace > best_match.freespace): - best_match = new_ds + # favor datastores with more free space + if (best_match is None or + new_ds.freespace > best_match.freespace): + best_match = new_ds return best_match +def _is_datastore_valid(propdict, datastore_regex): + """Checks if a datastore is valid based on the following criteria. + + Criteria: + - Datastore is accessible + - Datastore is not in maintenance mode (optional) + - Datastore is of a supported disk type + - Datastore matches the supplied regex (optional) + + :param propdict: datastore summary dict + :param datastore_regex : Regex to match the name of a datastore. + """ + + # Local storage identifier vSphere doesn't support CIFS or + # vfat for datastores, therefore filtered + return (propdict.get('summary.accessible') and + (propdict.get('summary.maintenanceMode') is None or + propdict.get('summary.maintenanceMode') == 'normal') and + propdict['summary.type'] in ALLOWED_DATASTORE_TYPES and + (datastore_regex is None or + datastore_regex.match(propdict['summary.name']))) + + def get_datastore(session, cluster=None, host=None, datastore_regex=None): """Get the datastore list and choose the most preferable one.""" if cluster is None and host is None: data_stores = session._call_method(vim_util, "get_objects", "Datastore", ["summary.type", "summary.name", "summary.capacity", "summary.freeSpace", - "summary.accessible"]) + "summary.accessible", + "summary.maintenanceMode"]) else: if cluster is not None: datastore_ret = session._call_method( @@ -247,7 +266,8 @@ def get_datastore(session, cluster=None, host=None, datastore_regex=None): "Datastore", data_store_mors, ["summary.type", "summary.name", "summary.capacity", "summary.freeSpace", - "summary.accessible"]) + "summary.accessible", + "summary.maintenanceMode"]) best_match = None while data_stores: best_match = _select_datastore(data_stores, best_match, @@ -268,7 +288,7 @@ def get_datastore(session, cluster=None, host=None, datastore_regex=None): raise exception.DatastoreNotFound() -def _get_allowed_datastores(data_stores, datastore_regex, allowed_types): +def _get_allowed_datastores(data_stores, datastore_regex): allowed = [] for obj_content in data_stores.objects: # the propset attribute "need not be set" by returning API @@ -276,13 +296,9 @@ def _get_allowed_datastores(data_stores, datastore_regex, allowed_types): continue propdict = vm_util.propset_dict(obj_content.propSet) - # Local storage identifier vSphere doesn't support CIFS or - # vfat for datastores, therefore filtered - ds_type = propdict['summary.type'] - ds_name = propdict['summary.name'] - if (propdict['summary.accessible'] and ds_type in allowed_types): - if datastore_regex is None or datastore_regex.match(ds_name): - allowed.append(Datastore(ref=obj_content.obj, name=ds_name)) + if _is_datastore_valid(propdict, datastore_regex): + allowed.append(Datastore(ref=obj_content.obj, + name=propdict['summary.name'])) return allowed @@ -304,12 +320,12 @@ def get_available_datastores(session, cluster=None, datastore_regex=None): data_stores = session._call_method(vim_util, "get_properties_for_a_collection_of_objects", "Datastore", data_store_mors, - ["summary.type", "summary.name", "summary.accessible"]) + ["summary.type", "summary.name", "summary.accessible", + "summary.maintenanceMode"]) allowed = [] while data_stores: - allowed.extend(_get_allowed_datastores(data_stores, datastore_regex, - ['VMFS', 'NFS'])) + allowed.extend(_get_allowed_datastores(data_stores, datastore_regex)) token = _get_token(data_stores) if not token: break |