Pass hostname to NovaServiceManager

Default TripleO deployments utilize compute domain names when looking up
nova service binaries, this lookup does not work when using a compute's
control plane IP address though. To allow the recent change [1] to run
downstream, this commit updates how parameters are passed to the init of
NovaServiceManager. The hostname is passed to NovaServiceManager instead
of the IP address.  During the init the compute's control plane IP
address is determined and passed to its SSHClient. When
NovaServiceManager attempts to access nova services, it uses the
compute's provided hostname now instead of the IP address.

The get_ctlplane_address() function was moved from api.compute.base to
utils, since services.clients now needs to leverage this functionality
as well. All test case calls of get_ctlplane_address() have been updated
to use the new module path. Unit test modules test_base and test_utils
were updated to reflect these changes.

Lastly test cases interfacing with NovaServiceManager have been updated
to store both the compute's hostname as well as it's associated control
plane IP address. Originally these tests only stored the compute's
control plane address.

[1] https://review.opendev.org/#/c/736820/

Change-Id: I4d9330cf8abcb6ba3c0852e6ce3db732e468c6a5
This commit is contained in:
James Parker 2020-08-17 17:10:08 -04:00
parent 6869d270e8
commit 1718597200
7 changed files with 74 additions and 52 deletions

View File

@ -21,8 +21,8 @@ from tempest.api.compute import base
from tempest.common import waiters from tempest.common import waiters
from tempest import config from tempest import config
from whitebox_tempest_plugin import exceptions
from whitebox_tempest_plugin.services import clients from whitebox_tempest_plugin.services import clients
from whitebox_tempest_plugin import utils as whitebox_utils
CONF = config.CONF CONF = config.CONF
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
@ -99,28 +99,6 @@ class BaseWhiteboxComputeTest(base.BaseV2ComputeAdminTest):
return new_image['id'] return new_image['id']
def get_ctlplane_address(self, compute_hostname):
"""Return the appropriate host address depending on a deployment.
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 its control plane IP address and returns it.
For Devstack deployments, no such parameters will exist and the method
will just return compute_hostname
:param compute_hostname: str the compute hostname
:return: The address to be used to access the compute host. For
devstack deployments, this is compute_host itself. For
TripleO, it needs to be looked up in the configuration.
"""
if not CONF.whitebox.ctlplane_addresses:
return compute_hostname
if compute_hostname in CONF.whitebox.ctlplane_addresses:
return CONF.whitebox.ctlplane_addresses[compute_hostname]
raise exceptions.CtrlplaneAddressResolutionError(host=compute_hostname)
def list_compute_hosts(self): def list_compute_hosts(self):
"""Returns a list of all nova-compute hostnames in the deployment. """Returns a list of all nova-compute hostnames in the deployment.
Assumes all are up and running. Assumes all are up and running.
@ -133,7 +111,7 @@ class BaseWhiteboxComputeTest(base.BaseV2ComputeAdminTest):
def get_server_xml(self, server_id): def get_server_xml(self, server_id):
server = self.servers_client.show_server(server_id) server = self.servers_client.show_server(server_id)
host = server['server']['OS-EXT-SRV-ATTR:host'] host = server['server']['OS-EXT-SRV-ATTR:host']
cntrlplane_addr = self.get_ctlplane_address(host) cntrlplane_addr = whitebox_utils.get_ctlplane_address(host)
server_instance_name = self.servers_client.show_server( server_instance_name = self.servers_client.show_server(
server_id)['server']['OS-EXT-SRV-ATTR:instance_name'] server_id)['server']['OS-EXT-SRV-ATTR:instance_name']

View File

@ -337,7 +337,7 @@ class CPUThreadPolicyTest(BasePinningTest):
""" """
siblings = {} siblings = {}
host = self.get_ctlplane_address(host) host = whitebox_utils.get_ctlplane_address(host)
virshxml = clients.VirshXMLClient(host) virshxml = clients.VirshXMLClient(host)
capxml = virshxml.capabilities() capxml = virshxml.capabilities()
root = ET.fromstring(capxml) root = ET.fromstring(capxml)
@ -490,11 +490,12 @@ class NUMALiveMigrationTest(BasePinningTest):
@decorators.skip_because(bug='2007395', bug_type='storyboard') @decorators.skip_because(bug='2007395', bug_type='storyboard')
def test_cpu_pinning(self): def test_cpu_pinning(self):
host1, host2 = [self.get_ctlplane_address(host) for host in host1, host2 = self.list_compute_hosts()
self.list_compute_hosts()] ctlplane1, ctlplane2 = [whitebox_utils.get_ctlplane_address(host) for
host in [host1, host2]]
numaclient_1 = clients.NUMAClient(host1) numaclient_1 = clients.NUMAClient(ctlplane1)
numaclient_2 = clients.NUMAClient(host2) numaclient_2 = clients.NUMAClient(ctlplane2)
# Get hosts's topology # Get hosts's topology
topo_1 = numaclient_1.get_host_topology() topo_1 = numaclient_1.get_host_topology()
@ -560,9 +561,9 @@ class NUMALiveMigrationTest(BasePinningTest):
# Live migrate server_b to server_a's compute, adding the second # Live migrate server_b to server_a's compute, adding the second
# NUMA node's CPUs to vcpu_pin_set # NUMA node's CPUs to vcpu_pin_set
host_a = self.get_host_other_than(server_b['id']) host_a = self.get_host_other_than(server_b['id'])
host_a_addr = self.get_ctlplane_address(host_a) host_a_addr = whitebox_utils.get_ctlplane_address(host_a)
host_a_sm = clients.NovaServiceManager( host_a_sm = clients.NovaServiceManager(
host_a_addr, 'nova-compute', self.os_admin.services_client) host_a, 'nova-compute', self.os_admin.services_client)
numaclient_a = clients.NUMAClient(host_a_addr) numaclient_a = clients.NUMAClient(host_a_addr)
topo_a = numaclient_a.get_host_topology() topo_a = numaclient_a.get_host_topology()
with host_a_sm.config_options( with host_a_sm.config_options(
@ -601,10 +602,11 @@ class NUMALiveMigrationTest(BasePinningTest):
def test_emulator_threads(self): def test_emulator_threads(self):
# Need 4 CPUs on each host # Need 4 CPUs on each host
host1, host2 = [self.get_ctlplane_address(host) for host in host1, host2 = self.list_compute_hosts()
self.list_compute_hosts()] ctlplane1, ctlplane2 = [whitebox_utils.get_ctlplane_address(host) for
host in [host1, host2]]
for host in [host1, host2]: for host in [ctlplane1, ctlplane2]:
numaclient = clients.NUMAClient(host) numaclient = clients.NUMAClient(host)
num_cpus = numaclient.get_num_cpus() num_cpus = numaclient.get_num_cpus()
if num_cpus < 4: if num_cpus < 4:
@ -666,7 +668,7 @@ class NUMALiveMigrationTest(BasePinningTest):
self.delete_server(server_b['id']) self.delete_server(server_b['id'])
def test_hugepages(self): def test_hugepages(self):
host_a, host_b = [self.get_ctlplane_address(host) for host in host_a, host_b = [whitebox_utils.get_ctlplane_address(host) for host in
self.list_compute_hosts()] self.list_compute_hosts()]
numaclient_a = clients.NUMAClient(host_a) numaclient_a = clients.NUMAClient(host_a)

View File

@ -22,6 +22,7 @@ from tempest import config
from whitebox_tempest_plugin.api.compute import base from whitebox_tempest_plugin.api.compute import base
from whitebox_tempest_plugin.services import clients from whitebox_tempest_plugin.services import clients
from whitebox_tempest_plugin import utils as whitebox_utils
CONF = config.CONF CONF = config.CONF
@ -70,7 +71,9 @@ class VolumesAdminNegativeTest(base.BaseWhiteboxComputeTest,
self.assertGreater( self.assertGreater(
len(disks_after_attach), len(disks_after_attach),
len(disks_before_attach)) len(disks_before_attach))
host = self.get_ctlplane_address(server['OS-EXT-SRV-ATTR:host']) host = whitebox_utils.get_ctlplane_address(
server['OS-EXT-SRV-ATTR:host']
)
# stop the libvirt service # stop the libvirt service
clients.ServiceManager(host, 'libvirt').stop() clients.ServiceManager(host, 'libvirt').stop()

View File

@ -27,6 +27,7 @@ from tempest.lib import exceptions as tempest_libexc
from whitebox_tempest_plugin.common import waiters from whitebox_tempest_plugin.common import waiters
from whitebox_tempest_plugin import exceptions from whitebox_tempest_plugin import exceptions
from whitebox_tempest_plugin import utils as whitebox_utils
CONF = config.CONF CONF = config.CONF
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
@ -35,13 +36,13 @@ LOG = logging.getLogger(__name__)
class SSHClient(object): class SSHClient(object):
"""A client to execute remote commands, based on tempest.lib.common.ssh.""" """A client to execute remote commands, based on tempest.lib.common.ssh."""
def __init__(self, hostname): def __init__(self, ctlplane_address):
self.ssh_key = CONF.whitebox.ctlplane_ssh_private_key_path self.ssh_key = CONF.whitebox.ctlplane_ssh_private_key_path
self.ssh_user = CONF.whitebox.ctlplane_ssh_username self.ssh_user = CONF.whitebox.ctlplane_ssh_username
self.hostname = hostname self.ctlplane_address = ctlplane_address
def execute(self, command, container_name=None, sudo=False): def execute(self, command, container_name=None, sudo=False):
ssh_client = ssh.Client(self.hostname, self.ssh_user, ssh_client = ssh.Client(self.ctlplane_address, self.ssh_user,
key_filename=self.ssh_key) key_filename=self.ssh_key)
if (CONF.whitebox.containers and container_name): if (CONF.whitebox.containers and container_name):
executable = CONF.whitebox.container_runtime executable = CONF.whitebox.container_runtime
@ -174,20 +175,26 @@ class NovaServiceManager(ServiceManager):
""" """
def __init__(self, host, service, services_client): def __init__(self, host, service, services_client):
super(NovaServiceManager, self).__init__(host, service) super(NovaServiceManager, self).__init__(
whitebox_utils.get_ctlplane_address(host),
service
)
self.services_client = services_client self.services_client = services_client
self.host = host
def start(self): def start(self):
result = self.execute(self.start_command, sudo=True) result = self.execute(self.start_command, sudo=True)
waiters.wait_for_nova_service_state(self.services_client, waiters.wait_for_nova_service_state(self.services_client,
self.hostname, self.service, self.host,
self.service,
'up') 'up')
return result return result
def stop(self): def stop(self):
result = self.execute(self.stop_command, sudo=True) result = self.execute(self.stop_command, sudo=True)
waiters.wait_for_nova_service_state(self.services_client, waiters.wait_for_nova_service_state(self.services_client,
self.hostname, self.service, self.host,
self.service,
'down') 'down')
return result return result

View File

@ -15,7 +15,6 @@
import mock import mock
from whitebox_tempest_plugin.api.compute import base as compute_base from whitebox_tempest_plugin.api.compute import base as compute_base
from whitebox_tempest_plugin import exceptions
from whitebox_tempest_plugin.tests import base from whitebox_tempest_plugin.tests import base
@ -48,15 +47,6 @@ class ComputeBaseTestCase(base.WhiteboxPluginTestCase):
'fake-host2': 'fake-ip2'}, 'fake-host2': 'fake-ip2'},
group='whitebox') group='whitebox')
def test_get_ctlplane_address(self):
self.assertEqual('fake-ip',
self.test_class.get_ctlplane_address('fake-host'))
@mock.patch.object(compute_base.LOG, 'error')
def test_get_ctlplane_address_keyerror(self, mock_log):
self.assertRaises(exceptions.CtrlplaneAddressResolutionError,
self.test_class.get_ctlplane_address, 'missing-id')
def test_list_compute_hosts(self): def test_list_compute_hosts(self):
self.assertItemsEqual(['fake-host', 'fake-host2'], self.assertItemsEqual(['fake-host', 'fake-host2'],
self.test_class.list_compute_hosts()) self.test_class.list_compute_hosts())

View File

@ -12,12 +12,19 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
from whitebox_tempest_plugin import exceptions
from whitebox_tempest_plugin.tests import base from whitebox_tempest_plugin.tests import base
from whitebox_tempest_plugin import utils from whitebox_tempest_plugin import utils
class UtilsTestCase(base.WhiteboxPluginTestCase): class UtilsTestCase(base.WhiteboxPluginTestCase):
def setUp(self):
super(UtilsTestCase, self).setUp()
self.flags(ctlplane_addresses={'fake-host': 'fake-ip',
'fake-host2': 'fake-ip2'},
group='whitebox')
def test_normalize_json(self): def test_normalize_json(self):
json = {'2': [2, 3, 1], json = {'2': [2, 3, 1],
'1': True, '1': True,
@ -34,3 +41,11 @@ class UtilsTestCase(base.WhiteboxPluginTestCase):
'y': [3, 4, 5], 'y': [3, 4, 5],
'z': [0, 1, 3]}]}, 'z': [0, 1, 3]}]},
utils.normalize_json(json)) utils.normalize_json(json))
def test_get_ctlplane_address(self):
self.assertEqual('fake-ip',
utils.get_ctlplane_address('fake-host'))
def test_get_ctlplane_address_keyerror(self):
self.assertRaises(exceptions.CtrlplaneAddressResolutionError,
utils.get_ctlplane_address, 'missing-id')

View File

@ -15,12 +15,16 @@
import six import six
from oslo_serialization import jsonutils from oslo_serialization import jsonutils
from tempest import config
from whitebox_tempest_plugin import exceptions
if six.PY2: if six.PY2:
import contextlib2 as contextlib import contextlib2 as contextlib
else: else:
import contextlib import contextlib
CONF = config.CONF
def normalize_json(json): def normalize_json(json):
"""Normalizes a JSON dict for consistent equality tests. Sorts the keys, """Normalizes a JSON dict for consistent equality tests. Sorts the keys,
@ -43,3 +47,26 @@ def normalize_json(json):
def multicontext(*context_managers): def multicontext(*context_managers):
with contextlib.ExitStack() as stack: with contextlib.ExitStack() as stack:
yield [stack.enter_context(mgr) for mgr in context_managers] yield [stack.enter_context(mgr) for mgr in context_managers]
def get_ctlplane_address(compute_hostname):
"""Return the appropriate host address depending on a deployment.
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 its control plane IP address and returns it.
For Devstack deployments, no such parameters will exist and the method
will just return compute_hostname
:param compute_hostname: str the compute hostname
:return: The address to be used to access the compute host. For
devstack deployments, this is compute_host itself. For TripleO, it needs
to be looked up in the configuration.
"""
if not CONF.whitebox.ctlplane_addresses:
return compute_hostname
if compute_hostname in CONF.whitebox.ctlplane_addresses:
return CONF.whitebox.ctlplane_addresses[compute_hostname]
raise exceptions.CtrlplaneAddressResolutionError(host=compute_hostname)