Merge "VMware: Ensure compute_node.hypervisor_hostname is unique"
This commit is contained in:
commit
93610787de
|
@ -36,6 +36,7 @@ _CLASSES = ['Datacenter', 'Datastore', 'ResourcePool', 'VirtualMachine',
|
||||||
'files', 'ClusterComputeResource', 'HostStorageSystem']
|
'files', 'ClusterComputeResource', 'HostStorageSystem']
|
||||||
|
|
||||||
_FAKE_FILE_SIZE = 1024
|
_FAKE_FILE_SIZE = 1024
|
||||||
|
_FAKE_VCENTER_UUID = '497c514c-ef5e-4e7f-8d93-ec921993b93a'
|
||||||
|
|
||||||
_db_content = {}
|
_db_content = {}
|
||||||
_array_types = {}
|
_array_types = {}
|
||||||
|
@ -1195,6 +1196,8 @@ class FakeVim(object):
|
||||||
about_info = DataObject()
|
about_info = DataObject()
|
||||||
about_info.name = "VMware vCenter Server"
|
about_info.name = "VMware vCenter Server"
|
||||||
about_info.version = "5.1.0"
|
about_info.version = "5.1.0"
|
||||||
|
about_info.instanceUuid = _FAKE_VCENTER_UUID
|
||||||
|
|
||||||
service_content.about = about_info
|
service_content.about = about_info
|
||||||
|
|
||||||
self._service_content = service_content
|
self._service_content = service_content
|
||||||
|
|
|
@ -220,7 +220,7 @@ class VMwareAPIVMTestCase(test.NoDBTestCase):
|
||||||
self._set_exception_vars()
|
self._set_exception_vars()
|
||||||
self.node_name = self.conn._resources.keys()[0]
|
self.node_name = self.conn._resources.keys()[0]
|
||||||
self.node_name2 = self.conn._resources.keys()[1]
|
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'
|
self.ds = 'ds1'
|
||||||
else:
|
else:
|
||||||
self.ds = 'ds2'
|
self.ds = 'ds2'
|
||||||
|
@ -2310,3 +2310,32 @@ class VMwareAPIVMTestCase(test.NoDBTestCase):
|
||||||
self.conn._update_pbm_location()
|
self.conn._update_pbm_location()
|
||||||
self.assertEqual('fira', self.conn._session._pbm_wsdl_loc)
|
self.assertEqual('fira', self.conn._session._pbm_wsdl_loc)
|
||||||
self.assertIsNone(self.conn._session._pbm)
|
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))
|
||||||
|
|
|
@ -37,6 +37,7 @@ from nova.virt import driver
|
||||||
from nova.virt.vmwareapi import constants
|
from nova.virt.vmwareapi import constants
|
||||||
from nova.virt.vmwareapi import error_util
|
from nova.virt.vmwareapi import error_util
|
||||||
from nova.virt.vmwareapi import host
|
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 vm_util
|
||||||
from nova.virt.vmwareapi import vmops
|
from nova.virt.vmwareapi import vmops
|
||||||
from nova.virt.vmwareapi import volumeops
|
from nova.virt.vmwareapi import volumeops
|
||||||
|
@ -113,6 +114,13 @@ class VMwareVCDriver(driver.ComputeDriver):
|
||||||
"supports_migrate_to_same_host": True
|
"supports_migrate_to_same_host": True
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# 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
|
# The vCenter driver includes API that acts on ESX hosts or groups
|
||||||
# of ESX hosts in clusters or non-cluster logical-groupings.
|
# of ESX hosts in clusters or non-cluster logical-groupings.
|
||||||
#
|
#
|
||||||
|
@ -171,6 +179,7 @@ class VMwareVCDriver(driver.ComputeDriver):
|
||||||
LOG.warning(_LW("The following clusters could not be found in the "
|
LOG.warning(_LW("The following clusters could not be found in the "
|
||||||
"vCenter %s"), list(missing_clusters))
|
"vCenter %s"), list(missing_clusters))
|
||||||
|
|
||||||
|
self._vcenter_uuid = self._get_vcenter_uuid()
|
||||||
# The _resources is used to maintain the vmops, volumeops and vcstate
|
# The _resources is used to maintain the vmops, volumeops and vcstate
|
||||||
# objects per cluster
|
# objects per cluster
|
||||||
self._resources = {}
|
self._resources = {}
|
||||||
|
@ -330,14 +339,16 @@ class VMwareVCDriver(driver.ComputeDriver):
|
||||||
The VMwareVMOps, VMwareVolumeOps and VCState object is for each
|
The VMwareVMOps, VMwareVolumeOps and VCState object is for each
|
||||||
cluster/rp. The dictionary is of the form
|
cluster/rp. The dictionary is of the form
|
||||||
{
|
{
|
||||||
domain-1000 : {'vmops': vmops_obj,
|
'domain-1000.497c514c-ef5e-4e7f-8d93-ec921993b93a' : {
|
||||||
'volumeops': volumeops_obj,
|
'vmops': vmops_obj,
|
||||||
'vcstate': vcstate_obj,
|
'volumeops': volumeops_obj,
|
||||||
'name': MyCluster},
|
'vcstate': vcstate_obj,
|
||||||
resgroup-1000 : {'vmops': vmops_obj,
|
'name': MyCluster},
|
||||||
'volumeops': volumeops_obj,
|
'resgroup-1000.497c514c-ef5e-4e7f-8d93-ec921993b93a' : {
|
||||||
'vcstate': vcstate_obj,
|
'vmops': vmops_obj,
|
||||||
'name': MyRP},
|
'volumeops': volumeops_obj,
|
||||||
|
'vcstate': vcstate_obj,
|
||||||
|
'name': MyRP},
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
added_nodes = set(self.dict_mors.keys()) - set(self._resource_keys)
|
added_nodes = set(self.dict_mors.keys()) - set(self._resource_keys)
|
||||||
|
@ -349,7 +360,7 @@ class VMwareVCDriver(driver.ComputeDriver):
|
||||||
self.dict_mors[node]['cluster_mor'],
|
self.dict_mors[node]['cluster_mor'],
|
||||||
datastore_regex=self._datastore_regex)
|
datastore_regex=self._datastore_regex)
|
||||||
name = self.dict_mors.get(node)['name']
|
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,
|
_vc_state = host.VCState(self._session, nodename,
|
||||||
self.dict_mors.get(node)['cluster_mor'],
|
self.dict_mors.get(node)['cluster_mor'],
|
||||||
self._datastore_regex)
|
self._datastore_regex)
|
||||||
|
@ -363,22 +374,52 @@ class VMwareVCDriver(driver.ComputeDriver):
|
||||||
deleted_nodes = (set(self._resource_keys) -
|
deleted_nodes = (set(self._resource_keys) -
|
||||||
set(self.dict_mors.keys()))
|
set(self.dict_mors.keys()))
|
||||||
for node in deleted_nodes:
|
for node in deleted_nodes:
|
||||||
name = self.dict_mors.get(node)['name']
|
nodename = self._create_nodename(node)
|
||||||
nodename = self._create_nodename(node, name)
|
|
||||||
del self._resources[nodename]
|
del self._resources[nodename]
|
||||||
self._resource_keys.discard(node)
|
self._resource_keys.discard(node)
|
||||||
|
|
||||||
def _create_nodename(self, mo_id, display_name):
|
def _get_vcenter_uuid(self):
|
||||||
"""Creates the name that is stored in hypervisor_hostname column.
|
"""Retrieves the vCenter UUID."""
|
||||||
|
|
||||||
The name will be of the form similar to
|
about = self._session._call_method(nova_vim_util, 'get_about_info')
|
||||||
domain-1000(MyCluster)
|
return about.instanceUuid
|
||||||
resgroup-1000(MyResourcePool)
|
|
||||||
|
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):
|
def _get_resource_for_node(self, nodename):
|
||||||
"""Gets the resource information for the specific node."""
|
"""Gets the resource information for the specific node."""
|
||||||
|
|
||||||
|
nodename = self._normalize_nodename(nodename)
|
||||||
|
|
||||||
resource = self._resources.get(nodename)
|
resource = self._resources.get(nodename)
|
||||||
if not resource:
|
if not resource:
|
||||||
msg = _("The resource %s does not exist") % nodename
|
msg = _("The resource %s does not exist") % nodename
|
||||||
|
@ -386,26 +427,17 @@ class VMwareVCDriver(driver.ComputeDriver):
|
||||||
return resource
|
return resource
|
||||||
|
|
||||||
def _get_vmops_for_compute_node(self, nodename):
|
def _get_vmops_for_compute_node(self, nodename):
|
||||||
"""Retrieve vmops object from mo_id stored in the node name.
|
"""Retrieve vmops object for this node."""
|
||||||
|
|
||||||
Node name is of the form domain-1000(MyCluster)
|
|
||||||
"""
|
|
||||||
resource = self._get_resource_for_node(nodename)
|
resource = self._get_resource_for_node(nodename)
|
||||||
return resource['vmops']
|
return resource['vmops']
|
||||||
|
|
||||||
def _get_volumeops_for_compute_node(self, nodename):
|
def _get_volumeops_for_compute_node(self, nodename):
|
||||||
"""Retrieve vmops object from mo_id stored in the node name.
|
"""Retrieve vmops object for this node."""
|
||||||
|
|
||||||
Node name is of the form domain-1000(MyCluster)
|
|
||||||
"""
|
|
||||||
resource = self._get_resource_for_node(nodename)
|
resource = self._get_resource_for_node(nodename)
|
||||||
return resource['volumeops']
|
return resource['volumeops']
|
||||||
|
|
||||||
def _get_vc_state_for_compute_node(self, nodename):
|
def _get_vc_state_for_compute_node(self, nodename):
|
||||||
"""Retrieve VCState object from mo_id stored in the node name.
|
"""Retrieve VCState object for this node."""
|
||||||
|
|
||||||
Node name is of the form domain-1000(MyCluster)
|
|
||||||
"""
|
|
||||||
resource = self._get_resource_for_node(nodename)
|
resource = self._get_resource_for_node(nodename)
|
||||||
return resource['vcstate']
|
return resource['vcstate']
|
||||||
|
|
||||||
|
@ -467,8 +499,7 @@ class VMwareVCDriver(driver.ComputeDriver):
|
||||||
node_list = []
|
node_list = []
|
||||||
self._update_resources()
|
self._update_resources()
|
||||||
for node in self.dict_mors.keys():
|
for node in self.dict_mors.keys():
|
||||||
nodename = self._create_nodename(node,
|
nodename = self._create_nodename(node)
|
||||||
self.dict_mors.get(node)['name'])
|
|
||||||
node_list.append(nodename)
|
node_list.append(nodename)
|
||||||
LOG.debug("The available nodes are: %s", node_list)
|
LOG.debug("The available nodes are: %s", node_list)
|
||||||
return node_list
|
return node_list
|
||||||
|
|
Loading…
Reference in New Issue