VMware: Ensure compute_node.hypervisor_hostname is unique
The VMware driver currently identifies a hypervisor as: <mo_id>(<cluster name>) Cluster name is not unique, and mo_id is only unique in the scope of a single vCenter, so the above can produce duplicate names when managing clusters with the same name in multiple vCenters. This is especially likely to occur in lab setups. This change alters the above format to the following, which uniquely identifies a cluster globally: <mo_id>.<vCenter uuid> Closes-bug: #1329261 Co-Authored-By: Kanagaraj Manickam <kanagaraj.manickam@hp.com> Change-Id: I2f3b5d224cc653d0465598de0788116e71d1ca0d
This commit is contained in:
parent
07d3c0d6d1
commit
ce690113c6
|
@ -36,6 +36,7 @@ _CLASSES = ['Datacenter', 'Datastore', 'ResourcePool', 'VirtualMachine',
|
|||
'files', 'ClusterComputeResource', 'HostStorageSystem']
|
||||
|
||||
_FAKE_FILE_SIZE = 1024
|
||||
_FAKE_VCENTER_UUID = '497c514c-ef5e-4e7f-8d93-ec921993b93a'
|
||||
|
||||
_db_content = {}
|
||||
_array_types = {}
|
||||
|
@ -1180,6 +1181,8 @@ class FakeVim(object):
|
|||
about_info = DataObject()
|
||||
about_info.name = "VMware vCenter Server"
|
||||
about_info.version = "5.1.0"
|
||||
about_info.instanceUuid = _FAKE_VCENTER_UUID
|
||||
|
||||
service_content.about = about_info
|
||||
|
||||
self._service_content = service_content
|
||||
|
|
|
@ -222,7 +222,7 @@ class VMwareAPIVMTestCase(test.NoDBTestCase):
|
|||
self._set_exception_vars()
|
||||
self.node_name = self.conn._resources.keys()[0]
|
||||
self.node_name2 = self.conn._resources.keys()[1]
|
||||
if cluster_name2 in self.node_name2:
|
||||
if self.conn._resources[self.node_name2]['name'] == cluster_name2:
|
||||
self.ds = 'ds1'
|
||||
else:
|
||||
self.ds = 'ds2'
|
||||
|
@ -2368,3 +2368,32 @@ class VMwareAPIVMTestCase(test.NoDBTestCase):
|
|||
self.conn._update_pbm_location()
|
||||
self.assertEqual('fira', self.conn._session._pbm_wsdl_loc)
|
||||
self.assertIsNone(self.conn._session._pbm)
|
||||
|
||||
def test_nodename(self):
|
||||
test_mor = "domain-26"
|
||||
self.assertEqual("%s.%s" % (test_mor,
|
||||
vmwareapi_fake._FAKE_VCENTER_UUID),
|
||||
self.conn._create_nodename(test_mor),
|
||||
"VC driver failed to create the proper node name")
|
||||
|
||||
def test_normalize_nodename_old(self):
|
||||
test_mor = "domain-26"
|
||||
sample_cluster_names = ["Cluster1",
|
||||
"Cluster:2",
|
||||
"Cluster:3)",
|
||||
"(Cluster:4",
|
||||
"(Cluster:5)",
|
||||
"Test Cluster"]
|
||||
|
||||
for cluster_name in sample_cluster_names:
|
||||
old_format = "%s(%s)" % (test_mor, cluster_name)
|
||||
self.assertEqual(self.conn._create_nodename(test_mor),
|
||||
self.conn._normalize_nodename(old_format),
|
||||
'VC driver failed to normalize cluster name %s' %
|
||||
cluster_name)
|
||||
|
||||
def test_normalize_nodename_new(self):
|
||||
# Assert that _normalize_nodename doesn't touch the new format
|
||||
test_mor = "domain-26"
|
||||
nodename = "%s.%s" % (test_mor, vmwareapi_fake._FAKE_VCENTER_UUID)
|
||||
self.assertEqual(nodename, self.conn._normalize_nodename(nodename))
|
||||
|
|
|
@ -36,6 +36,7 @@ from nova.virt import driver
|
|||
from nova.virt.vmwareapi import constants
|
||||
from nova.virt.vmwareapi import error_util
|
||||
from nova.virt.vmwareapi import host
|
||||
from nova.virt.vmwareapi import vim_util as nova_vim_util
|
||||
from nova.virt.vmwareapi import vm_util
|
||||
from nova.virt.vmwareapi import vmops
|
||||
from nova.virt.vmwareapi import volumeops
|
||||
|
@ -111,6 +112,13 @@ class VMwareVCDriver(driver.ComputeDriver):
|
|||
"supports_recreate": False,
|
||||
}
|
||||
|
||||
# Legacy nodename is of the form: <mo id>(<cluster name>)
|
||||
# e.g. domain-26(TestCluster)
|
||||
# We assume <mo id> consists of alphanumeric, _ and -.
|
||||
# We assume cluster name is everything between the first ( and the last ).
|
||||
# We pull out <mo id> for re-use.
|
||||
LEGACY_NODENAME = re.compile('([\w-]+)\(.+\)')
|
||||
|
||||
# The vCenter driver includes API that acts on ESX hosts or groups
|
||||
# of ESX hosts in clusters or non-cluster logical-groupings.
|
||||
#
|
||||
|
@ -161,6 +169,7 @@ class VMwareVCDriver(driver.ComputeDriver):
|
|||
LOG.warning(_LW("The following clusters could not be found in the "
|
||||
"vCenter %s"), list(missing_clusters))
|
||||
|
||||
self._vcenter_uuid = self._get_vcenter_uuid()
|
||||
# The _resources is used to maintain the vmops, volumeops and vcstate
|
||||
# objects per cluster
|
||||
self._resources = {}
|
||||
|
@ -320,14 +329,16 @@ class VMwareVCDriver(driver.ComputeDriver):
|
|||
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},
|
||||
'domain-1000.497c514c-ef5e-4e7f-8d93-ec921993b93a' : {
|
||||
'vmops': vmops_obj,
|
||||
'volumeops': volumeops_obj,
|
||||
'vcstate': vcstate_obj,
|
||||
'name': MyCluster},
|
||||
'resgroup-1000.497c514c-ef5e-4e7f-8d93-ec921993b93a' : {
|
||||
'vmops': vmops_obj,
|
||||
'volumeops': volumeops_obj,
|
||||
'vcstate': vcstate_obj,
|
||||
'name': MyRP},
|
||||
}
|
||||
"""
|
||||
added_nodes = set(self.dict_mors.keys()) - set(self._resource_keys)
|
||||
|
@ -339,7 +350,7 @@ class VMwareVCDriver(driver.ComputeDriver):
|
|||
self.dict_mors[node]['cluster_mor'],
|
||||
datastore_regex=self._datastore_regex)
|
||||
name = self.dict_mors.get(node)['name']
|
||||
nodename = self._create_nodename(node, name)
|
||||
nodename = self._create_nodename(node)
|
||||
_vc_state = host.VCState(self._session, nodename,
|
||||
self.dict_mors.get(node)['cluster_mor'],
|
||||
self._datastore_regex)
|
||||
|
@ -353,22 +364,52 @@ class VMwareVCDriver(driver.ComputeDriver):
|
|||
deleted_nodes = (set(self._resource_keys) -
|
||||
set(self.dict_mors.keys()))
|
||||
for node in deleted_nodes:
|
||||
name = self.dict_mors.get(node)['name']
|
||||
nodename = self._create_nodename(node, name)
|
||||
nodename = self._create_nodename(node)
|
||||
del self._resources[nodename]
|
||||
self._resource_keys.discard(node)
|
||||
|
||||
def _create_nodename(self, mo_id, display_name):
|
||||
"""Creates the name that is stored in hypervisor_hostname column.
|
||||
def _get_vcenter_uuid(self):
|
||||
"""Retrieves the vCenter UUID."""
|
||||
|
||||
The name will be of the form similar to
|
||||
domain-1000(MyCluster)
|
||||
resgroup-1000(MyResourcePool)
|
||||
about = self._session._call_method(nova_vim_util, 'get_about_info')
|
||||
return about.instanceUuid
|
||||
|
||||
def _create_nodename(self, mo_id):
|
||||
"""Return a nodename which uniquely describes a cluster.
|
||||
|
||||
The name will be of the form:
|
||||
<mo id>.<vcenter uuid>
|
||||
e.g.
|
||||
domain-26.9d51f082-58a4-4449-beed-6fd205a5726b
|
||||
"""
|
||||
return mo_id + '(' + display_name + ')'
|
||||
|
||||
return '%s.%s' % (mo_id, self._vcenter_uuid)
|
||||
|
||||
def _normalize_nodename(self, nodename):
|
||||
"""Change I2f3b5d224cc653d0465598de0788116e71d1ca0d altered the format
|
||||
of nodename to <mo id>.<vCenter UUID>. This function matches legacy
|
||||
nodenames and translates them to the new format.
|
||||
|
||||
Note that the legacy format did not contain the vCenter UUID, which we
|
||||
are adding here. We can safely assume that we are adding the correct
|
||||
vCenter UUID because instance.host has caused it to be scheduled to
|
||||
this compute, which can only be configured with a single vCenter.
|
||||
"""
|
||||
|
||||
match = self.LEGACY_NODENAME.match(nodename)
|
||||
|
||||
# Return it unmodified if it's not in the legacy format
|
||||
if match is None:
|
||||
return nodename
|
||||
|
||||
mo_id = match.group(1)
|
||||
return self._create_nodename(mo_id)
|
||||
|
||||
def _get_resource_for_node(self, nodename):
|
||||
"""Gets the resource information for the specific node."""
|
||||
|
||||
nodename = self._normalize_nodename(nodename)
|
||||
|
||||
resource = self._resources.get(nodename)
|
||||
if not resource:
|
||||
msg = _("The resource %s does not exist") % nodename
|
||||
|
@ -376,26 +417,17 @@ class VMwareVCDriver(driver.ComputeDriver):
|
|||
return resource
|
||||
|
||||
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)
|
||||
"""
|
||||
"""Retrieve vmops object for this node."""
|
||||
resource = self._get_resource_for_node(nodename)
|
||||
return resource['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)
|
||||
"""
|
||||
"""Retrieve vmops object for this node."""
|
||||
resource = self._get_resource_for_node(nodename)
|
||||
return resource['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)
|
||||
"""
|
||||
"""Retrieve VCState object for this node."""
|
||||
resource = self._get_resource_for_node(nodename)
|
||||
return resource['vcstate']
|
||||
|
||||
|
@ -457,8 +489,7 @@ class VMwareVCDriver(driver.ComputeDriver):
|
|||
node_list = []
|
||||
self._update_resources()
|
||||
for node in self.dict_mors.keys():
|
||||
nodename = self._create_nodename(node,
|
||||
self.dict_mors.get(node)['name'])
|
||||
nodename = self._create_nodename(node)
|
||||
node_list.append(nodename)
|
||||
LOG.debug("The available nodes are: %s", node_list)
|
||||
return node_list
|
||||
|
|
Loading…
Reference in New Issue