Convert to compute services name scheme

Hypervisor naming scheme was 'split' into two distinct address values.
The first is compute host names which are derived from
list-compute-services API [1]. The second address value is the compute
control plane address that the tempest execution host will use to access
compute hosts. From BaseWhiteboxComputeTest get_hypervisor_ip and
get_all_hypervisors have been replaced with get_ctrlplane_address and
list_compute_hosts respectively. Unlike get_hypervisor_ip,
get_ctrlplane_address does not take a server id when finding a host ip,
but instead takes a compute hostname. list_compute_hosts returns all
service hostnames with binary name 'nova-compte' from the
list-compute-services API.

Several testcases from test_cpu_pinning.NUMALiveMigrationTest were
updated to use the new methods accordingly. One key change is every test
that calls list_compute_hosts, explicitly tries to convert returned
hostname to it's controlplane address. This is done because TripleO does
not have DNS entries for the compute hostnames and needs an IP address
when utilizing the whitebox.services module. test_volume_negative.py was
also updated to use the new methods when attempting to setup services.

test_base.py had its tests updated as well to work with the new methods.

[1] https://docs.openstack.org/api-ref/compute/?expanded=list-compute-services-detail#list-compute-services

Change-Id: I4660043fcccec6721e197c4d77390744e356c59e
This commit is contained in:
James Parker 2020-03-27 16:34:40 -04:00
parent 6a9c3bab8f
commit 2b6a050d92
6 changed files with 94 additions and 75 deletions

View File

@ -36,6 +36,7 @@ class BaseWhiteboxComputeTest(base.BaseV2ComputeAdminTest):
# TODO(stephenfin): Rewrite tests to use 'admin_servers_client' etc.
cls.servers_client = cls.os_admin.servers_client
cls.flavors_client = cls.os_admin.flavors_client
cls.service_client = cls.os_admin.services_client
cls.hypervisor_client = cls.os_admin.hypervisor_client
cls.image_client = cls.os_admin.image_client_v2
cls.admin_migration_client = cls.os_admin.migrations_client
@ -99,37 +100,44 @@ class BaseWhiteboxComputeTest(base.BaseV2ComputeAdminTest):
return new_image['id']
def get_hypervisor_ip(self, server_id):
server = self.servers_client.show_server(server_id)
host = server['server']['OS-EXT-SRV-ATTR:host']
def get_ctrlplane_address(self, compute_hostname):
"""Return the appropriate host address depending on a deployment.
if not CONF.whitebox.hypervisors:
# NOTE(artom) [whitebox]/hypervisors not being set means we're
# running against a devstack deployment and we can just use host
# directly.
return host
In TripleO deployments the Undercloud does not have DNS entries for
the compute hosts. This method checks if there are 'DNS' mappings of
the provided hostname to it's control plane IP address and returns it.
For Devstack deployments, no such parameters will exist and the method
will just return compute_hostname
if host in CONF.whitebox.hypervisors:
return CONF.whitebox.hypervisors[host]
raise exceptions.MissingHypervisorException(server=server_id,
host=host)
def get_all_hypervisors(self):
"""Returns a list of all hypervisor IPs in the deployment. Assumes all
are up and running.
:param compute_hostname: str the compute hostname
:return str simply pass the provided compute_hostname back or
return the associated control plane IP address
"""
if CONF.whitebox.hypervisors:
return CONF.whitebox.hypervisors.values()
hvs = self.hypervisor_client.list_hypervisors()['hypervisors']
return [hv['hypervisor_hostname'] for hv in hvs]
if not CONF.whitebox.ctrlplane_addresses:
return compute_hostname
if compute_hostname in CONF.whitebox.ctrlplane_addresses:
return CONF.whitebox.ctrlplane_addresses[compute_hostname]
raise exceptions.CtrlplaneAddressResolutionError(host=compute_hostname)
def list_compute_hosts(self):
"""Returns a list of all nova-compute hostnames in the deployment.
Assumes all are up and running.
"""
binary_name = 'nova-compute'
services = \
self.service_client.list_services(binary=binary_name)['services']
return [service['host'] for service in services]
def get_server_xml(self, server_id):
hv_ip = self.get_hypervisor_ip(server_id)
server = self.servers_client.show_server(server_id)
host = server['server']['OS-EXT-SRV-ATTR:host']
cntrlplane_addr = self.get_ctrlplane_address(host)
server_instance_name = self.servers_client.show_server(
server_id)['server']['OS-EXT-SRV-ATTR:instance_name']
virshxml = clients.VirshXMLClient(hv_ip)
virshxml = clients.VirshXMLClient(cntrlplane_addr)
xml = virshxml.dumpxml(server_instance_name)
return ET.fromstring(xml)

View File

@ -338,8 +338,7 @@ class CPUThreadPolicyTest(BasePinningTest):
try:
host_address = CONF.whitebox.hypervisors[host]
except KeyError:
raise exceptions.MissingHypervisorException(server="",
host=host)
raise exceptions.CtrlplaneAddressResolutionError(host=host)
else:
host_address = host
@ -490,9 +489,11 @@ class NUMALiveMigrationTest(BasePinningTest):
return set([len(cpu_list) for cpu_list in chain(*args)])
def test_cpu_pinning(self):
hv1, hv2 = self.get_all_hypervisors()
numaclient_1 = clients.NUMAClient(hv1)
numaclient_2 = clients.NUMAClient(hv2)
host1, host2 = [self.get_ctrlplane_address(host) for host in
self.list_compute_hosts()]
numaclient_1 = clients.NUMAClient(host1)
numaclient_2 = clients.NUMAClient(host2)
# Get hosts's topology
topo_1 = numaclient_1.get_host_topology()
@ -511,12 +512,12 @@ class NUMALiveMigrationTest(BasePinningTest):
# Set both hosts's vcpu_pin_set to the CPUs in the first NUMA node to
# force instances to land there
hv1_sm = clients.ServiceManager(hv1, 'nova-compute')
hv2_sm = clients.ServiceManager(hv2, 'nova-compute')
with hv1_sm.config_option('DEFAULT', 'vcpu_pin_set',
self._get_cpu_spec(topo_1[0])), \
hv2_sm.config_option('DEFAULT', 'vcpu_pin_set',
self._get_cpu_spec(topo_2[0])):
host1_sm = clients.ServiceManager(host1, 'nova-compute')
host2_sm = clients.ServiceManager(host2, 'nova-compute')
with host1_sm.config_option('DEFAULT', 'vcpu_pin_set',
self._get_cpu_spec(topo_1[0])), \
host2_sm.config_option('DEFAULT', 'vcpu_pin_set',
self._get_cpu_spec(topo_2[0])):
# Boot 2 servers such that their vCPUs "fill" a NUMA node.
specs = {'hw:cpu_policy': 'dedicated'}
flavor = self.create_flavor(vcpus=cpus_per_node.pop(),
@ -554,13 +555,9 @@ class NUMALiveMigrationTest(BasePinningTest):
# Live migrate server_b to server_a's compute, adding the second
# NUMA node's CPUs to vcpu_pin_set
host_a = self.get_host_other_than(server_b['id'])
# NOTE (jparker) since certain deployment's execution hosts
# may not have DNS mappings for their respective compute and
# controllers nodes, attempt to determine and use the hv ip
# when setting up the ServiceManager
host_a_ip = self.get_hypervisor_ip(server_a['id'])
host_a_sm = clients.ServiceManager(host_a_ip, 'nova-compute')
numaclient_a = clients.NUMAClient(host_a_ip)
host_a_addr = self.get_ctrlplane_address(host_a)
host_a_sm = clients.ServiceManager(host_a_addr, 'nova-compute')
numaclient_a = clients.NUMAClient(host_a_addr)
topo_a = numaclient_a.get_host_topology()
with host_a_sm.config_option(
'DEFAULT', 'vcpu_pin_set',
@ -597,20 +594,23 @@ class NUMALiveMigrationTest(BasePinningTest):
def test_emulator_threads(self):
# Need 4 CPUs on each host
for hv in self.get_all_hypervisors():
numaclient = clients.NUMAClient(hv)
host1, host2 = [self.get_ctrlplane_address(host) for host in
self.list_compute_hosts()]
for host in [host1, host2]:
numaclient = clients.NUMAClient(host)
num_cpus = numaclient.get_num_cpus()
if num_cpus < 4:
raise self.skipException('%s has %d CPUs, need 4', hv,
raise self.skipException('%s has %d CPUs, need 4',
host,
num_cpus)
hv1, hv2 = self.get_all_hypervisors()
hv1_sm = clients.ServiceManager(hv1, 'nova-compute')
hv2_sm = clients.ServiceManager(hv2, 'nova-compute')
with hv1_sm.config_option('DEFAULT', 'vcpu_pin_set', '0,1'), \
hv1_sm.config_option('compute', 'cpu_shared_set', '2'), \
hv2_sm.config_option('DEFAULT', 'vcpu_pin_set', '0,1'), \
hv2_sm.config_option('compute', 'cpu_shared_set', '3'):
host1_sm = clients.ServiceManager(host1, 'nova-compute')
host2_sm = clients.ServiceManager(host2, 'nova-compute')
with host1_sm.config_option('DEFAULT', 'vcpu_pin_set', '0,1'), \
host1_sm.config_option('compute', 'cpu_shared_set', '2'), \
host2_sm.config_option('DEFAULT', 'vcpu_pin_set', '0,1'), \
host2_sm.config_option('compute', 'cpu_shared_set', '3'):
# Boot two servers
specs = {'hw:cpu_policy': 'dedicated',
@ -656,9 +656,11 @@ class NUMALiveMigrationTest(BasePinningTest):
self.delete_server(server_b['id'])
def test_hugepages(self):
hv_a, hv_b = self.get_all_hypervisors()
numaclient_a = clients.NUMAClient(hv_a)
numaclient_b = clients.NUMAClient(hv_b)
host_a, host_b = [self.get_ctrlplane_address(host) for host in
self.list_compute_hosts()]
numaclient_a = clients.NUMAClient(host_a)
numaclient_b = clients.NUMAClient(host_b)
# Get the first host's topology and hugepages config
topo_a = numaclient_a.get_host_topology()

View File

@ -70,10 +70,10 @@ class VolumesAdminNegativeTest(base.BaseWhiteboxComputeTest,
self.assertGreater(
len(disks_after_attach),
len(disks_before_attach))
hypervisor = self.get_hypervisor_ip(server['id'])
host = self.get_ctrlplane_address(server['OS-EXT-SRV-ATTR:host'])
# stop the nova_libvirt service
clients.ServiceManager(hypervisor, 'nova-libvirt').stop()
clients.ServiceManager(host, 'nova-libvirt').stop()
# While this call to n-api will return successfully the underlying call
# to the virt driver will fail as the libvirt service is stopped.
@ -85,7 +85,7 @@ class VolumesAdminNegativeTest(base.BaseWhiteboxComputeTest,
len(disks_after_failed_detach), len(disks_after_attach))
# restart the nova_libvirt after failed detach
clients.ServiceManager(hypervisor, 'nova-libvirt').restart()
clients.ServiceManager(host, 'nova-libvirt').restart()
# This will be a successful detach as nova_libvirt is started again
self.servers_client.detach_volume(server['id'], attachment['volumeId'])

View File

@ -40,18 +40,20 @@ general_opts = [
default=False,
help='Deployment is containerized.'),
cfg.DictOpt(
'hypervisors',
help="Dictionary of hypervisor IP addresses. The keys are the "
"hostnames as they appear in the OS-EXT-SRV-ATTR:host field of "
"Nova's show server details API. The values are the ctlplane IP "
"addresses. For example:"
'ctrlplane_addresses',
help="Dictionary of control plane addresses. The keys are the "
"compute hostnames as they appear in the OS-EXT-SRV-ATTR:host "
"field of Nova's show server details API. The values are the "
"control plane addresses. For example:"
""
" hypervisors = compute-0.localdomain:172.16.42.11,"
" controller-0.localdomain:172.16.42.10"
" ctrlplane_addresses = compute-0.localdomain:172.16.42.11,"
" compute-1.localdomain:172.16.42.10"
""
"While this looks like a poor man's DNS, this is needed "
"because the environment running the test does not necessarily "
"have the ctlplane DNS accessible."),
"have the ctlplane DNS accessible.",
deprecated_opts=[cfg.DeprecatedOpt('hypervisors',
group='whitebox')]),
cfg.IntOpt(
'max_compute_nodes',
default=31337,

View File

@ -16,8 +16,8 @@
from tempest.lib import exceptions
class MissingHypervisorException(exceptions.TempestException):
message = "Unable to find IP in conf. Server: %(server)s, host: %(host)s."
class CtrlplaneAddressResolutionError(exceptions.TempestException):
message = "Unable to find IP in conf. for host: %(host)s."
class MissingServiceSectionException(exceptions.TempestException):

View File

@ -26,6 +26,11 @@ def fake_show_server(server_id):
return {'server': {'OS-EXT-SRV-ATTR:host': 'missing-host'}}
def fake_list_services(binary):
return {'services': [{'binary': 'nova-compute', 'host': 'fake-host'},
{'binary': 'nova-compute', 'host': 'fake-host2'}]}
class UtilsTestCase(base.WhiteboxPluginTestCase):
def setUp(self):
@ -36,19 +41,21 @@ class UtilsTestCase(base.WhiteboxPluginTestCase):
return_value=None)
self.test_class = compute_base.BaseWhiteboxComputeTest()
self.test_class.servers_client = mock.Mock()
self.test_class.service_client = mock.Mock()
self.test_class.servers_client.show_server = fake_show_server
self.test_class.service_client.list_services = fake_list_services
self.flags(hypervisors={'fake-host': 'fake-ip',
'fake-host2': 'fake-ip2'}, group='whitebox')
def test_get_hypervisor_ip(self):
def test_get_ctrlplane_address(self):
self.assertEqual('fake-ip',
self.test_class.get_hypervisor_ip('fake-id'))
self.test_class.get_ctrlplane_address('fake-host'))
@mock.patch.object(compute_base.LOG, 'error')
def test_get_hypervisor_ip_keyerror(self, mock_log):
self.assertRaises(exceptions.MissingHypervisorException,
self.test_class.get_hypervisor_ip, 'missing-id')
def test_get_ctrlplane_address_keyerror(self, mock_log):
self.assertRaises(exceptions.CtrlplaneAddressResolutionError,
self.test_class.get_ctrlplane_address, 'missing-id')
def test_get_all_hypervisors(self):
self.assertItemsEqual(['fake-ip', 'fake-ip2'],
self.test_class.get_all_hypervisors())
def test_list_compute_hosts(self):
self.assertItemsEqual(['fake-host', 'fake-host2'],
self.test_class.list_compute_hosts())