From: Vipin Balachandran Date: Wed, 27 Aug 2014 14:56:34 +0000 (+0530) Subject: VMware: Relocate volume to compliant datastore X-Git-Url: https://review.fuel-infra.org/gitweb?a=commitdiff_plain;h=4be8913520f5e9fe4109ade101da9509e4a83360;p=openstack-build%2Fcinder-build.git VMware: Relocate volume to compliant datastore During attach to a nova instance, the backing VM corresponding to the volume is relocated only if the nova instance's ESX host cannot access the backing's current datastore. The storage profile is ignored and the volume's virtual disk might end up in a non-compliant datastore. This patch fixes the problem by checking storage profile compliance of the current datastore. Change-Id: I3865654e219c05dcec3aaab07c4cee0658fe181e Closes-Bug: #1301348 --- diff --git a/cinder/tests/test_vmware_vmdk.py b/cinder/tests/test_vmware_vmdk.py index b00868ffc..b1d25d48b 100644 --- a/cinder/tests/test_vmware_vmdk.py +++ b/cinder/tests/test_vmware_vmdk.py @@ -375,30 +375,97 @@ class VMwareEsxVmdkDriverTestCase(test.TestCase): m.UnsetStubs() m.VerifyAll() - def test_init_conn_with_instance_and_backing(self): - """Test initialize_connection with instance and backing.""" - m = self.mox - m.StubOutWithMock(self._driver.__class__, 'volumeops') - self._driver.volumeops = self._volumeops - m.StubOutWithMock(self._volumeops, 'get_backing') - volume = FakeObject() - volume['name'] = 'volume_name' - volume['id'] = 'volume_id' - volume['size'] = 1 - connector = {'instance': 'my_instance'} - backing = FakeMor('VirtualMachine', 'my_back') - self._volumeops.get_backing(volume['name']).AndReturn(backing) - m.StubOutWithMock(self._volumeops, 'get_host') - host = FakeMor('HostSystem', 'my_host') - self._volumeops.get_host(mox.IgnoreArg()).AndReturn(host) + @mock.patch.object(VMDK_DRIVER, 'volumeops') + @mock.patch.object(VMDK_DRIVER, '_relocate_backing') + @mock.patch.object(VMDK_DRIVER, '_create_backing') + def test_initialize_connection_with_instance_and_backing( + self, create_backing, relocate_backing, vops): + self._test_initialize_connection_with_instance_and_backing( + create_backing, relocate_backing, vops) + + def _test_initialize_connection_with_instance_and_backing( + self, create_backing, relocate_backing, vops): + instance = mock.sentinel.instance + connector = {'instance': instance} + + backing = mock.Mock(value=mock.sentinel.backing_value) + vops.get_backing.return_value = backing - m.ReplayAll() + host = mock.sentinel.host + vops.get_host.return_value = host + + volume = {'name': 'vol-1', 'id': 1} conn_info = self._driver.initialize_connection(volume, connector) - self.assertEqual(conn_info['driver_volume_type'], 'vmdk') - self.assertEqual(conn_info['data']['volume'], 'my_back') - self.assertEqual(conn_info['data']['volume_id'], 'volume_id') - m.UnsetStubs() - m.VerifyAll() + + relocate_backing.assert_called_once_with(volume, backing, host) + self.assertFalse(create_backing.called) + + self.assertEqual('vmdk', conn_info['driver_volume_type']) + self.assertEqual(backing.value, conn_info['data']['volume']) + self.assertEqual(volume['id'], + conn_info['data']['volume_id']) + + @mock.patch.object(VMDK_DRIVER, 'volumeops') + @mock.patch.object(VMDK_DRIVER, '_relocate_backing') + @mock.patch.object(VMDK_DRIVER, '_create_backing') + def test_initialize_connection_with_instance_and_no_backing( + self, create_backing, relocate_backing, vops): + self._test_initialize_connection_with_instance_and_no_backing( + create_backing, relocate_backing, vops) + + def _test_initialize_connection_with_instance_and_no_backing( + self, create_backing, relocate_backing, vops): + instance = mock.sentinel.instance + connector = {'instance': instance} + + vops.get_backing.return_value = None + + host = mock.sentinel.host + vops.get_host.return_value = host + + backing = mock.Mock(value=mock.sentinel.backing_value) + create_backing.return_value = backing + + volume = {'name': 'vol-1', 'id': 1} + conn_info = self._driver.initialize_connection(volume, connector) + + create_backing.assert_called_once_with(volume, host) + self.assertFalse(relocate_backing.called) + + self.assertEqual('vmdk', conn_info['driver_volume_type']) + self.assertEqual(backing.value, conn_info['data']['volume']) + self.assertEqual(volume['id'], + conn_info['data']['volume_id']) + + @mock.patch.object(VMDK_DRIVER, 'volumeops') + @mock.patch.object(VMDK_DRIVER, '_relocate_backing') + @mock.patch.object(VMDK_DRIVER, '_create_backing_in_inventory') + def test_initialize_connection_with_no_instance_and_no_backing( + self, create_backing_in_inventory, relocate_backing, vops): + self._test_initialize_connection_with_no_instance_and_no_backing( + create_backing_in_inventory, relocate_backing, vops) + + def _test_initialize_connection_with_no_instance_and_no_backing( + self, create_backing_in_inventory, relocate_backing, vops): + vops.get_backing.return_value = None + + host = mock.sentinel.host + vops.get_host.return_value = host + + backing = mock.Mock(value=mock.sentinel.backing_value) + create_backing_in_inventory.return_value = backing + + connector = {} + volume = {'name': 'vol-1', 'id': 1} + conn_info = self._driver.initialize_connection(volume, connector) + + create_backing_in_inventory.assert_called_once_with(volume) + self.assertFalse(relocate_backing.called) + + self.assertEqual('vmdk', conn_info['driver_volume_type']) + self.assertEqual(backing.value, conn_info['data']['volume']) + self.assertEqual(volume['id'], + conn_info['data']['volume_id']) def test_get_volume_group_folder(self): """Test _get_volume_group_folder.""" @@ -529,71 +596,6 @@ class VMwareEsxVmdkDriverTestCase(test.TestCase): vmdk.VMwareEsxVmdkDriver._get_disk_type, volume) - def test_init_conn_with_instance_no_backing(self): - """Test initialize_connection with instance and without backing.""" - m = self.mox - m.StubOutWithMock(self._driver.__class__, 'volumeops') - self._driver.volumeops = self._volumeops - m.StubOutWithMock(self._volumeops, 'get_backing') - volume = FakeObject() - volume['name'] = 'volume_name' - volume['id'] = 'volume_id' - volume['size'] = 1 - volume['volume_type_id'] = None - connector = {'instance': 'my_instance'} - self._volumeops.get_backing(volume['name']) - m.StubOutWithMock(self._volumeops, 'get_host') - host = FakeMor('HostSystem', 'my_host') - self._volumeops.get_host(mox.IgnoreArg()).AndReturn(host) - m.StubOutWithMock(self._volumeops, 'get_dss_rp') - resource_pool = FakeMor('ResourcePool', 'my_rp') - datastores = [FakeMor('Datastore', 'my_ds')] - self._volumeops.get_dss_rp(host).AndReturn((datastores, resource_pool)) - m.StubOutWithMock(self._driver, '_get_folder_ds_summary') - folder = FakeMor('Folder', 'my_fol') - summary = FakeDatastoreSummary(1, 1) - self._driver._get_folder_ds_summary(volume, resource_pool, - datastores).AndReturn((folder, - summary)) - backing = FakeMor('VirtualMachine', 'my_back') - m.StubOutWithMock(self._volumeops, 'create_backing') - self._volumeops.create_backing(volume['name'], - volume['size'] * units.Mi, - mox.IgnoreArg(), folder, - resource_pool, host, - mox.IgnoreArg(), - mox.IgnoreArg(), - mox.IgnoreArg()).AndReturn(backing) - - m.ReplayAll() - conn_info = self._driver.initialize_connection(volume, connector) - self.assertEqual(conn_info['driver_volume_type'], 'vmdk') - self.assertEqual(conn_info['data']['volume'], 'my_back') - self.assertEqual(conn_info['data']['volume_id'], 'volume_id') - m.UnsetStubs() - m.VerifyAll() - - def test_init_conn_without_instance(self): - """Test initialize_connection without instance and a backing.""" - m = self.mox - m.StubOutWithMock(self._driver.__class__, 'volumeops') - self._driver.volumeops = self._volumeops - m.StubOutWithMock(self._volumeops, 'get_backing') - backing = FakeMor('VirtualMachine', 'my_back') - volume = FakeObject() - volume['name'] = 'volume_name' - volume['id'] = 'volume_id' - connector = {} - self._volumeops.get_backing(volume['name']).AndReturn(backing) - - m.ReplayAll() - conn_info = self._driver.initialize_connection(volume, connector) - self.assertEqual(conn_info['driver_volume_type'], 'vmdk') - self.assertEqual(conn_info['data']['volume'], 'my_back') - self.assertEqual(conn_info['data']['volume_id'], 'volume_id') - m.UnsetStubs() - m.VerifyAll() - def test_create_snapshot_without_backing(self): """Test vmdk.create_snapshot without backing.""" m = self.mox @@ -2003,37 +2005,29 @@ class VMwareVcVmdkDriverTestCase(VMwareEsxVmdkDriverTestCase): self._test_create_backing_by_copying(volumeops, create_backing, extend_virtual_disk) - def test_init_conn_with_instance_and_backing(self): - """Test initialize_connection with instance and backing.""" - m = self.mox - m.StubOutWithMock(self._driver.__class__, 'volumeops') - self._driver.volumeops = self._volumeops - m.StubOutWithMock(self._volumeops, 'get_backing') - volume = FakeObject() - volume['name'] = 'volume_name' - volume['id'] = 'volume_id' - volume['size'] = 1 - connector = {'instance': 'my_instance'} - backing = FakeMor('VirtualMachine', 'my_back') - self._volumeops.get_backing(volume['name']).AndReturn(backing) - m.StubOutWithMock(self._volumeops, 'get_host') - host = FakeMor('HostSystem', 'my_host') - self._volumeops.get_host(mox.IgnoreArg()).AndReturn(host) - datastore = FakeMor('Datastore', 'my_ds') - resource_pool = FakeMor('ResourcePool', 'my_rp') - m.StubOutWithMock(self._volumeops, 'get_dss_rp') - self._volumeops.get_dss_rp(host).AndReturn(([datastore], - resource_pool)) - m.StubOutWithMock(self._volumeops, 'get_datastore') - self._volumeops.get_datastore(backing).AndReturn(datastore) + @mock.patch.object(VMDK_DRIVER, 'volumeops') + @mock.patch.object(VMDK_DRIVER, '_relocate_backing') + @mock.patch.object(VMDK_DRIVER, '_create_backing') + def test_initialize_connection_with_instance_and_backing( + self, create_backing, relocate_backing, vops): + self._test_initialize_connection_with_instance_and_backing( + create_backing, relocate_backing, vops) - m.ReplayAll() - conn_info = self._driver.initialize_connection(volume, connector) - self.assertEqual(conn_info['driver_volume_type'], 'vmdk') - self.assertEqual(conn_info['data']['volume'], 'my_back') - self.assertEqual(conn_info['data']['volume_id'], 'volume_id') - m.UnsetStubs() - m.VerifyAll() + @mock.patch.object(VMDK_DRIVER, 'volumeops') + @mock.patch.object(VMDK_DRIVER, '_relocate_backing') + @mock.patch.object(VMDK_DRIVER, '_create_backing') + def test_initialize_connection_with_instance_and_no_backing( + self, create_backing, relocate_backing, vops): + self._test_initialize_connection_with_instance_and_no_backing( + create_backing, relocate_backing, vops) + + @mock.patch.object(VMDK_DRIVER, 'volumeops') + @mock.patch.object(VMDK_DRIVER, '_relocate_backing') + @mock.patch.object(VMDK_DRIVER, '_create_backing_in_inventory') + def test_initialize_connection_with_no_instance_and_no_backing( + self, create_backing_in_inventory, relocate_backing, vops): + self._test_initialize_connection_with_no_instance_and_no_backing( + create_backing_in_inventory, relocate_backing, vops) def test_get_volume_group_folder(self): """Test _get_volume_group_folder.""" @@ -2052,50 +2046,6 @@ class VMwareVcVmdkDriverTestCase(VMwareEsxVmdkDriverTestCase): m.UnsetStubs() m.VerifyAll() - def test_init_conn_with_instance_and_backing_and_relocation(self): - """Test initialize_connection with backing being relocated.""" - m = self.mox - m.StubOutWithMock(self._driver.__class__, 'volumeops') - self._driver.volumeops = self._volumeops - m.StubOutWithMock(self._volumeops, 'get_backing') - volume = FakeObject() - volume['name'] = 'volume_name' - volume['id'] = 'volume_id' - volume['size'] = 1 - connector = {'instance': 'my_instance'} - backing = FakeMor('VirtualMachine', 'my_back') - self._volumeops.get_backing(volume['name']).AndReturn(backing) - m.StubOutWithMock(self._volumeops, 'get_host') - host = FakeMor('HostSystem', 'my_host') - self._volumeops.get_host(mox.IgnoreArg()).AndReturn(host) - datastore1 = FakeMor('Datastore', 'my_ds_1') - datastore2 = FakeMor('Datastore', 'my_ds_2') - resource_pool = FakeMor('ResourcePool', 'my_rp') - m.StubOutWithMock(self._volumeops, 'get_dss_rp') - self._volumeops.get_dss_rp(host).AndReturn(([datastore1], - resource_pool)) - m.StubOutWithMock(self._volumeops, 'get_datastore') - self._volumeops.get_datastore(backing).AndReturn(datastore2) - m.StubOutWithMock(self._driver, '_get_folder_ds_summary') - folder = FakeMor('Folder', 'my_fol') - summary = FakeDatastoreSummary(1, 1, datastore1) - self._driver._get_folder_ds_summary(volume, resource_pool, - [datastore1]).AndReturn((folder, - summary)) - m.StubOutWithMock(self._volumeops, 'relocate_backing') - self._volumeops.relocate_backing(backing, datastore1, - resource_pool, host) - m.StubOutWithMock(self._volumeops, 'move_backing_to_folder') - self._volumeops.move_backing_to_folder(backing, folder) - - m.ReplayAll() - conn_info = self._driver.initialize_connection(volume, connector) - self.assertEqual(conn_info['driver_volume_type'], 'vmdk') - self.assertEqual(conn_info['data']['volume'], 'my_back') - self.assertEqual(conn_info['data']['volume_id'], 'volume_id') - m.UnsetStubs() - m.VerifyAll() - @mock.patch.object(VMDK_DRIVER, '_extend_vmdk_virtual_disk') @mock.patch.object(VMDK_DRIVER, 'volumeops') def test_clone_backing_linked(self, volume_ops, _extend_vmdk_virtual_disk): @@ -2691,6 +2641,86 @@ class VMwareVcVmdkDriverTestCase(VMwareEsxVmdkDriverTestCase): close.assert_called_once_with(fd) delete_if_exists.assert_called_once_with(tmp) + @mock.patch.object(VMDK_DRIVER, 'volumeops') + @mock.patch.object(VMDK_DRIVER, 'ds_sel') + def test_relocate_backing_nop(self, ds_sel, vops): + volume = {'name': 'vol-1', 'size': 1} + + datastore = mock.sentinel.datastore + vops.get_datastore.return_value = datastore + + profile = mock.sentinel.profile + vops.get_profile.return_value = profile + + vops.is_datastore_accessible.return_value = True + ds_sel.is_datastore_compliant.return_value = True + + backing = mock.sentinel.backing + host = mock.sentinel.host + self._driver._relocate_backing(volume, backing, host) + + vops.is_datastore_accessible.assert_called_once_with(datastore, host) + ds_sel.is_datastore_compliant.assert_called_once_with(datastore, + profile) + self.assertFalse(vops.relocate_backing.called) + + @mock.patch.object(VMDK_DRIVER, 'volumeops') + @mock.patch.object(VMDK_DRIVER, 'ds_sel') + def test_relocate_backing_with_no_datastore( + self, ds_sel, vops): + volume = {'name': 'vol-1', 'size': 1} + + profile = mock.sentinel.profile + vops.get_profile.return_value = profile + + vops.is_datastore_accessible.return_value = True + ds_sel.is_datastore_compliant.return_value = False + + ds_sel.select_datastore.return_value = [] + + backing = mock.sentinel.backing + host = mock.sentinel.host + + self.assertRaises(error_util.NoValidDatastoreException, + self._driver._relocate_backing, + volume, + backing, + host) + ds_sel.select_datastore.assert_called_once_with( + {hub.DatastoreSelector.SIZE_BYTES: volume['size'] * units.Gi, + hub.DatastoreSelector.PROFILE_NAME: profile}, hosts=[host]) + self.assertFalse(vops.relocate_backing.called) + + @mock.patch.object(VMDK_DRIVER, 'volumeops') + @mock.patch.object(VMDK_DRIVER, '_get_volume_group_folder') + @mock.patch.object(VMDK_DRIVER, 'ds_sel') + def test_relocate_backing( + self, ds_sel, get_volume_group_folder, vops): + volume = {'name': 'vol-1', 'size': 1} + + vops.is_datastore_accessible.return_value = False + ds_sel.is_datastore_compliant.return_value = True + + backing = mock.sentinel.backing + host = mock.sentinel.host + + rp = mock.sentinel.rp + datastore = mock.sentinel.datastore + summary = mock.Mock(datastore=datastore) + ds_sel.select_datastore.return_value = (host, rp, summary) + + folder = mock.sentinel.folder + get_volume_group_folder.return_value = folder + + self._driver._relocate_backing(volume, backing, host) + + vops.relocate_backing.assert_called_once_with(backing, + datastore, + rp, + host) + vops.move_backing_to_folder.assert_called_once_with(backing, + folder) + class ImageDiskTypeTest(test.TestCase): """Unit tests for ImageDiskType.""" diff --git a/cinder/tests/test_vmware_volumeops.py b/cinder/tests/test_vmware_volumeops.py index 0817fb865..3ace853ca 100644 --- a/cinder/tests/test_vmware_volumeops.py +++ b/cinder/tests/test_vmware_volumeops.py @@ -237,6 +237,30 @@ class VolumeOpsTestCase(test.TestCase): hosts = self.vops.get_connected_hosts(datastore) self.assertEqual([], hosts) + @mock.patch('cinder.volume.drivers.vmware.volumeops.VMwareVolumeOps.' + 'get_connected_hosts') + def test_is_datastore_accessible(self, get_connected_hosts): + host_1 = mock.Mock(value=mock.sentinel.host_1) + host_2 = mock.Mock(value=mock.sentinel.host_2) + get_connected_hosts.return_value = [host_1, host_2] + + ds = mock.sentinel.datastore + host = mock.Mock(value=mock.sentinel.host_1) + self.assertTrue(self.vops.is_datastore_accessible(ds, host)) + get_connected_hosts.assert_called_once_with(ds) + + @mock.patch('cinder.volume.drivers.vmware.volumeops.VMwareVolumeOps.' + 'get_connected_hosts') + def test_is_datastore_accessible_with_inaccessible(self, + get_connected_hosts): + host_1 = mock.Mock(value=mock.sentinel.host_1) + get_connected_hosts.return_value = [host_1] + + ds = mock.sentinel.datastore + host = mock.Mock(value=mock.sentinel.host_2) + self.assertFalse(self.vops.is_datastore_accessible(ds, host)) + get_connected_hosts.assert_called_once_with(ds) + def test_is_valid(self): with mock.patch.object(self.vops, 'get_summary') as get_summary: summary = mock.Mock(spec=object) @@ -1229,6 +1253,52 @@ class VolumeOpsTestCase(test.TestCase): datacenter=dc_ref) self.session.wait_for_task.assert_called_once_with(task) + def test_get_profile(self): + server_obj = mock.Mock() + self.session.pbm.client.factory.create.return_value = server_obj + + profile_ids = [mock.sentinel.profile_id] + profile_name = mock.sentinel.profile_name + profile = mock.Mock() + profile.name = profile_name + self.session.invoke_api.side_effect = [profile_ids, [profile]] + + value = mock.sentinel.value + backing = mock.Mock(value=value) + self.assertEqual(profile_name, self.vops.get_profile(backing)) + + pbm = self.session.pbm + profile_manager = pbm.service_content.profileManager + exp_calls = [mock.call(pbm, 'PbmQueryAssociatedProfile', + profile_manager, entity=server_obj), + mock.call(pbm, 'PbmRetrieveContent', profile_manager, + profileIds=profile_ids)] + self.assertEqual(exp_calls, self.session.invoke_api.call_args_list) + + self.assertEqual(value, server_obj.key) + self.assertEqual('virtualMachine', server_obj.objectType) + self.session.invoke_api.side_effect = None + + def test_get_profile_with_no_profile(self): + server_obj = mock.Mock() + self.session.pbm.client.factory.create.return_value = server_obj + + self.session.invoke_api.side_effect = [[]] + + value = mock.sentinel.value + backing = mock.Mock(value=value) + self.assertIsNone(self.vops.get_profile(backing)) + + pbm = self.session.pbm + profile_manager = pbm.service_content.profileManager + exp_calls = [mock.call(pbm, 'PbmQueryAssociatedProfile', + profile_manager, entity=server_obj)] + self.assertEqual(exp_calls, self.session.invoke_api.call_args_list) + + self.assertEqual(value, server_obj.key) + self.assertEqual('virtualMachine', server_obj.objectType) + self.session.invoke_api.side_effect = None + def test_extend_virtual_disk(self): """Test volumeops.extend_virtual_disk.""" task = mock.sentinel.task diff --git a/cinder/volume/drivers/vmware/error_util.py b/cinder/volume/drivers/vmware/error_util.py index 5696bc700..1790d0197 100644 --- a/cinder/volume/drivers/vmware/error_util.py +++ b/cinder/volume/drivers/vmware/error_util.py @@ -93,3 +93,8 @@ class VirtualDiskNotFoundException(VMwareDriverException): class ProfileNotFoundException(VMwareDriverException): """Thrown when the given storage profile cannot be found.""" message = _("Storage profile: %(storage_profile)s not found.") + + +class NoValidDatastoreException(VMwareDriverException): + """Thrown when there are no valid datastores.""" + message = _("There are no valid datastores.") diff --git a/cinder/volume/drivers/vmware/vmdk.py b/cinder/volume/drivers/vmware/vmdk.py index b8f48ec51..c95056721 100644 --- a/cinder/volume/drivers/vmware/vmdk.py +++ b/cinder/volume/drivers/vmware/vmdk.py @@ -1916,38 +1916,52 @@ class VMwareVcVmdkDriver(VMwareEsxVmdkDriver): return self.volumeops.create_folder(vm_folder, volume_folder) def _relocate_backing(self, volume, backing, host): - """Relocate volume backing under host and move to volume_group folder. + """Relocate volume backing to a datastore accessible to the given host. - If the volume backing is on a datastore that is visible to the host, - then need not do any operation. + The backing is not relocated if the current datastore is already + accessible to the host and compliant with the backing's storage + profile. - :param volume: volume to be relocated + :param volume: Volume to be relocated :param backing: Reference to the backing :param host: Reference to the host """ - # Check if volume's datastore is visible to host managing - # the instance - (datastores, resource_pool) = self.volumeops.get_dss_rp(host) + # Check if current datastore is visible to host managing + # the instance and compliant with the storage profile. datastore = self.volumeops.get_datastore(backing) - - visible_to_host = False - for _datastore in datastores: - if _datastore.value == datastore.value: - visible_to_host = True - break - if visible_to_host: + backing_profile = self.volumeops.get_profile(backing) + if (self.volumeops.is_datastore_accessible(datastore, host) and + self.ds_sel.is_datastore_compliant(datastore, + backing_profile)): + LOG.debug("Datastore: %(datastore)s of backing: %(backing)s is " + "already accessible to instance's host: %(host)s and " + "compliant with storage profile: %(profile)s.", + {'backing': backing, + 'datastore': datastore, + 'host': host, + 'profile': backing_profile}) return - # The volume's backing is on a datastore that is not visible to the - # host managing the instance. We relocate the volume's backing. + # We need to relocate the backing to an accessible and profile + # compliant datastore. + req = {} + req[hub.DatastoreSelector.SIZE_BYTES] = (volume['size'] * + units.Gi) + req[hub.DatastoreSelector.PROFILE_NAME] = backing_profile + + # Select datastore satisfying the requirements. + best_candidate = self.ds_sel.select_datastore(req, hosts=[host]) + if not best_candidate: + # No candidate datastore to relocate. + msg = _("There are no datastores matching volume requirements;" + " can't relocate volume: %s.") % volume['name'] + LOG.error(msg) + raise error_util.NoValidDatastoreException(msg) + + (host, resource_pool, summary) = best_candidate + dc = self.volumeops.get_dc(resource_pool) + folder = self._get_volume_group_folder(dc) - # Pick a folder and datastore to relocate volume backing to - (folder, summary) = self._get_folder_ds_summary(volume, - resource_pool, - datastores) - LOG.info(_("Relocating volume: %(backing)s to %(ds)s and %(rp)s.") % - {'backing': backing, 'ds': summary, 'rp': resource_pool}) - # Relocate the backing to the datastore and folder self.volumeops.relocate_backing(backing, summary.datastore, resource_pool, host) self.volumeops.move_backing_to_folder(backing, folder) diff --git a/cinder/volume/drivers/vmware/volumeops.py b/cinder/volume/drivers/vmware/volumeops.py index 4fb4a3b12..307b2f33a 100644 --- a/cinder/volume/drivers/vmware/volumeops.py +++ b/cinder/volume/drivers/vmware/volumeops.py @@ -386,6 +386,15 @@ class VMwareVolumeOps(object): return connected_hosts + def is_datastore_accessible(self, datastore, host): + """Check if the datastore is accessible to the given host. + + :param datastore: datastore reference + :return: True if the datastore is accessible + """ + hosts = self.get_connected_hosts(datastore) + return host.value in [host_ref.value for host_ref in hosts] + def _in_maintenance(self, summary): """Check if a datastore is entering maintenance or in maintenance. @@ -1379,3 +1388,27 @@ class VMwareVolumeOps(object): profile=profile_id) LOG.debug("Filtered hubs: %s", filtered_hubs) return filtered_hubs + + def get_profile(self, backing): + """Query storage profile associated with the given backing. + + :param backing: backing reference + :return: profile name + """ + pbm = self._session.pbm + profile_manager = pbm.service_content.profileManager + + object_ref = pbm.client.factory.create('ns0:PbmServerObjectRef') + object_ref.key = backing.value + object_ref.objectType = 'virtualMachine' + + profile_ids = self._session.invoke_api(pbm, + 'PbmQueryAssociatedProfile', + profile_manager, + entity=object_ref) + if profile_ids: + profiles = self._session.invoke_api(pbm, + 'PbmRetrieveContent', + profile_manager, + profileIds=profile_ids) + return profiles[0].name