VMware: improve instance names on VC

Up until now the instance name of the VM on the VC was the UUID of
the instance. This is very confusing for administrators and they
would like some context for the VM.

This will now be:
 <display-name> (<instance-uuid>)

Once the the instance is created we will perform a rename. The reason
for this is that the disks are all stored under a folder on the datastore
that has the name of the instance UUID.

NOTE: this is only for the display on the VC. The search for the VM's
      is done via the instanceUUID configured on the VM. If that fails
      then the nvp.uuid extra spec is used for the search.

This completed the blueprint vmware-better-display-names

Closes-bug: #1330873

Change-Id: Ibeb000f4c1d88fa296390143b0cf98e05e735086
This commit is contained in:
Gary Kotton 2015-03-22 06:57:09 -07:00
parent 23907ba774
commit 47e3a4e961
6 changed files with 109 additions and 29 deletions

View File

@ -1325,6 +1325,13 @@ class FakeVim(object):
task_mdo = create_task(method, "success")
return task_mdo.obj
def _rename(self, method, *args, **kwargs):
vm_ref = args[0]
vm_mdo = _get_vm_mdo(vm_ref)
vm_mdo.set('name', kwargs['newName'])
task_mdo = create_task(method, "success")
return task_mdo.obj
def _create_copy_disk(self, method, vmdk_file_path):
"""Creates/copies a vmdk file object in the datastore."""
# We need to add/create both .vmdk and .-flat.vmdk files
@ -1589,6 +1596,9 @@ class FakeVim(object):
elif attr_name == "ReconfigVM_Task":
return lambda *args, **kwargs: self._reconfig_vm(attr_name,
*args, **kwargs)
elif attr_name == "Rename_Task":
return lambda *args, **kwargs: self._rename(attr_name,
*args, **kwargs)
elif attr_name == "CreateVirtualDisk_Task":
return lambda *args, **kwargs: self._create_copy_disk(attr_name,
kwargs.get("name"))

View File

@ -177,6 +177,7 @@ class VMwareAPIVMTestCase(test.NoDBTestCase):
self._set_exception_vars()
self.node_name = self.conn._nodename
self.ds = 'ds1'
self._display_name = 'fake-display-name'
self.vim = vmwareapi_fake.FakeVim()
@ -301,6 +302,7 @@ class VMwareAPIVMTestCase(test.NoDBTestCase):
if ephemeral is not None:
self.type_data['ephemeral_gb'] = ephemeral
values = {'name': 'fake_name',
'display_name': self._display_name,
'id': 1,
'uuid': uuid,
'project_id': self.project_id,
@ -348,7 +350,8 @@ class VMwareAPIVMTestCase(test.NoDBTestCase):
# Get record for VM
vms = vmwareapi_fake._get_objects("VirtualMachine")
for vm in vms.objects:
if vm.get('name') == self.uuid:
if vm.get('name') == vm_util._get_vm_name(self._display_name,
self.uuid):
return vm
self.fail('Unable to find VM backing!')
@ -439,11 +442,6 @@ class VMwareAPIVMTestCase(test.NoDBTestCase):
uuids = self.conn.list_instance_uuids()
self.assertEqual(1, len(uuids))
def test_list_instance_uuids_invalid_uuid(self):
self._create_vm(uuid='fake_id')
uuids = self.conn.list_instance_uuids()
self.assertEqual(0, len(uuids))
def _cached_files_exist(self, exists=True):
cache = ds_obj.DatastorePath(self.ds, 'vmware_base',
self.fake_image_uuid,

View File

@ -49,6 +49,7 @@ class VMwareVMUtilTestCase(test.NoDBTestCase):
self._instance = fake_instance.fake_instance_obj(
None,
**{'id': 7, 'name': 'fake!',
'display_name': 'fake-display-name',
'uuid': uuidutils.generate_uuid(),
'vcpus': 2, 'memory_mb': 2048})
@ -1732,6 +1733,39 @@ class VMwareVMUtilTestCase(test.NoDBTestCase):
vm_util.folder_ref_cache_update(path, 'fake-ref')
self.assertEqual('fake-ref', vm_util.folder_ref_cache_get(path))
def test_get_vm_name(self):
uuid = uuidutils.generate_uuid()
expected = uuid
name = vm_util._get_vm_name(None, uuid)
self.assertEqual(expected, name)
display_name = 'fira'
expected = 'fira (%s)' % uuid
name = vm_util._get_vm_name(display_name, uuid)
self.assertEqual(expected, name)
display_name = 'X' * 255
expected = '%s (%s)' % ('X' * 41, uuid)
name = vm_util._get_vm_name(display_name, uuid)
self.assertEqual(expected, name)
self.assertEqual(len(name), 80)
@mock.patch.object(vm_util, '_get_vm_name', return_value='fake-name')
def test_rename_vm(self, mock_get_name):
session = fake.FakeSession()
with test.nested(
mock.patch.object(session, '_call_method',
return_value='fake_rename_task'),
mock.patch.object(session, '_wait_for_task')
) as (_call_method, _wait_for_task):
vm_util.rename_vm(session, 'fake-ref', self._instance)
_call_method.assert_called_once_with(mock.ANY,
'Rename_Task', 'fake-ref', newName='fake-name')
_wait_for_task.assert_called_once_with(
'fake_rename_task')
mock_get_name.assert_called_once_with(self._instance.display_name,
self._instance.uuid)
@mock.patch.object(driver.VMwareAPISession, 'vim', stubs.fake_vim_prop)
class VMwareVMUtilGetHostRefTestCase(test.NoDBTestCase):

View File

@ -81,6 +81,7 @@ class VMwareVMOpsTestCase(test.NoDBTestCase):
self._instance_values = {
'display_name': 'fake_display_name',
'name': 'fake_name',
'display_name': 'fake_display_name',
'uuid': 'fake_uuid',
'vcpus': 1,
'memory_mb': 512,
@ -203,12 +204,12 @@ class VMwareVMOpsTestCase(test.NoDBTestCase):
def test_get_valid_vms_from_retrieve_result(self, _mock_cont):
ops = vmops.VMwareVMOps(self._session, mock.Mock(), mock.Mock())
fake_objects = vmwareapi_fake.FakeRetrieveResult()
fake_objects.add_object(vmwareapi_fake.VirtualMachine(
name=uuidutils.generate_uuid()))
fake_objects.add_object(vmwareapi_fake.VirtualMachine(
name=uuidutils.generate_uuid()))
fake_objects.add_object(vmwareapi_fake.VirtualMachine(
name=uuidutils.generate_uuid()))
for x in range(0, 3):
vm = vmwareapi_fake.VirtualMachine()
vm.set('config.extraConfig["nvp.vm-uuid"]',
vmwareapi_fake.OptionValue(
value=uuidutils.generate_uuid()))
fake_objects.add_object(vm)
vms = ops._get_valid_vms_from_retrieve_result(fake_objects)
self.assertEqual(3, len(vms))
@ -217,14 +218,21 @@ class VMwareVMOpsTestCase(test.NoDBTestCase):
_mock_cont):
ops = vmops.VMwareVMOps(self._session, mock.Mock(), mock.Mock())
fake_objects = vmwareapi_fake.FakeRetrieveResult()
fake_objects.add_object(vmwareapi_fake.VirtualMachine(
name=uuidutils.generate_uuid()))
invalid_vm1 = vmwareapi_fake.VirtualMachine(
name=uuidutils.generate_uuid())
valid_vm = vmwareapi_fake.VirtualMachine()
valid_vm.set('config.extraConfig["nvp.vm-uuid"]',
vmwareapi_fake.OptionValue(
value=uuidutils.generate_uuid()))
fake_objects.add_object(valid_vm)
invalid_vm1 = vmwareapi_fake.VirtualMachine()
invalid_vm1.set('runtime.connectionState', 'orphaned')
invalid_vm2 = vmwareapi_fake.VirtualMachine(
name=uuidutils.generate_uuid())
invalid_vm1.set('config.extraConfig["nvp.vm-uuid"]',
vmwareapi_fake.OptionValue(
value=uuidutils.generate_uuid()))
invalid_vm2 = vmwareapi_fake.VirtualMachine()
invalid_vm2.set('runtime.connectionState', 'inaccessible')
invalid_vm2.set('config.extraConfig["nvp.vm-uuid"]',
vmwareapi_fake.OptionValue(
value=uuidutils.generate_uuid()))
fake_objects.add_object(invalid_vm1)
fake_objects.add_object(invalid_vm2)
vms = ops._get_valid_vms_from_retrieve_result(fake_objects)
@ -944,6 +952,7 @@ class VMwareVMOpsTestCase(test.NoDBTestCase):
'image_id': image_id,
'version': version.version_string_with_package()})
@mock.patch.object(vm_util, 'rename_vm')
@mock.patch.object(vmops.VMwareVMOps, '_create_folders',
return_value='fake_vm_folder')
@mock.patch('nova.virt.vmwareapi.vm_util.power_on_instance')
@ -963,7 +972,8 @@ class VMwareVMOpsTestCase(test.NoDBTestCase):
enlist_image, fetch_image,
use_disk_image,
power_on_instance,
create_folders):
create_folders,
rename_vm):
self._instance.flavor = self._flavor
extra_specs = get_extra_specs.return_value
connection_info1 = {'data': 'fake-data1', 'serial': 'volume-fake-id1'}
@ -1007,6 +1017,7 @@ class VMwareVMOpsTestCase(test.NoDBTestCase):
connection_info2, self._instance,
constants.DEFAULT_ADAPTER_TYPE)
@mock.patch.object(vm_util, 'rename_vm')
@mock.patch.object(vmops.VMwareVMOps, '_create_folders',
return_value='fake_vm_folder')
@mock.patch('nova.virt.vmwareapi.vm_util.power_on_instance')
@ -1020,7 +1031,8 @@ class VMwareVMOpsTestCase(test.NoDBTestCase):
get_vm_config_info,
build_virtual_machine,
power_on_instance,
create_folders):
create_folders,
rename_vm):
self._instance.image_ref = None
self._instance.flavor = self._flavor
extra_specs = get_extra_specs.return_value
@ -1294,6 +1306,8 @@ class VMwareVMOpsTestCase(test.NoDBTestCase):
if extras:
expected_methods.extend(extras)
# Last call should be renaming the instance
expected_methods.append('Rename_Task')
recorded_methods = [c[1][1] for c in mock_call_method.mock_calls]
self.assertEqual(expected_methods, recorded_methods)
@ -1570,6 +1584,7 @@ class VMwareVMOpsTestCase(test.NoDBTestCase):
'device_name': '/dev/sdb'}}
self._test_spawn(block_device_info=block_device_info)
@mock.patch.object(vm_util, 'rename_vm')
@mock.patch('nova.virt.vmwareapi.vm_util.power_on_instance')
@mock.patch.object(vmops.VMwareVMOps, '_create_and_attach_thin_disk')
@mock.patch.object(vmops.VMwareVMOps, '_use_disk_image_as_linked_clone')
@ -1589,7 +1604,8 @@ class VMwareVMOpsTestCase(test.NoDBTestCase):
fetch_image,
use_disk_image,
create_and_attach_thin_disk,
power_on_instance):
power_on_instance,
rename_vm):
self._instance.flavor = objects.Flavor(vcpus=1, memory_mb=512,
name="m1.tiny", root_gb=1,
ephemeral_gb=1, swap=512,

View File

@ -1608,3 +1608,17 @@ def folder_ref_cache_update(path, folder_ref):
def folder_ref_cache_get(path):
return _FOLDER_PATH_REF_MAPPING.get(path)
def _get_vm_name(display_name, id):
if display_name:
return '%s (%s)' % (display_name[:41], id[:36])
else:
return id[:36]
def rename_vm(session, vm_ref, instance):
vm_name = _get_vm_name(instance.display_name, instance.uuid)
rename_task = session._call_method(session.vim, "Rename_Task", vm_ref,
newName=vm_name)
session._wait_for_task(rename_task)

View File

@ -785,6 +785,11 @@ class VMwareVMOps(object):
instance, vm_ref, vi.dc_info, vi.datastore,
injected_files, admin_password, network_info)
# Rename the VM. This is done after the spec is created to ensure
# that all of the files for the instance are under the directory
# 'uuid' of the instance
vm_util.rename_vm(self._session, vm_ref, instance)
vm_util.power_on_instance(self._session, instance, vm_ref=vm_ref)
def _is_bdm_valid(self, block_device_mapping):
@ -1674,17 +1679,19 @@ class VMwareVMOps(object):
while retrieve_result:
for vm in retrieve_result.objects:
vm_name = None
vm_uuid = None
conn_state = None
for prop in vm.propSet:
if prop.name == "name":
vm_name = prop.val
elif prop.name == "runtime.connectionState":
if prop.name == "runtime.connectionState":
conn_state = prop.val
elif prop.name == 'config.extraConfig["nvp.vm-uuid"]':
vm_uuid = prop.val.value
# Ignore VM's that do not have nvp.vm-uuid defined
if not vm_uuid:
continue
# Ignoring the orphaned or inaccessible VMs
if (conn_state not in ["orphaned", "inaccessible"] and
uuidutils.is_uuid_like(vm_name)):
lst_vm_names.append(vm_name)
if conn_state not in ["orphaned", "inaccessible"]:
lst_vm_names.append(vm_uuid)
retrieve_result = self._session._call_method(vutil,
'continue_retrieval',
retrieve_result)
@ -1911,7 +1918,8 @@ class VMwareVMOps(object):
def list_instances(self):
"""Lists the VM instances that are registered with vCenter cluster."""
properties = ['name', 'runtime.connectionState']
properties = ['runtime.connectionState',
'config.extraConfig["nvp.vm-uuid"]']
LOG.debug("Getting list of instances from cluster %s",
self._cluster)
vms = []