VMware: refactor get_datastore_ref_and_name

Update vm_util.get_datastore_ref_and_name to use Datastore class,
renamed it to get_datastore, remove vm_util.DSRecord along the way.

partial blueprint vmware-spawn-refactor

Change-Id: Ib3308870c09344036c4f330a48513581f5771246
This commit is contained in:
Vui Lam 2014-06-02 02:43:46 -07:00 committed by Matthew Booth
parent f6aa3174d1
commit 9bb9b45e55
8 changed files with 107 additions and 87 deletions

View File

@ -15,6 +15,7 @@
import contextlib
import mock
from nova.openstack.common import units
from nova import test
from nova.virt.vmwareapi import ds_util
from nova.virt.vmwareapi import error_util
@ -169,9 +170,23 @@ class DsUtilTestCase(test.NoDBTestCase):
class DatastoreTestCase(test.NoDBTestCase):
def test_ds(self):
ds = ds_util.Datastore("fake_ref", "ds_name")
ds = ds_util.Datastore(
"fake_ref", "ds_name", 2 * units.Gi, 1 * units.Gi)
self.assertEqual('ds_name', ds.name)
self.assertEqual('fake_ref', ds.ref)
self.assertEqual(2 * units.Gi, ds.capacity)
self.assertEqual(1 * units.Gi, ds.freespace)
def test_ds_invalid_space(self):
self.assertRaises(ValueError, ds_util.Datastore,
"fake_ref", "ds_name", 1 * units.Gi, 2 * units.Gi)
self.assertRaises(ValueError, ds_util.Datastore,
"fake_ref", "ds_name", None, 2 * units.Gi)
def test_ds_no_capacity_no_freespace(self):
ds = ds_util.Datastore("fake_ref", "ds_name")
self.assertIsNone(ds.capacity)
self.assertIsNone(ds.freespace)
def test_ds_invalid(self):
self.assertRaises(ValueError, ds_util.Datastore, None, "ds_name")

View File

@ -65,26 +65,26 @@ class VMwareVMUtilTestCase(test.NoDBTestCase):
super(VMwareVMUtilTestCase, self).tearDown()
fake.reset()
def test_get_datastore_ref_and_name(self):
def test_get_datastore(self):
fake_objects = fake.FakeRetrieveResult()
fake_objects.add_object(fake.Datastore())
result = vm_util.get_datastore_ref_and_name(
result = vm_util.get_datastore(
fake_session(fake_objects))
self.assertEqual(result[1], "fake-ds")
self.assertEqual(result[2], units.Ti)
self.assertEqual(result[3], 500 * units.Gi)
self.assertEqual("fake-ds", result.name)
self.assertEqual(units.Ti, result.capacity)
self.assertEqual(500 * units.Gi, result.freespace)
def test_get_datastore_ref_and_name_with_regex(self):
def test_get_datastore_with_regex(self):
# Test with a regex that matches with a datastore
datastore_valid_regex = re.compile("^openstack.*\d$")
fake_objects = fake.FakeRetrieveResult()
fake_objects.add_object(fake.Datastore("openstack-ds0"))
fake_objects.add_object(fake.Datastore("fake-ds0"))
fake_objects.add_object(fake.Datastore("fake-ds1"))
result = vm_util.get_datastore_ref_and_name(
result = vm_util.get_datastore(
fake_session(fake_objects), None, None, datastore_valid_regex)
self.assertEqual("openstack-ds0", result[1])
self.assertEqual("openstack-ds0", result.name)
def _test_get_stats_from_cluster(self, connection_state="connected",
maintenance_mode=False):
@ -165,7 +165,7 @@ class VMwareVMUtilTestCase(test.NoDBTestCase):
def test_get_stats_from_cluster_hosts_connected_and_maintenance(self):
self._test_get_stats_from_cluster(maintenance_mode=True)
def test_get_datastore_ref_and_name_with_token(self):
def test_get_datastore_with_token(self):
regex = re.compile("^ds.*\d$")
fake0 = fake.FakeRetrieveResult()
fake0.add_object(fake.Datastore("ds0", 10 * units.Gi, 5 * units.Gi))
@ -174,22 +174,22 @@ class VMwareVMUtilTestCase(test.NoDBTestCase):
fake1 = fake.FakeRetrieveResult()
fake1.add_object(fake.Datastore("ds2", 10 * units.Gi, 8 * units.Gi))
fake1.add_object(fake.Datastore("ds3", 10 * units.Gi, 1 * units.Gi))
result = vm_util.get_datastore_ref_and_name(
result = vm_util.get_datastore(
fake_session(fake0, fake1), None, None, regex)
self.assertEqual("ds2", result[1])
self.assertEqual("ds2", result.name)
def test_get_datastore_ref_and_name_with_list(self):
def test_get_datastore_with_list(self):
# Test with a regex containing whitelist of datastores
datastore_valid_regex = re.compile("(openstack-ds0|openstack-ds2)")
fake_objects = fake.FakeRetrieveResult()
fake_objects.add_object(fake.Datastore("openstack-ds0"))
fake_objects.add_object(fake.Datastore("openstack-ds1"))
fake_objects.add_object(fake.Datastore("openstack-ds2"))
result = vm_util.get_datastore_ref_and_name(
result = vm_util.get_datastore(
fake_session(fake_objects), None, None, datastore_valid_regex)
self.assertNotEqual("openstack-ds1", result[1])
self.assertNotEqual("openstack-ds1", result.name)
def test_get_datastore_ref_and_name_with_regex_error(self):
def test_get_datastore_with_regex_error(self):
# Test with a regex that has no match
# Checks if code raises DatastoreNotFound with a specific message
datastore_invalid_regex = re.compile("unknown-ds")
@ -201,7 +201,7 @@ class VMwareVMUtilTestCase(test.NoDBTestCase):
# assertRaisesRegExp would have been a good choice instead of
# try/catch block, but it's available only from Py 2.7.
try:
vm_util.get_datastore_ref_and_name(
vm_util.get_datastore(
fake_session(fake_objects), None, None,
datastore_invalid_regex)
except exception.DatastoreNotFound as e:
@ -210,14 +210,14 @@ class VMwareVMUtilTestCase(test.NoDBTestCase):
self.fail("DatastoreNotFound Exception was not raised with "
"message: %s" % exp_message)
def test_get_datastore_ref_and_name_without_datastore(self):
def test_get_datastore_without_datastore(self):
self.assertRaises(exception.DatastoreNotFound,
vm_util.get_datastore_ref_and_name,
vm_util.get_datastore,
fake_session(None), host="fake-host")
self.assertRaises(exception.DatastoreNotFound,
vm_util.get_datastore_ref_and_name,
vm_util.get_datastore,
fake_session(None), cluster="fake-cluster")
def test_get_host_ref_from_id(self):
@ -241,9 +241,9 @@ class VMwareVMUtilTestCase(test.NoDBTestCase):
vm_util.get_host_ref,
fake_session(""), 'fake_cluster')
def test_get_datastore_ref_and_name_no_host_in_cluster(self):
def test_get_datastore_no_host_in_cluster(self):
self.assertRaises(exception.DatastoreNotFound,
vm_util.get_datastore_ref_and_name,
vm_util.get_datastore,
fake_session(""), 'fake_cluster')
@mock.patch.object(vm_util, '_get_vm_ref_from_vm_uuid',
@ -320,7 +320,7 @@ class VMwareVMUtilTestCase(test.NoDBTestCase):
self.assertIsNotNone(prop4)
self.assertEqual('bar1', prop4.val)
def test_get_datastore_ref_and_name_inaccessible_ds(self):
def test_get_datastore_inaccessible_ds(self):
data_store = fake.Datastore()
data_store.set("summary.accessible", False)
@ -328,7 +328,7 @@ class VMwareVMUtilTestCase(test.NoDBTestCase):
fake_objects.add_object(data_store)
self.assertRaises(exception.DatastoreNotFound,
vm_util.get_datastore_ref_and_name,
vm_util.get_datastore,
fake_session(fake_objects))
def test_get_resize_spec(self):

View File

@ -16,6 +16,7 @@ import re
from nova.openstack.common import units
from nova import test
from nova.virt.vmwareapi import ds_util
from nova.virt.vmwareapi import vm_util
ResultSet = collections.namedtuple('ResultSet', ['objects'])
@ -60,22 +61,22 @@ class VMwareVMUtilDatastoreSelectionTestCase(test.NoDBTestCase):
def test_filter_datastores_simple(self):
datastores = self.build_result_set(self.data)
best_match = vm_util.DSRecord(datastore=None, name=None,
capacity=None, freespace=0)
best_match = ds_util.Datastore(ref='fake_ref', name='ds',
capacity=0, freespace=0)
rec = vm_util._select_datastore(datastores, best_match)
self.assertIsNotNone(rec[0], "could not find datastore!")
self.assertEqual('ds-001', rec[0].value,
self.assertIsNotNone(rec.ref, "could not find datastore!")
self.assertEqual('ds-001', rec.ref.value,
"didn't find the right datastore!")
self.assertEqual(123467890, rec[3],
self.assertEqual(123467890, rec.freespace,
"did not obtain correct freespace!")
def test_filter_datastores_empty(self):
data = []
datastores = self.build_result_set(data)
best_match = vm_util.DSRecord(datastore=None, name=None,
capacity=None, freespace=0)
best_match = ds_util.Datastore(ref='fake_ref', name='ds',
capacity=0, freespace=0)
rec = vm_util._select_datastore(datastores, best_match)
self.assertEqual(rec, best_match)
@ -84,8 +85,8 @@ class VMwareVMUtilDatastoreSelectionTestCase(test.NoDBTestCase):
datastores = self.build_result_set(self.data)
datastore_regex = re.compile('no_match.*')
best_match = vm_util.DSRecord(datastore=None, name=None,
capacity=None, freespace=0)
best_match = ds_util.Datastore(ref='fake_ref', name='ds',
capacity=0, freespace=0)
rec = vm_util._select_datastore(datastores,
best_match,
datastore_regex)
@ -105,21 +106,21 @@ class VMwareVMUtilDatastoreSelectionTestCase(test.NoDBTestCase):
datastores = self.build_result_set(data)
datastore_regex = re.compile('.*-good$')
best_match = vm_util.DSRecord(datastore=None, name=None,
capacity=None, freespace=0)
best_match = ds_util.Datastore(ref='fake_ref', name='ds',
capacity=0, freespace=0)
rec = vm_util._select_datastore(datastores,
best_match,
datastore_regex)
self.assertIsNotNone(rec, "could not find datastore!")
self.assertEqual('ds-003', rec[0].value,
self.assertEqual('ds-003', rec.ref.value,
"didn't find the right datastore!")
self.assertNotEqual('ds-004', rec[0].value,
self.assertNotEqual('ds-004', rec.ref.value,
"accepted an unreachable datastore!")
self.assertEqual('some-name-good', rec[1])
self.assertEqual(12346789, rec[3],
self.assertEqual('some-name-good', rec.name)
self.assertEqual(12346789, rec.freespace,
"did not obtain correct freespace!")
self.assertEqual(987654321, rec[2],
self.assertEqual(987654321, rec.capacity,
"did not obtain correct capacity!")
def test_filter_datastores_missing_props(self):
@ -131,8 +132,8 @@ class VMwareVMUtilDatastoreSelectionTestCase(test.NoDBTestCase):
prop_names = ['summary.type', 'summary.name',
'summary.capacity', 'summary.freeSpace']
datastores = self.build_result_set(data, prop_names)
best_match = vm_util.DSRecord(datastore=None, name=None,
capacity=None, freespace=0)
best_match = ds_util.Datastore(ref='fake_ref', name='ds',
capacity=0, freespace=0)
rec = vm_util._select_datastore(datastores, best_match)
self.assertEqual(rec, best_match, "no matches were expected")
@ -150,7 +151,7 @@ class VMwareVMUtilDatastoreSelectionTestCase(test.NoDBTestCase):
datastore_regex = re.compile('.*-good$')
# the current best match is better than all candidates
best_match = vm_util.DSRecord(datastore='ds-100', name='best-ds-good',
best_match = ds_util.Datastore(ref='ds-100', name='best-ds-good',
capacity=20 * units.Gi, freespace=19 * units.Gi)
rec = vm_util._select_datastore(datastores,
best_match,

View File

@ -67,9 +67,8 @@ class VMwareVMOpsTestCase(test.NoDBTestCase):
self._context, **values)
fake_ds_ref = vmwareapi_fake.ManagedObjectReference('fake-ds')
self._ds_record = vm_util.DSRecord(
datastore=fake_ds_ref, name='fake_ds',
self._ds = ds_util.Datastore(
ref=fake_ds_ref, name='fake_ds',
capacity=10 * units.Gi,
freespace=10 * units.Gi)
self._dc_info = vmops.DcInfo(
@ -571,7 +570,7 @@ class VMwareVMOpsTestCase(test.NoDBTestCase):
recorded_methods = [c[1][1] for c in mock_call_method.mock_calls]
self.assertEqual(expected_methods, recorded_methods)
@mock.patch('nova.virt.vmwareapi.vm_util.get_datastore_ref_and_name')
@mock.patch('nova.virt.vmwareapi.vm_util.get_datastore')
@mock.patch(
'nova.virt.vmwareapi.vmops.VMwareVCVMOps.get_datacenter_ref_and_name')
@mock.patch('nova.virt.vmwareapi.vm_util.get_mo_id_from_instance',
@ -608,7 +607,7 @@ class VMwareVMOpsTestCase(test.NoDBTestCase):
mock_get_res_pool_ref,
mock_get_mo_id_for_instance,
mock_get_datacenter_ref_and_name,
mock_get_datastore_ref_and_name,
mock_get_datastore,
block_device_info=None,
power_on=True):
@ -619,7 +618,7 @@ class VMwareVMOpsTestCase(test.NoDBTestCase):
'size': 1 * units.Gi,
}
network_info = mock.Mock()
mock_get_datastore_ref_and_name.return_value = self._ds_record
mock_get_datastore.return_value = self._ds
mock_get_datacenter_ref_and_name.return_value = self._dc_info
mock_call_method = mock.Mock(return_value='fake_task')
@ -679,7 +678,7 @@ class VMwareVMOpsTestCase(test.NoDBTestCase):
mock_attach = self._vmops._volumeops.attach_root_volume
mock_attach.assert_called_once_with(
root_disk['connection_info'], self._instance, 'vda',
self._ds_record.datastore)
self._ds.ref)
self.assertFalse(_wait_for_task.called)
self.assertFalse(_fetch_image.called)
self.assertFalse(_call_method.called)
@ -691,7 +690,7 @@ class VMwareVMOpsTestCase(test.NoDBTestCase):
self._instance,
self._session._host_ip,
self._dc_info.name,
self._ds_record.name,
self._ds.name,
upload_file_name,
cookies='Fake-CookieJar')
self.assertTrue(len(_wait_for_task.mock_calls) > 0)

View File

@ -26,19 +26,28 @@ LOG = logging.getLogger(__name__)
class Datastore(object):
def __init__(self, ref, name):
def __init__(self, ref, name, capacity=None, freespace=None):
"""Datastore object holds ref and name together for convenience.
:param ref: a vSphere reference to a datastore
:param name: vSphere unique name for this datastore
:param capacity: (optional) capacity in bytes of this datastore
:param freespace: (optional) free space in bytes of datastore
"""
if name is None:
raise ValueError(_("Datastore name cannot be None"))
if ref is None:
raise ValueError(_("Datastore reference cannot be None"))
if freespace is not None and capacity is None:
raise ValueError(_("Invalid capacity"))
if capacity is not None and freespace is not None:
if capacity < freespace:
raise ValueError(_("Capacity is smaller than free space"))
self._ref = ref
self._name = name
self._capacity = capacity
self._freespace = freespace
@property
def ref(self):
@ -48,6 +57,14 @@ class Datastore(object):
def name(self):
return self._name
@property
def capacity(self):
return self._capacity
@property
def freespace(self):
return self._freespace
def build_path(self, *paths):
"""Constructs and returns a DatastorePath.

View File

@ -78,6 +78,14 @@ class Host(object):
pass
def _get_ds_capacity_and_freespace(session, cluster=None):
try:
ds = vm_util.get_datastore(session, cluster)
return ds.capacity, ds.freespace
except exception.DatastoreNotFound:
return 0, 0
class HostState(object):
"""Manages information about the ESX host this compute
node is running on.
@ -110,10 +118,7 @@ class HostState(object):
if summary is None:
return
try:
ds = vm_util.get_datastore_ref_and_name(self._session)
except exception.DatastoreNotFound:
ds = (None, None, 0, 0)
capacity, freespace = _get_ds_capacity_and_freespace(self._session)
data = {}
data["vcpus"] = summary.hardware.numCpuThreads
@ -124,8 +129,8 @@ class HostState(object):
"sockets": summary.hardware.numCpuPkgs,
"threads": summary.hardware.numCpuThreads}
}
data["disk_total"] = ds[2] / units.Gi
data["disk_available"] = ds[3] / units.Gi
data["disk_total"] = capacity / units.Gi
data["disk_available"] = freespace / units.Gi
data["disk_used"] = data["disk_total"] - data["disk_available"]
data["host_memory_total"] = summary.hardware.memorySize / units.Mi
data["host_memory_free"] = data["host_memory_total"] - \
@ -163,12 +168,8 @@ class VCState(object):
def update_status(self):
"""Update the current state of the cluster."""
# Get the datastore in the cluster
try:
ds = vm_util.get_datastore_ref_and_name(self._session,
self._cluster)
except exception.DatastoreNotFound:
ds = (None, None, 0, 0)
capacity, freespace = _get_ds_capacity_and_freespace(self._session,
self._cluster)
# Get cpu, memory stats from the cluster
stats = vm_util.get_stats_from_cluster(self._session, self._cluster)
@ -179,8 +180,8 @@ class VCState(object):
"model": stats['cpu']['model'],
"topology": {"cores": stats['cpu']['cores'],
"threads": stats['cpu']['vcpus']}}
data["disk_total"] = ds[2] / units.Gi
data["disk_available"] = ds[3] / units.Gi
data["disk_total"] = capacity / units.Gi
data["disk_available"] = freespace / units.Gi
data["disk_used"] = data["disk_total"] - data["disk_available"]
data["host_memory_total"] = stats['mem']['total']
data["host_memory_free"] = stats['mem']['free']

View File

@ -18,7 +18,6 @@
The VMware API VM utility module to build SOAP object specs.
"""
import collections
import copy
import functools
@ -40,8 +39,6 @@ LOG = logging.getLogger(__name__)
ALL_SUPPORTED_NETWORK_DEVICES = ['VirtualE1000', 'VirtualE1000e',
'VirtualPCNet32', 'VirtualSriovEthernetCard',
'VirtualVmxnet']
DSRecord = collections.namedtuple(
'DSRecord', ['datastore', 'name', 'capacity', 'freespace'])
# A cache for VM references. The key will be the VM name
# and the value is the VM reference. The VM name is unique. This
@ -1032,29 +1029,20 @@ def _select_datastore(data_stores, best_match, datastore_regex=None):
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 = DSRecord(
datastore=obj_content.obj,
new_ds = ds_util.Datastore(
ref=obj_content.obj,
name=ds_name,
capacity=propdict['summary.capacity'],
freespace=propdict['summary.freeSpace'])
# favor datastores with more free space
if new_ds.freespace > best_match.freespace:
if (best_match is None or
new_ds.freespace > best_match.freespace):
best_match = new_ds
return best_match
def get_datastore(session, cluster=None, host=None, datastore_regex=None):
# TODO(hartsocks): refactor get_datastore_ref_and_name and Datastore object
# such that this does not need to exist and the Datastore object holds the
# correct values pulled in by dynamic properties calls.
# Look to move to ds_util module.
ds = get_datastore_ref_and_name(session, cluster, host, datastore_regex)
return ds_util.Datastore(ds[0], ds[1])
def get_datastore_ref_and_name(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",
@ -1082,8 +1070,7 @@ def get_datastore_ref_and_name(session, cluster=None, host=None,
["summary.type", "summary.name",
"summary.capacity", "summary.freeSpace",
"summary.accessible"])
best_match = DSRecord(datastore=None, name=None,
capacity=None, freespace=0)
best_match = None
while data_stores:
best_match = _select_datastore(data_stores, best_match,
datastore_regex)
@ -1093,7 +1080,7 @@ def get_datastore_ref_and_name(session, cluster=None, host=None,
data_stores = session._call_method(vim_util,
"continue_to_get_objects",
token)
if best_match.datastore:
if best_match:
return best_match
if datastore_regex:
raise exception.DatastoreNotFound(

View File

@ -1113,9 +1113,9 @@ class VMwareVMOps(object):
step=2,
total_steps=RESIZE_TOTAL_STEPS)
ds_ref = vm_util.get_datastore_ref_and_name(
ds_ref = vm_util.get_datastore(
self._session, self._cluster, host_ref,
datastore_regex=self._datastore_regex)[0]
datastore_regex=self._datastore_regex).ref
dc_info = self.get_datacenter_ref_and_name(ds_ref)
# 3. Clone the VM for instance
vm_util.clone_vmref_for_instance(self._session, instance, vm_ref,