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 import config
from whitebox_tempest_plugin import exceptions
from whitebox_tempest_plugin.services import clients
from whitebox_tempest_plugin import utils as whitebox_utils
CONF = config.CONF
LOG = logging.getLogger(__name__)
@ -99,28 +99,6 @@ class BaseWhiteboxComputeTest(base.BaseV2ComputeAdminTest):
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):
"""Returns a list of all nova-compute hostnames in the deployment.
Assumes all are up and running.
@ -133,7 +111,7 @@ class BaseWhiteboxComputeTest(base.BaseV2ComputeAdminTest):
def get_server_xml(self, server_id):
server = self.servers_client.show_server(server_id)
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_id)['server']['OS-EXT-SRV-ATTR:instance_name']

View File

@ -337,7 +337,7 @@ class CPUThreadPolicyTest(BasePinningTest):
"""
siblings = {}
host = self.get_ctlplane_address(host)
host = whitebox_utils.get_ctlplane_address(host)
virshxml = clients.VirshXMLClient(host)
capxml = virshxml.capabilities()
root = ET.fromstring(capxml)
@ -490,11 +490,12 @@ class NUMALiveMigrationTest(BasePinningTest):
@decorators.skip_because(bug='2007395', bug_type='storyboard')
def test_cpu_pinning(self):
host1, host2 = [self.get_ctlplane_address(host) for host in
self.list_compute_hosts()]
host1, host2 = self.list_compute_hosts()
ctlplane1, ctlplane2 = [whitebox_utils.get_ctlplane_address(host) for
host in [host1, host2]]
numaclient_1 = clients.NUMAClient(host1)
numaclient_2 = clients.NUMAClient(host2)
numaclient_1 = clients.NUMAClient(ctlplane1)
numaclient_2 = clients.NUMAClient(ctlplane2)
# Get hosts's 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
# NUMA node's CPUs to vcpu_pin_set
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_addr, 'nova-compute', self.os_admin.services_client)
host_a, 'nova-compute', self.os_admin.services_client)
numaclient_a = clients.NUMAClient(host_a_addr)
topo_a = numaclient_a.get_host_topology()
with host_a_sm.config_options(
@ -601,10 +602,11 @@ class NUMALiveMigrationTest(BasePinningTest):
def test_emulator_threads(self):
# Need 4 CPUs on each host
host1, host2 = [self.get_ctlplane_address(host) for host in
self.list_compute_hosts()]
host1, host2 = 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)
num_cpus = numaclient.get_num_cpus()
if num_cpus < 4:
@ -666,7 +668,7 @@ class NUMALiveMigrationTest(BasePinningTest):
self.delete_server(server_b['id'])
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()]
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.services import clients
from whitebox_tempest_plugin import utils as whitebox_utils
CONF = config.CONF
@ -70,7 +71,9 @@ class VolumesAdminNegativeTest(base.BaseWhiteboxComputeTest,
self.assertGreater(
len(disks_after_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
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 import exceptions
from whitebox_tempest_plugin import utils as whitebox_utils
CONF = config.CONF
LOG = logging.getLogger(__name__)
@ -35,13 +36,13 @@ LOG = logging.getLogger(__name__)
class SSHClient(object):
"""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_user = CONF.whitebox.ctlplane_ssh_username
self.hostname = hostname
self.ctlplane_address = ctlplane_address
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)
if (CONF.whitebox.containers and container_name):
executable = CONF.whitebox.container_runtime
@ -174,20 +175,26 @@ class NovaServiceManager(ServiceManager):
"""
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.host = host
def start(self):
result = self.execute(self.start_command, sudo=True)
waiters.wait_for_nova_service_state(self.services_client,
self.hostname, self.service,
self.host,
self.service,
'up')
return result
def stop(self):
result = self.execute(self.stop_command, sudo=True)
waiters.wait_for_nova_service_state(self.services_client,
self.hostname, self.service,
self.host,
self.service,
'down')
return result

View File

@ -15,7 +15,6 @@
import mock
from whitebox_tempest_plugin.api.compute import base as compute_base
from whitebox_tempest_plugin import exceptions
from whitebox_tempest_plugin.tests import base
@ -48,15 +47,6 @@ class ComputeBaseTestCase(base.WhiteboxPluginTestCase):
'fake-host2': 'fake-ip2'},
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):
self.assertItemsEqual(['fake-host', 'fake-host2'],
self.test_class.list_compute_hosts())

View File

@ -12,12 +12,19 @@
# License for the specific language governing permissions and limitations
# under the License.
from whitebox_tempest_plugin import exceptions
from whitebox_tempest_plugin.tests import base
from whitebox_tempest_plugin import utils
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):
json = {'2': [2, 3, 1],
'1': True,
@ -34,3 +41,11 @@ class UtilsTestCase(base.WhiteboxPluginTestCase):
'y': [3, 4, 5],
'z': [0, 1, 3]}]},
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
from oslo_serialization import jsonutils
from tempest import config
from whitebox_tempest_plugin import exceptions
if six.PY2:
import contextlib2 as contextlib
else:
import contextlib
CONF = config.CONF
def normalize_json(json):
"""Normalizes a JSON dict for consistent equality tests. Sorts the keys,
@ -43,3 +47,26 @@ def normalize_json(json):
def multicontext(*context_managers):
with contextlib.ExitStack() as stack:
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)