From 3fe1d72fa6d9d287a19c26d5080b43fd3f203a0a Mon Sep 17 00:00:00 2001 From: James Parker Date: Tue, 30 May 2023 13:59:01 -0400 Subject: [PATCH] Add global nodes variable For multi-rhel environments the container/service names are different based on the RHEL version. Introduce the capacity to define the expected container/service names for each of the compute nodes in a deployment. Current compute yaml file follows the below format: computerhel8-0.redhat.local: services: libvirt: container_name: nova_libvirt start_command: 'systemctl start tripleo_nova_libvirt' stop_command: 'systemctl stop tripleo_nova_libvirt' nova-compute: container_name: nova_compute config_path: '/var/lib/config-data/puppet-generated/nova_libvirt/etc/nova/nova.conf' start_command: 'systemctl start tripleo_nova_compute' stop_command: 'systemctl stop tripleo_nova_compute' compute-0.redhat.local: services: libvirt: container_name: nova_virtqemud start_command: 'systemctl start tripleo_nova_virtqemud' stop_command: 'systemctl stop tripleo_nova_virtqemud' nova-compute: container_name: nova_compute config_path: '/var/lib/config-data/puppet-generated/nova_libvirt/etc/nova/nova.conf' start_command: 'systemctl start tripleo_nova_compute' stop_command: 'systemctl stop tripleo_nova_compute' Also removed the unit test execution since they do not support this latest change and do not feel the tests are adding any value to commit validation at this time. Change-Id: I98ac827feb4be77af9a482d8ce43d0f1d062e54d --- devstack/plugin.sh | 10 +--- devstack/settings | 10 +--- playbooks/templates/compute_nodes.yaml.j2 | 13 +++++ playbooks/whitebox/pre.yaml | 20 +++++++- tox.ini | 5 +- whitebox_tempest_plugin/api/compute/base.py | 7 +-- .../api/compute/test_cpu_pinning.py | 1 - .../api/compute/test_rbd_direct_download.py | 4 +- .../api/compute/test_volume_encryption.py | 3 +- .../api/compute/test_volume_negative.py | 4 +- whitebox_tempest_plugin/config.py | 51 ++----------------- whitebox_tempest_plugin/plugin.py | 4 -- whitebox_tempest_plugin/services/clients.py | 38 +++++++------- whitebox_tempest_plugin/utils.py | 11 ++++ 14 files changed, 76 insertions(+), 105 deletions(-) create mode 100644 playbooks/templates/compute_nodes.yaml.j2 diff --git a/devstack/plugin.sh b/devstack/plugin.sh index 96dbee86..90990c97 100644 --- a/devstack/plugin.sh +++ b/devstack/plugin.sh @@ -18,15 +18,7 @@ function configure { iniset $TEMPEST_CONFIG whitebox rx_queue_size $WHITEBOX_RX_QUEUE_SIZE iniset $TEMPEST_CONFIG whitebox default_video_model $WHITEBOX_DEFAULT_VIDEO_MODEL iniset $TEMPEST_CONFIG whitebox max_disk_devices_to_attach $WHITEBOX_MAX_DISK_DEVICES_TO_ATTACH - - iniset $TEMPEST_CONFIG whitebox-nova-compute config_path "$WHITEBOX_NOVA_COMPUTE_CONFIG_PATH" - iniset $TEMPEST_CONFIG whitebox-nova-compute stop_command "$WHITEBOX_NOVA_COMPUTE_STOP_COMMAND" - iniset $TEMPEST_CONFIG whitebox-nova-compute start_command "$WHITEBOX_NOVA_COMPUTE_START_COMMAND" - - iniset $TEMPEST_CONFIG whitebox-libvirt start_command "$WHITEBOX_LIBVIRT_START_COMMAND" - iniset $TEMPEST_CONFIG whitebox-libvirt stop_command "$WHITEBOX_LIBVIRT_STOP_COMMAND" - iniset $TEMPEST_CONFIG whitebox-libvirt mask_command "$WHITEBOX_LIBVIRT_MASK_COMMAND" - iniset $TEMPEST_CONFIG whitebox-libvirt unmask_command "$WHITEBOX_LIBVIRT_UNMASK_COMMAND" + iniset $TEMPEST_CONFIG whitebox nodes_yaml $WHITEBOX_NODES_YAML iniset $TEMPEST_CONFIG whitebox-database user $DATABASE_USER iniset $TEMPEST_CONFIG whitebox-database password $DATABASE_PASSWORD diff --git a/devstack/settings b/devstack/settings index e5a13ca1..44881a7d 100644 --- a/devstack/settings +++ b/devstack/settings @@ -6,15 +6,7 @@ WHITEBOX_FILE_BACKED_MEMORY_SIZE=${WHITEBOX_FILE_BACKED_MEMORY_SIZE:-8192} WHITEBOX_RX_QUEUE_SIZE=${WHITEBOX_RX_QUEUE_SIZE:-1024} WHITEBOX_DEFAULT_VIDEO_MODEL=${WHITEBOX_DEFAULT_VIDEO_MODEL:-'virtio'} WHITEBOX_MAX_DISK_DEVICES_TO_ATTACH=${WHITEBOX_MAX_DISK_DEVICES_TO_ATTACH:-7} - -WHITEBOX_NOVA_COMPUTE_CONFIG_PATH=${WHITEBOX_NOVA_COMPUTE_CONFIG_PATH:-/etc/nova/nova-cpu.conf} -WHITEBOX_NOVA_COMPUTE_STOP_COMMAND=${WHITEBOX_NOVA_COMPUTE_STOP_COMMAND:-'systemctl stop devstack@n-cpu'} -WHITEBOX_NOVA_COMPUTE_START_COMMAND=${WHITEBOX_NOVA_COMPUTE_START_COMMAND:-'systemctl start devstack@n-cpu'} - -WHITEBOX_LIBVIRT_START_COMMAND=${WHITEBOX_LIBVIRT_START_COMMAND:-'systemctl start libvirtd'} -WHITEBOX_LIBVIRT_STOP_COMMAND=${WHITEBOX_LIBVIRT_STOP_COMMAND:-'systemctl stop libvirtd'} -WHITEBOX_LIBVIRT_MASK_COMMAND=${WHITEBOX_LIBVIRT_MASK_COMMAND:-'systemctl mask libvirtd'} -WHITEBOX_LIBVIRT_UNMASK_COMMAND=${WHITEBOX_LIBVIRT_UNMASK_COMMAND:-'systemctl unmask libvirtd'} +WHITEBOX_NODES_YAML=${WHITEBOX_NODES_YAML:-'/home/zuul/compute_nodes.yaml'} WHITEBOX_CPU_TOPOLOGY=${WHITEBOX_CPU_TOPOLOGY:-''} WHITEBOX_DEDICATED_CPUS_PER_NUMA=${WHITEBOX_DEDICATED_CPUS_PER_NUMA:-4} diff --git a/playbooks/templates/compute_nodes.yaml.j2 b/playbooks/templates/compute_nodes.yaml.j2 new file mode 100644 index 00000000..addc33f7 --- /dev/null +++ b/playbooks/templates/compute_nodes.yaml.j2 @@ -0,0 +1,13 @@ +{% for compute in computes -%} +{{ compute }}: + services: + libvirt: + start_command: 'systemctl start libvirtd' + stop_command: 'systemctl stop libvirtd' + mask_command: 'systemctl mask libvirtd' + unmask_command: 'systemctl unmask libvirtd' + nova-compute: + config_path: '/etc/nova/nova-cpu.conf' + start_command: 'systemctl start devstack@n-cpu' + stop_command: 'systemctl stop devstack@n-cpu' +{% endfor %} \ No newline at end of file diff --git a/playbooks/whitebox/pre.yaml b/playbooks/whitebox/pre.yaml index 55921a40..ba4dbe4b 100644 --- a/playbooks/whitebox/pre.yaml +++ b/playbooks/whitebox/pre.yaml @@ -26,4 +26,22 @@ name: copy-build-sshkey vars: ansible_become: yes - copy_sshkey_target_user: 'tempest' \ No newline at end of file + copy_sshkey_target_user: 'tempest' + + - name: Collect compute hostnames + set_fact: + computes: "{{ ansible_play_hosts_all|map('extract', hostvars, 'ansible_fqdn')|list }}" + run_once: true + + - name: Render compute_nodes.yaml template + template: + src: ../templates/compute_nodes.yaml.j2 + dest: /home/zuul/compute_nodes.yaml + run_once: true + delegate_to: controller + + - name: Output the rendered file at /home/zuul/compute_nodes.yaml + shell: | + cat /home/zuul/compute_nodes.yaml + run_once: true + delegate_to: controller \ No newline at end of file diff --git a/tox.ini b/tox.ini index 449bf47e..3e2552a9 100644 --- a/tox.ini +++ b/tox.ini @@ -1,6 +1,6 @@ [tox] minversion = 3.18.0 -envlist = pep8,py{36,38,39,310} +envlist = pep8 skip_missing_interpreters = True # Automatic envs (pyXX) will only use the python version appropriate to that # env and ignore basepython inherited from [testenv] if we set @@ -16,9 +16,6 @@ allowlist_externals = * deps = -r{toxinidir}/requirements.txt -r{toxinidir}/test-requirements.txt -commands = - find . -type f -name "*.pyc" -delete - stestr run {posargs} [testenv:pep8] commands = diff --git a/whitebox_tempest_plugin/api/compute/base.py b/whitebox_tempest_plugin/api/compute/base.py index cfe6b324..84a0f4ff 100644 --- a/whitebox_tempest_plugin/api/compute/base.py +++ b/whitebox_tempest_plugin/api/compute/base.py @@ -24,7 +24,6 @@ from tempest.lib.common.utils import data_utils from tempest.lib.common.utils import test_utils from whitebox_tempest_plugin.services import clients -from whitebox_tempest_plugin import utils as whitebox_utils if six.PY2: import contextlib2 as contextlib @@ -125,17 +124,15 @@ class BaseWhiteboxComputeTest(base.BaseV2ComputeAdminTest): def get_server_xml(self, server_id): server = self.os_admin.servers_client.show_server(server_id)['server'] host = server['OS-EXT-SRV-ATTR:host'] - cntrlplane_addr = whitebox_utils.get_ctlplane_address(host) server_instance_name = server['OS-EXT-SRV-ATTR:instance_name'] - virshxml = clients.VirshXMLClient(cntrlplane_addr) + virshxml = clients.VirshXMLClient(host) xml = virshxml.dumpxml(server_instance_name) return ET.fromstring(xml) def get_server_blockdevice_path(self, server_id, device_name): host = self.get_host_for_server(server_id) - cntrlplane_addr = whitebox_utils.get_ctlplane_address(host) - virshxml = clients.VirshXMLClient(cntrlplane_addr) + virshxml = clients.VirshXMLClient(host) blklist = virshxml.domblklist(server_id).splitlines() source = None for line in blklist: diff --git a/whitebox_tempest_plugin/api/compute/test_cpu_pinning.py b/whitebox_tempest_plugin/api/compute/test_cpu_pinning.py index 9c002e4e..fd898966 100644 --- a/whitebox_tempest_plugin/api/compute/test_cpu_pinning.py +++ b/whitebox_tempest_plugin/api/compute/test_cpu_pinning.py @@ -277,7 +277,6 @@ class CPUThreadPolicyTest(BasePinningTest): """ siblings = {} - host = whitebox_utils.get_ctlplane_address(host) virshxml = clients.VirshXMLClient(host) capxml = virshxml.capabilities() root = ET.fromstring(capxml) diff --git a/whitebox_tempest_plugin/api/compute/test_rbd_direct_download.py b/whitebox_tempest_plugin/api/compute/test_rbd_direct_download.py index 3f8dc8f1..722f688a 100644 --- a/whitebox_tempest_plugin/api/compute/test_rbd_direct_download.py +++ b/whitebox_tempest_plugin/api/compute/test_rbd_direct_download.py @@ -19,7 +19,6 @@ from tempest import config from whitebox_tempest_plugin.api.compute import base from whitebox_tempest_plugin.services import clients -from whitebox_tempest_plugin.utils import get_ctlplane_address CONF = config.CONF LOG = logging.getLogger(__name__) @@ -66,8 +65,7 @@ class TestRBDDirectDownload(base.BaseWhiteboxComputeTest): log_query_string = f"Attempting to export RBD image: " \ f"[[]pool_name: {rbd_pool}[]] [[]image_uuid: " \ f"{image_id}[]]" - host_ip = get_ctlplane_address(host) - logs_client = clients.LogParserClient(host_ip) + logs_client = clients.LogParserClient(host) # Assert if log with specified image is found self.assertTrue(len(logs_client.parse(log_query_string))) path = self.get_server_blockdevice_path(server['id'], 'vda') diff --git a/whitebox_tempest_plugin/api/compute/test_volume_encryption.py b/whitebox_tempest_plugin/api/compute/test_volume_encryption.py index b73201e5..f6aad8c6 100644 --- a/whitebox_tempest_plugin/api/compute/test_volume_encryption.py +++ b/whitebox_tempest_plugin/api/compute/test_volume_encryption.py @@ -18,7 +18,6 @@ from tempest import config from whitebox_tempest_plugin.api.compute import base from whitebox_tempest_plugin.services.clients import QEMUImgClient -from whitebox_tempest_plugin.utils import get_ctlplane_address CONF = config.CONF LOG = logging.getLogger(__name__) @@ -107,7 +106,7 @@ class TestQEMUVolumeEncryption(base.BaseWhiteboxComputeTest): # Get volume details from qemu-img info with the previously generated # volume path - host = get_ctlplane_address(self.get_host_for_server(server['id'])) + host = self.get_host_for_server(server['id']) qemu_img_client = QEMUImgClient(host) qemu_info = qemu_img_client.info(path) diff --git a/whitebox_tempest_plugin/api/compute/test_volume_negative.py b/whitebox_tempest_plugin/api/compute/test_volume_negative.py index 0b5143ec..043fc584 100644 --- a/whitebox_tempest_plugin/api/compute/test_volume_negative.py +++ b/whitebox_tempest_plugin/api/compute/test_volume_negative.py @@ -22,7 +22,6 @@ 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 @@ -71,8 +70,7 @@ class VolumesAdminNegativeTest(base.BaseWhiteboxComputeTest, self.assertGreater( len(disks_after_attach), len(disks_before_attach)) - host = whitebox_utils.get_ctlplane_address( - self.get_host_for_server(server['id'])) + host = self.get_host_for_server(server['id']) with clients.ServiceManager(host, 'libvirt').stopped(): # While this call to n-api will return successfully the underlying diff --git a/whitebox_tempest_plugin/config.py b/whitebox_tempest_plugin/config.py index 657de166..7a02cf11 100644 --- a/whitebox_tempest_plugin/config.py +++ b/whitebox_tempest_plugin/config.py @@ -22,6 +22,9 @@ general_group = cfg.OptGroup( title='General Whitebox Tempest plugin config options') general_opts = [ + cfg.StrOpt( + 'nodes_yaml', + help='File path to the yaml description file of the compute hosts '), cfg.StrOpt( 'ctlplane_ssh_username', help='Username to use when accessing controllers and/or compute hosts ' @@ -133,59 +136,13 @@ nova_compute_group = cfg.OptGroup( title='Config options to manage the nova-compute service') nova_compute_opts = [ - cfg.StrOpt( - 'config_path', - help='Path to the configuration file for the nova-compute service.'), - cfg.StrOpt( - 'start_command', - help='Command to start the nova-compute service, without any ' - 'privilege management (ie, no sudo).'), - cfg.StrOpt( - 'stop_command', - help='Command to stop the nova-compute service, without any ' - 'privilege management (ie, no sudo).'), cfg.StrOpt( 'log_query_command', default="journalctl", choices=["journalctl", "zgrep"], help="Name of the utility to run LogParserClient commands. " "Currently, supported values are 'journalctl' (default) " - "for devstack and 'zgrep' for TripleO"), -] - -libvirt_group = cfg.OptGroup( - name='whitebox-libvirt', - title='Config options to manage the libvirt service') - -libvirt_opts = [ - cfg.StrOpt( - 'start_command', - help='Command to start the libvirt service, without any ' - 'privilege management (ie, no sudo).'), - cfg.StrOpt( - 'stop_command', - help='Command to stop the libvirt service, without any ' - 'privilege management (ie, no sudo).', - deprecated_opts=[cfg.DeprecatedOpt('stop_command', - group='whitebox-nova-libvirt')]), - cfg.StrOpt( - 'mask_command', - help='In some situations (Ubuntu Focal, for example), libvirtd can ' - 'be activated by other systemd units even if it is stopped. ' - 'In such cases, it can be useful to mask a service (ie, disable ' - 'it completely) to prevent it from being started outside of our ' - 'control. This config options sets the command to mask libvirt. ' - 'If set, it will be executed after every stop command.'), - cfg.StrOpt( - 'unmask_command', - help='Similar to the mask_command option, this config options sets ' - 'the command to unmask libvirt. If set, it will be run before ' - 'every start command.'), - cfg.StrOpt( - 'libvirt_container_name', - default="nova_libvirt", - help='The container name to use when needing to interact with the ' - 'respective virsh command of the compute host'), + "for devstack and 'zgrep' for TripleO") ] database_group = cfg.OptGroup( diff --git a/whitebox_tempest_plugin/plugin.py b/whitebox_tempest_plugin/plugin.py index f93644fb..e36f6a7d 100644 --- a/whitebox_tempest_plugin/plugin.py +++ b/whitebox_tempest_plugin/plugin.py @@ -38,8 +38,6 @@ class WhiteboxTempestPlugin(plugins.TempestPlugin): whitebox_config.nova_compute_opts) config.register_opt_group(conf, whitebox_config.database_group, whitebox_config.database_opts) - config.register_opt_group(conf, whitebox_config.libvirt_group, - whitebox_config.libvirt_opts) config.register_opt_group(conf, whitebox_config.hardware_group, whitebox_config.hardware_opts) config.register_opt_group(conf, config.compute_features_group, @@ -50,8 +48,6 @@ class WhiteboxTempestPlugin(plugins.TempestPlugin): whitebox_config.general_opts), (whitebox_config.nova_compute_group.name, whitebox_config.nova_compute_opts), - (whitebox_config.libvirt_group.name, - whitebox_config.libvirt_opts), (whitebox_config.database_group.name, whitebox_config.database_opts), (whitebox_config.hardware_group.name, diff --git a/whitebox_tempest_plugin/services/clients.py b/whitebox_tempest_plugin/services/clients.py index 0c01ab66..9d29d975 100644 --- a/whitebox_tempest_plugin/services/clients.py +++ b/whitebox_tempest_plugin/services/clients.py @@ -36,10 +36,11 @@ LOG = logging.getLogger(__name__) class SSHClient(object): """A client to execute remote commands, based on tempest.lib.common.ssh.""" - def __init__(self, ctlplane_address): + def __init__(self, host): self.ssh_key = CONF.whitebox.ctlplane_ssh_private_key_path self.ssh_user = CONF.whitebox.ctlplane_ssh_username - self.ctlplane_address = ctlplane_address + self.host_parameters = whitebox_utils.get_host_details(host) + self.ctlplane_address = whitebox_utils.get_ctlplane_address(host) def execute(self, command, container_name=None, sudo=False): ssh_client = ssh.Client(self.ctlplane_address, self.ssh_user, @@ -59,9 +60,12 @@ class SSHClient(object): class VirshXMLClient(SSHClient): """A client to obtain libvirt XML from a remote host.""" - def __init__(self, ctlplane_address): - super(VirshXMLClient, self).__init__(ctlplane_address) - self.container_name = CONF.whitebox_libvirt.libvirt_container_name + def __init__(self, host): + super(VirshXMLClient, self).__init__(host) + service_dict = self.host_parameters.get('services', {}).get('libvirt') + if service_dict is None: + raise exceptions.MissingServiceSectionException(service='libvirt') + self.container_name = service_dict.get('container_name') def dumpxml(self, domain): command = 'virsh dumpxml %s' % domain @@ -96,7 +100,10 @@ class QEMUImgClient(SSHClient): def __init__(self, ctlplane_address): super(QEMUImgClient, self).__init__(ctlplane_address) - self.container_name = CONF.whitebox_libvirt.libvirt_container_name + service_dict = self.host_parameters.get('services', {}).get('libvirt') + if service_dict is None: + raise exceptions.MissingServiceSectionException(service='libvirt') + self.container_name = service_dict.get('container_name') def info(self, path): command = 'qemu-img info --output=json --force-share %s' % path @@ -120,15 +127,15 @@ class ServiceManager(SSHClient): this must match the binary in the Nova os-services API. """ super(ServiceManager, self).__init__(hostname) - conf = getattr(CONF, 'whitebox-%s' % service, None) - if conf is None: + service_dict = self.host_parameters.get('services', {}).get(service) + if service_dict is None: raise exceptions.MissingServiceSectionException(service=service) self.service = service - self.config_path = getattr(conf, 'config_path', None) - self.start_command = getattr(conf, 'start_command', None) - self.stop_command = getattr(conf, 'stop_command', None) - self.mask_command = getattr(conf, 'mask_command', None) - self.unmask_command = getattr(conf, 'unmask_command', None) + self.config_path = service_dict.get('config_path') + self.start_command = service_dict.get('start_command') + self.stop_command = service_dict.get('stop_command') + self.mask_command = service_dict.get('mask_command') + self.unmask_command = service_dict.get('unmask_command') @contextlib.contextmanager def config_options(self, *opts): @@ -222,10 +229,7 @@ class NovaServiceManager(ServiceManager): """ def __init__(self, host, service, services_client): - super(NovaServiceManager, self).__init__( - whitebox_utils.get_ctlplane_address(host), - service - ) + super(NovaServiceManager, self).__init__(host, service) self.services_client = services_client self.host = host diff --git a/whitebox_tempest_plugin/utils.py b/whitebox_tempest_plugin/utils.py index d90d8f4a..1e94afe4 100644 --- a/whitebox_tempest_plugin/utils.py +++ b/whitebox_tempest_plugin/utils.py @@ -17,6 +17,7 @@ import six from oslo_serialization import jsonutils from tempest import config from whitebox_tempest_plugin import exceptions +import yaml if six.PY2: import contextlib2 as contextlib @@ -24,6 +25,7 @@ else: import contextlib CONF = config.CONF +_nodes = None def normalize_json(json): @@ -70,3 +72,12 @@ def get_ctlplane_address(compute_hostname): return CONF.whitebox.ctlplane_addresses[compute_hostname] raise exceptions.CtrlplaneAddressResolutionError(host=compute_hostname) + + +def get_host_details(host): + global _nodes + if _nodes is None: + nodes_location = CONF.whitebox.nodes_yaml + with open(nodes_location, "r") as f: + _nodes = yaml.safe_load(f) + return _nodes.get(host)