VMware: Multiple cluster support using single compute service

To allow a single VC driver to model multiple clusters in vCenter
as multiple nova-compute nodes. The VC driver will be configured
to represent a set of clusters as compute nodes.
For example to specify two clusters named cluster-A and cluster-B,
the nova.conf should have:
    cluster_name = cluster-A
    cluster_name = cluster-B

DocImpact
Change-Id: Ia5464948cc30b9b744450f9c301c4f3afaff717b
Blueprint: multiple-clusters-managed-by-one-service
This commit is contained in:
kirankv
2013-05-23 07:57:40 -07:00
committed by Gary Kotton
parent 2c7a994ab3
commit 9408d79e74
7 changed files with 468 additions and 76 deletions

View File

@@ -3228,7 +3228,7 @@
#host_password=<None>
# Name of a VMware Cluster ComputeResource. Used only if
# compute_driver is vmwareapi.VMwareVCDriver. (string value)
# compute_driver is vmwareapi.VMwareVCDriver. (multi valued)
#cluster_name=<None>
# Regex to match the name of a datastore. Used only if

View File

@@ -35,7 +35,9 @@ class ConfigDriveTestCase(test.TestCase):
def setUp(self):
super(ConfigDriveTestCase, self).setUp()
self.context = context.RequestContext('fake', 'fake', is_admin=False)
self.flags(host_ip='test_url',
cluster_name = 'test_cluster'
self.flags(cluster_name=[cluster_name],
host_ip='test_url',
host_username='test_username',
host_password='test_pass',
use_linked_clone=False, group='vmware')
@@ -50,6 +52,8 @@ class ConfigDriveTestCase(test.TestCase):
'disk_format': 'vhd',
'size': 512,
}
self.node_name = '%s(%s)' % (self.conn.dict_mors.keys()[0],
cluster_name)
self.test_instance = {'node': 'test_url',
'vm_state': 'building',
'project_id': 'fake',
@@ -70,7 +74,8 @@ class ConfigDriveTestCase(test.TestCase):
'scheduling',
'reservation_id': 'r-3t8muvr0',
'id': 1,
'uuid': 'fake-uuid'}
'uuid': 'fake-uuid',
'node': self.node_name}
class FakeInstanceMetadata(object):
def __init__(self, instance, content=None, extra_md=None):

View File

@@ -121,7 +121,7 @@ class VMwareAPIVMTestCase(test.TestCase):
vmwareapi_fake.reset()
db_fakes.stub_out_db_instance_api(self.stubs)
stubs.set_stubs(self.stubs)
self.conn = driver.VMwareVCDriver(fake.FakeVirtAPI)
self.conn = driver.VMwareESXDriver(fake.FakeVirtAPI)
# NOTE(vish): none of the network plugging code is actually
# being tested
self.network_info = utils.get_test_network_info()
@@ -138,7 +138,9 @@ class VMwareAPIVMTestCase(test.TestCase):
vmwareapi_fake.cleanup()
nova.tests.image.fake.FakeImageService_reset()
def _create_instance_in_the_db(self):
def _create_instance_in_the_db(self, node=None):
if not node:
node = self.node_name
values = {'name': '1',
'id': 1,
'uuid': "fake-uuid",
@@ -149,27 +151,29 @@ class VMwareAPIVMTestCase(test.TestCase):
'ramdisk_id': "1",
'mac_address': "de:ad:be:ef:be:ef",
'instance_type': 'm1.large',
'node': self.node_name,
'node': node,
}
self.instance = db.instance_create(None, values)
def _create_vm(self):
def _create_vm(self, node=None, num_instances=1):
"""Create and spawn the VM."""
self._create_instance_in_the_db()
if not node:
node = self.node_name
self._create_instance_in_the_db(node=node)
self.type_data = db.flavor_get_by_name(None, 'm1.large')
self.conn.spawn(self.context, self.instance, self.image,
injected_files=[], admin_password=None,
network_info=self.network_info,
block_device_info=None)
self._check_vm_record()
self._check_vm_record(num_instances=num_instances)
def _check_vm_record(self):
def _check_vm_record(self, num_instances=1):
"""
Check if the spawned VM's properties correspond to the instance in
the db.
"""
instances = self.conn.list_instances()
self.assertEquals(len(instances), 1)
self.assertEquals(len(instances), num_instances)
# Get Nova record for VM
vm_info = self.conn.get_info({'uuid': 'fake-uuid',
@@ -473,9 +477,7 @@ class VMwareAPIVMTestCase(test.TestCase):
def _test_finish_migration(self, power_on):
"""
Tests the finish_migration method on vmops via the
VMwareVCDriver. Results are checked against whether or not
the underlying instance should have been powered on.
Tests the finish_migration method on vmops
"""
self.power_on_called = False
@@ -504,22 +506,21 @@ class VMwareAPIVMTestCase(test.TestCase):
disk_info=None,
network_info=None,
block_device_info=None,
resize_instance=False,
image_meta=None,
power_on=power_on)
# verify the results
self.assertEquals(power_on, self.power_on_called)
def test_finish_migration_power_on(self):
self._test_finish_migration(power_on=True)
self.assertRaises(NotImplementedError,
self._test_finish_migration, power_on=True)
def test_finish_migration_power_off(self):
self._test_finish_migration(power_on=False)
self.assertRaises(NotImplementedError,
self._test_finish_migration, power_on=False)
def _test_finish_revert_migration(self, power_on):
"""
Tests the finish_revert_migration method on vmops via the
VMwareVCDriver. Results are checked against whether or not
the underlying instance should have been powered on.
Tests the finish_revert_migration method on vmops
"""
# setup the test instance in the database
@@ -564,14 +565,14 @@ class VMwareAPIVMTestCase(test.TestCase):
self.conn.finish_revert_migration(instance=self.instance,
network_info=None,
power_on=power_on)
# verify the results
self.assertEquals(power_on, self.power_on_called)
def test_finish_revert_migration_power_on(self):
self._test_finish_revert_migration(power_on=True)
self.assertRaises(NotImplementedError,
self._test_finish_migration, power_on=True)
def test_finish_revert_migration_power_off(self):
self._test_finish_revert_migration(power_on=False)
self.assertRaises(NotImplementedError,
self._test_finish_migration, power_on=False)
def test_diagnostics_non_existent_vm(self):
self._create_instance_in_the_db()
@@ -596,7 +597,7 @@ class VMwareAPIVMTestCase(test.TestCase):
fake_vm = vmwareapi_fake._get_objects("VirtualMachine").objects[0]
fake_vm_id = int(fake_vm.obj.value.replace('vm-', ''))
vnc_dict = self.conn.get_vnc_console(self.instance)
self.assertEquals(vnc_dict['host'], "ha-host")
self.assertEquals(vnc_dict['host'], 'test_url')
self.assertEquals(vnc_dict['port'], cfg.CONF.vmware.vnc_port +
fake_vm_id % cfg.CONF.vmware.vnc_port_total)
@@ -804,10 +805,18 @@ class VMwareAPIVCDriverTestCase(VMwareAPIVMTestCase):
def setUp(self):
super(VMwareAPIVCDriverTestCase, self).setUp()
self.flags(cluster_name='test_cluster',
cluster_name = 'test_cluster'
cluster_name2 = 'test_cluster2'
self.flags(cluster_name=[cluster_name, cluster_name2],
task_poll_interval=10, datastore_regex='.*', group='vmware')
self.flags(vnc_enabled=False)
self.conn = driver.VMwareVCDriver(None, False)
node = self.conn._resources.keys()[0]
self.node_name = '%s(%s)' % (node,
self.conn._resources[node]['name'])
node = self.conn._resources.keys()[1]
self.node_name2 = '%s(%s)' % (node,
self.conn._resources[node]['name'])
def tearDown(self):
super(VMwareAPIVCDriverTestCase, self).tearDown()
@@ -822,13 +831,51 @@ class VMwareAPIVCDriverTestCase(VMwareAPIVMTestCase):
self.assertEquals(stats['memory_mb_used'], 1024 - 524)
self.assertEquals(stats['hypervisor_type'], 'VMware ESXi')
self.assertEquals(stats['hypervisor_version'], '5.0.0')
self.assertEquals(stats['hypervisor_hostname'], 'test_url')
self.assertEquals(stats['hypervisor_hostname'], self.node_name)
self.assertEquals(stats['supported_instances'],
'[["i686", "vmware", "hvm"], ["x86_64", "vmware", "hvm"]]')
def test_invalid_datastore_regex(self):
# Tests if we raise an exception for Invalid Regular Expression in
# vmware_datastore_regex
self.flags(cluster_name='test_cluster', datastore_regex='fake-ds(01',
self.flags(cluster_name=['test_cluster'], datastore_regex='fake-ds(01',
group='vmware')
self.assertRaises(exception.InvalidInput, driver.VMwareVCDriver, None)
def test_get_available_nodes(self):
nodelist = self.conn.get_available_nodes()
self.assertEquals(nodelist, [self.node_name, self.node_name2])
def test_spawn_multiple_node(self):
self._create_vm(node=self.node_name, num_instances=1)
info = self.conn.get_info({'uuid': 'fake-uuid'})
self._check_vm_info(info, power_state.RUNNING)
self._create_vm(node=self.node_name2, num_instances=2)
info = self.conn.get_info({'uuid': 'fake-uuid'})
self._check_vm_info(info, power_state.RUNNING)
def test_finish_migration_power_on(self):
self._test_finish_migration(power_on=True)
self.assertEquals(True, self.power_on_called)
def test_finish_migration_power_off(self):
self._test_finish_migration(power_on=False)
self.assertEquals(False, self.power_on_called)
def test_finish_revert_migration_power_on(self):
self._test_finish_revert_migration(power_on=True)
self.assertEquals(True, self.power_on_called)
def test_finish_revert_migration_power_off(self):
self._test_finish_revert_migration(power_on=False)
self.assertEquals(False, self.power_on_called)
def test_get_vnc_console(self):
self._create_instance_in_the_db()
self._create_vm()
fake_vm = vmwareapi_fake._get_objects("VirtualMachine").objects[0]
fake_vm_id = int(fake_vm.obj.value.replace('vm-', ''))
vnc_dict = self.conn.get_vnc_console(self.instance)
self.assertEquals(vnc_dict['host'], "ha-host")
self.assertEquals(vnc_dict['port'], cfg.CONF.vmware.vnc_port +
fake_vm_id % cfg.CONF.vmware.vnc_port_total)

View File

@@ -64,12 +64,11 @@ vmwareapi_opts = [
'Used only if compute_driver is '
'vmwareapi.VMwareESXDriver or vmwareapi.VMwareVCDriver.',
secret=True),
cfg.StrOpt('cluster_name',
cfg.MultiStrOpt('cluster_name',
deprecated_name='vmwareapi_cluster_name',
deprecated_group='DEFAULT',
help='Name of a VMware Cluster ComputeResource. '
'Used only if compute_driver is '
'vmwareapi.VMwareVCDriver.'),
help='Name of a VMware Cluster ComputeResource. Used only if '
'compute_driver is vmwareapi.VMwareVCDriver.'),
cfg.StrOpt('datastore_regex',
help='Regex to match the name of a datastore. '
'Used only if compute_driver is '
@@ -302,19 +301,8 @@ class VMwareESXDriver(driver.ComputeDriver):
'username': CONF.vmware.host_username,
'password': CONF.vmware.host_password}
def get_available_resource(self, nodename):
"""Retrieve resource information.
This method is called when nova-compute launches, and
as part of a periodic task that records the results in the DB.
:returns: dictionary describing resources
"""
host_stats = self.get_host_stats(refresh=True)
# Updating host information
dic = {'vcpus': host_stats["vcpus"],
def _get_available_resources(self, host_stats):
return {'vcpus': host_stats['vcpus'],
'memory_mb': host_stats['host_memory_total'],
'local_gb': host_stats['disk_total'],
'vcpus_used': 0,
@@ -329,7 +317,19 @@ class VMwareESXDriver(driver.ComputeDriver):
host_stats['supported_instances']),
}
return dic
def get_available_resource(self, nodename):
"""Retrieve resource information.
This method is called when nova-compute launches, and
as part of a periodic task that records the results in the DB.
:returns: dictionary describing resources
"""
host_stats = self.get_host_stats(refresh=True)
# Updating host information
return self._get_available_resources(host_stats)
def update_host_status(self):
"""Update the status info of the host, and return those values
@@ -384,15 +384,24 @@ class VMwareVCDriver(VMwareESXDriver):
def __init__(self, virtapi, read_only=False, scheme="https"):
super(VMwareVCDriver, self).__init__(virtapi)
self._cluster_name = CONF.vmware.cluster_name
if not self._cluster_name:
self._cluster = None
else:
self._cluster = vm_util.get_cluster_ref_from_name(
self._session, self._cluster_name)
if self._cluster is None:
raise exception.NotFound(_("VMware Cluster %s is not found")
% self._cluster_name)
# Get the list of clusters to be used
self._cluster_names = CONF.vmware.cluster_name
self.dict_mors = vm_util.get_all_cluster_refs_by_name(self._session,
self._cluster_names)
if not self.dict_mors:
raise exception.NotFound(_("All clusters specified %s were not"
" found in the vCenter")
% self._cluster_names)
# Check if there are any clusters that were specified in the nova.conf
# but are not in the vCenter, for missing clusters log a warning.
clusters_found = [v.get('name') for k, v in self.dict_mors.iteritems()]
missing_clusters = set(self._cluster_names) - set(clusters_found)
if missing_clusters:
LOG.warn(_("The following clusters could not be found in the"
" vCenter %s") % list(missing_clusters))
self._datastore_regex = None
if CONF.vmware.datastore_regex:
try:
@@ -401,21 +410,18 @@ class VMwareVCDriver(VMwareESXDriver):
raise exception.InvalidInput(reason=
_("Invalid Regular Expression %s")
% CONF.vmware.datastore_regex)
self._volumeops = volumeops.VMwareVolumeOps(self._session,
cluster=self._cluster,
vc_support=True)
self._vmops = vmops.VMwareVMOps(self._session, self.virtapi,
self._volumeops, self._cluster,
self._datastore_regex)
self._vc_state = None
# The _resources is used to maintain the vmops, volumeops and vcstate
# objects per cluster
self._resources = {}
self._virtapi = virtapi
self._update_resources()
@property
def host_state(self):
if not self._vc_state:
self._vc_state = host.VCState(self._session,
self._host_ip,
self._cluster)
return self._vc_state
# The following initialization is necessary since the base class does
# not use VC state.
first_cluster = self._resources.keys()[0]
self._vmops = self._resources.get(first_cluster).get('vmops')
self._volumeops = self._resources.get(first_cluster).get('volumeops')
self._vc_state = self._resources.get(first_cluster).get('vcstate')
def migrate_disk_and_power_off(self, context, instance, dest,
instance_type, network_info,
@@ -461,6 +467,161 @@ class VMwareVCDriver(VMwareESXDriver):
# itself. You must talk to the VNC host underneath vCenter.
return self._vmops.get_vnc_console_vcenter(instance)
def _update_resources(self):
"""This method creates a dictionary of VMOps, VolumeOps and VCState.
The VMwareVMOps, VMwareVolumeOps and VCState object is for each
cluster/rp. The dictionary is of the form
{
domain-1000 : {'vmops': vmops_obj,
'volumeops': volumeops_obj,
'vcstate': vcstate_obj,
'name': MyCluster},
resgroup-1000 : {'vmops': vmops_obj,
'volumeops': volumeops_obj,
'vcstate': vcstate_obj,
'name': MyRP},
}
"""
# TODO(kirankv) we can avoid creating multiple vmops and volumeops
# if we make them utility class so that cluster is passed as a
# parameter to the method
added_nodes = set(self.dict_mors.keys()) - set(self._resources.keys())
for node in added_nodes:
_volumeops = volumeops.VMwareVolumeOps(self._session,
self.dict_mors[node]['cluster_mor'],
vc_support=True)
_vmops = vmops.VMwareVMOps(self._session, self._virtapi,
_volumeops,
self.dict_mors[node]['cluster_mor'])
name = self.dict_mors.get(node)['name']
_vc_state = host.VCState(self._session,
self._create_nodename(node, name),
self.dict_mors.get(node)['cluster_mor'])
self._resources[node] = {'vmops': _vmops,
'volumeops': _volumeops,
'vcstate': _vc_state,
'name': name,
}
deleted_nodes = (set(self._resources.keys()) -
set(self.dict_mors.keys()))
for node in deleted_nodes:
LOG.debug(_("Removing node %s since its removed from"
" nova.conf") % node)
del self._resources[node]
def _create_nodename(self, mo_id, display_name):
"""Creates the name that is stored in hypervisor_hostname column.
The name will be of the form similar to
domain-1000(MyCluster)
resgroup-1000(MyResourcePool)
"""
return mo_id + '(' + display_name + ')'
def _get_mo_id(self, nodename):
return nodename.partition('(')[0]
def _get_vmops_for_compute_node(self, nodename):
"""Retrieve vmops object from mo_id stored in the node name.
Node name is of the form domain-1000(MyCluster)
"""
return self._resources.get(self._get_mo_id(nodename)).get('vmops')
def _get_volumeops_for_compute_node(self, nodename):
"""Retrieve vmops object from mo_id stored in the node name.
Node name is of the form domain-1000(MyCluster)
"""
return self._resources.get(self._get_mo_id(nodename)).get('volumeops')
def _get_vc_state_for_compute_node(self, nodename):
"""Retrieve VCState object from mo_id stored in the node name.
Node name is of the form domain-1000(MyCluster)
"""
return self._resources.get(self._get_mo_id(nodename)).get('vcstate')
def get_available_resource(self, nodename):
"""Retrieve resource info.
This method is called when nova-compute launches, and
as part of a periodic task.
:returns: dictionary describing resources
"""
stats_dict = {}
vc_state = self._get_vc_state_for_compute_node(nodename)
if vc_state:
host_stats = vc_state.get_host_stats(refresh=True)
# Updating host information
stats_dict = self._get_available_resources(host_stats)
else:
LOG.info(_("Invalid cluster or resource pool"
" name : %s") % nodename)
return stats_dict
def get_available_nodes(self):
"""Returns nodenames of all nodes managed by the compute service.
This method is for multi compute-nodes support. If a driver supports
multi compute-nodes, this method returns a list of nodenames managed
by the service. Otherwise, this method should return
[hypervisor_hostname].
"""
self.dict_mors = vm_util.get_all_cluster_refs_by_name(
self._session,
CONF.vmware.cluster_name)
nodes = self.dict_mors.keys()
node_list = []
self._update_resources()
for node in self.dict_mors.keys():
nodename = self._create_nodename(node,
self.dict_mors.get(node)['name'])
node_list.append(nodename)
LOG.debug(_("The available nodes are: %s") % node_list)
return node_list
def get_host_stats(self, refresh=True):
"""Return currently known host stats."""
stats_list = []
nodes = self.get_available_nodes()
for node in nodes:
stats_list.append(self.get_available_resource(node))
return stats_list
def spawn(self, context, instance, image_meta, injected_files,
admin_password, network_info=None, block_device_info=None):
"""Create VM instance."""
_vmops = self._get_vmops_for_compute_node(instance['node'])
_vmops.spawn(context, instance, image_meta, injected_files,
admin_password, network_info, block_device_info)
def attach_volume(self, connection_info, instance, mountpoint):
"""Attach volume storage to VM instance."""
_volumeops = self._get_volumeops_for_compute_node(instance['node'])
return _volumeops.attach_volume(connection_info,
instance,
mountpoint)
def detach_volume(self, connection_info, instance, mountpoint):
"""Detach volume storage to VM instance."""
_volumeops = self._get_volumeops_for_compute_node(instance['node'])
return _volumeops.detach_volume(connection_info,
instance,
mountpoint)
def get_volume_connector(self, instance):
"""Return volume connector information."""
_volumeops = self._get_volumeops_for_compute_node(instance['node'])
return _volumeops.get_volume_connector(instance)
class VMwareAPISession(object):
"""

View File

@@ -63,7 +63,8 @@ def reset():
create_datacenter()
create_datastore()
create_res_pool()
create_cluster()
create_cluster('test_cluster')
create_cluster('test_cluster2')
def cleanup():
@@ -350,9 +351,50 @@ class Network(ManagedObject):
class ResourcePool(ManagedObject):
"""Resource Pool class."""
def __init__(self, name="test-rpool"):
def __init__(self, name="test-rpool", value="resgroup-test"):
super(ResourcePool, self).__init__("rp")
self.set("name", name)
self.set("name", "test_ResPool")
summary = DataObject()
runtime = DataObject()
config = DataObject()
memory = DataObject()
cpu = DataObject()
memoryAllocation = DataObject()
cpuAllocation = DataObject()
memory.maxUsage = 1000 * 1024 * 1024
memory.overallUsage = 500 * 1024 * 1024
cpu.maxUsage = 10000
cpu.overallUsage = 1000
runtime.cpu = cpu
runtime.memory = memory
summary.runtime = runtime
cpuAllocation.limit = 10000
memoryAllocation.limit = 1024
memoryAllocation.reservation = 1024
config.memoryAllocation = memoryAllocation
config.cpuAllocation = cpuAllocation
self.set("summary", summary)
self.set("config", config)
parent = ManagedObjectReference(value=value,
name=name)
owner = ManagedObjectReference(value=value,
name=name)
self.set("parent", parent)
self.set("owner", owner)
class DatastoreHostMount(DataObject):
def __init__(self, value='host-100'):
super(DatastoreHostMount, self).__init__()
host_ref = (_db_content["HostSystem"]
[_db_content["HostSystem"].keys()[0]].obj)
host_system = DataObject()
host_system.ManagedObjectReference = [host_ref]
host_system.value = value
self.key = host_system
class ClusterComputeResource(ManagedObject):
@@ -373,6 +415,7 @@ class ClusterComputeResource(ManagedObject):
summary.totalMemory = 0
summary.effectiveMemory = 0
self.set("summary", summary)
self.set("summary.effectiveCpu", 10000)
def _add_resource_pool(self, r_pool):
if r_pool:
@@ -477,6 +520,7 @@ class HostSystem(ManagedObject):
hardware.numCpuThreads = 16
hardware.vendor = "Intel"
hardware.cpuModel = "Intel(R) Xeon(R)"
hardware.uuid = "host-uuid"
hardware.memorySize = 1024 * 1024 * 1024
summary.hardware = hardware
@@ -497,7 +541,9 @@ class HostSystem(ManagedObject):
net_info_pnic.PhysicalNic = [pnic_do]
self.set("summary", summary)
self.set("summary.hardware", hardware)
self.set("capability.maxHostSupportedVcpus", 600)
self.set("summary.runtime.inMaintenanceMode", False)
self.set("runtime.connectionState", "connected")
self.set("config.network.pnic", net_info_pnic)
self.set("connected", connected)
@@ -623,10 +669,11 @@ def create_network():
_create_object('Network', network)
def create_cluster():
cluster = ClusterComputeResource()
def create_cluster(name):
cluster = ClusterComputeResource(name=name)
cluster._add_host(_get_object_refs("HostSystem")[0])
cluster._add_datastore(_get_object_refs("Datastore")[0])
cluster._add_resource_pool(_get_object_refs("ResourcePool")[0])
_create_object('ClusterComputeResource', cluster)

View File

@@ -1,5 +1,6 @@
# vim: tabstop=4 shiftwidth=4 softtabstop=4
# Copyright (c) 2013 Hewlett-Packard Development Company, L.P.
# Copyright (c) 2012 VMware, Inc.
# Copyright (c) 2011 Citrix Systems, Inc.
# Copyright 2011 OpenStack Foundation
@@ -23,9 +24,13 @@ import copy
from nova import exception
from nova.openstack.common.gettextutils import _
from nova.openstack.common import log as logging
from nova.virt.vmwareapi import vim_util
LOG = logging.getLogger(__name__)
def build_datastore_path(datastore_name, path):
"""Build the datastore compliant path."""
return "[%s] %s" % (datastore_name, path)
@@ -882,3 +887,128 @@ def get_vmdk_volume_disk(hardware_devices):
for device in hardware_devices:
if (device.__class__.__name__ == "VirtualDisk"):
return device
def get_res_pool_ref(session, cluster, node_mo_id):
"""Get the resource pool."""
if cluster is None:
# With no cluster named, use the root resource pool.
results = session._call_method(vim_util, "get_objects",
"ResourcePool")
_cancel_retrieve_if_necessary(session, results)
# The 0th resource pool is always the root resource pool on both ESX
# and vCenter.
res_pool_ref = results.objects[0].obj
else:
if cluster.value == node_mo_id:
# Get the root resource pool of the cluster
res_pool_ref = session._call_method(vim_util,
"get_dynamic_property",
cluster,
"ClusterComputeResource",
"resourcePool")
return res_pool_ref
def get_all_cluster_mors(session):
"""Get all the clusters in the vCenter."""
try:
results = session._call_method(vim_util, "get_objects",
"ClusterComputeResource", ["name"])
_cancel_retrieve_if_necessary(session, results)
return results.objects
except Exception as excep:
LOG.warn(_("Failed to get cluster references %s") % excep)
def get_all_res_pool_mors(session):
"""Get all the resource pools in the vCenter."""
try:
results = session._call_method(vim_util, "get_objects",
"ResourcePool")
_cancel_retrieve_if_necessary(session, results)
return results.objects
except Exception as excep:
LOG.warn(_("Failed to get resource pool references " "%s") % excep)
def get_dynamic_property_mor(session, mor_ref, attribute):
"""Get the value of an attribute for a given managed object."""
return session._call_method(vim_util, "get_dynamic_property",
mor_ref, mor_ref._type, attribute)
def find_entity_mor(entity_list, entity_name):
"""Returns managed object ref for given cluster or resource pool name."""
return [mor for mor in entity_list if mor.propSet[0].val == entity_name]
def get_all_cluster_refs_by_name(session, path_list):
"""Get reference to the Cluster, ResourcePool with the path specified.
The path is the display name. This can be the full path as well.
The input will have the list of clusters and resource pool names
"""
cls = get_all_cluster_mors(session)
res = get_all_res_pool_mors(session)
path_list = [path.strip() for path in path_list]
list_obj = []
for entity_path in path_list:
# entity_path could be unique cluster and/or resource-pool name
res_mor = find_entity_mor(res, entity_path)
cls_mor = find_entity_mor(cls, entity_path)
cls_mor.extend(res_mor)
for mor in cls_mor:
list_obj.append((mor.obj, mor.propSet[0].val))
return get_dict_mor(session, list_obj)
def get_dict_mor(session, list_obj):
"""The input is a list of objects in the form
(manage_object,display_name)
The managed object will be in the form
{ value = "domain-1002", _type = "ClusterComputeResource" }
Output data format:
dict_mors = {
'respool-1001': { 'cluster_mor': clusterMor,
'res_pool_mor': resourcePoolMor,
'name': display_name },
'domain-1002': { 'cluster_mor': clusterMor,
'res_pool_mor': resourcePoolMor,
'name': display_name },
}
"""
dict_mors = {}
for obj_ref, path in list_obj:
if obj_ref._type == "ResourcePool":
# Get owner cluster-ref mor
cluster_ref = get_dynamic_property_mor(session, obj_ref, "owner")
dict_mors[obj_ref.value] = {'cluster_mor': cluster_ref,
'res_pool_mor': obj_ref,
'name': path,
}
else:
# Get default resource pool of the cluster
res_pool_ref = get_dynamic_property_mor(session,
obj_ref, "resourcePool")
dict_mors[obj_ref.value] = {'cluster_mor': obj_ref,
'res_pool_mor': res_pool_ref,
'name': path,
}
return dict_mors
def get_mo_id_from_instance(instance):
"""Return the managed object ID from the instance.
The instance['node'] will have the hypervisor_hostname field of the
compute node on which the instance exists or will be provisioned.
This will be of the form
'respool-1001(MyResPoolName)'
'domain-1001(MyClusterName)'
"""
return instance['node'].partition('(')[0]

View File

@@ -180,7 +180,9 @@ class VMwareVMOps(object):
disk_type, vif_model) = _get_image_properties()
vm_folder_ref = self._get_vmfolder_ref()
res_pool_ref = self._get_res_pool_ref()
node_mo_id = vm_util.get_mo_id_from_instance(instance)
res_pool_ref = vm_util.get_res_pool_ref(self._session,
self._cluster, node_mo_id)
def _get_vif_infos():
vif_infos = []