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
This commit is contained in:
James Parker 2023-05-30 13:59:01 -04:00 committed by Artom Lifshitz
parent 9fd80a8b5a
commit 3fe1d72fa6
14 changed files with 76 additions and 105 deletions

View File

@ -18,15 +18,7 @@ function configure {
iniset $TEMPEST_CONFIG whitebox rx_queue_size $WHITEBOX_RX_QUEUE_SIZE 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 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 max_disk_devices_to_attach $WHITEBOX_MAX_DISK_DEVICES_TO_ATTACH
iniset $TEMPEST_CONFIG whitebox nodes_yaml $WHITEBOX_NODES_YAML
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-database user $DATABASE_USER iniset $TEMPEST_CONFIG whitebox-database user $DATABASE_USER
iniset $TEMPEST_CONFIG whitebox-database password $DATABASE_PASSWORD iniset $TEMPEST_CONFIG whitebox-database password $DATABASE_PASSWORD

View File

@ -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_RX_QUEUE_SIZE=${WHITEBOX_RX_QUEUE_SIZE:-1024}
WHITEBOX_DEFAULT_VIDEO_MODEL=${WHITEBOX_DEFAULT_VIDEO_MODEL:-'virtio'} WHITEBOX_DEFAULT_VIDEO_MODEL=${WHITEBOX_DEFAULT_VIDEO_MODEL:-'virtio'}
WHITEBOX_MAX_DISK_DEVICES_TO_ATTACH=${WHITEBOX_MAX_DISK_DEVICES_TO_ATTACH:-7} WHITEBOX_MAX_DISK_DEVICES_TO_ATTACH=${WHITEBOX_MAX_DISK_DEVICES_TO_ATTACH:-7}
WHITEBOX_NODES_YAML=${WHITEBOX_NODES_YAML:-'/home/zuul/compute_nodes.yaml'}
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_CPU_TOPOLOGY=${WHITEBOX_CPU_TOPOLOGY:-''} WHITEBOX_CPU_TOPOLOGY=${WHITEBOX_CPU_TOPOLOGY:-''}
WHITEBOX_DEDICATED_CPUS_PER_NUMA=${WHITEBOX_DEDICATED_CPUS_PER_NUMA:-4} WHITEBOX_DEDICATED_CPUS_PER_NUMA=${WHITEBOX_DEDICATED_CPUS_PER_NUMA:-4}

View File

@ -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 %}

View File

@ -27,3 +27,21 @@
vars: vars:
ansible_become: yes ansible_become: yes
copy_sshkey_target_user: 'tempest' 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

View File

@ -1,6 +1,6 @@
[tox] [tox]
minversion = 3.18.0 minversion = 3.18.0
envlist = pep8,py{36,38,39,310} envlist = pep8
skip_missing_interpreters = True skip_missing_interpreters = True
# Automatic envs (pyXX) will only use the python version appropriate to that # Automatic envs (pyXX) will only use the python version appropriate to that
# env and ignore basepython inherited from [testenv] if we set # env and ignore basepython inherited from [testenv] if we set
@ -16,9 +16,6 @@ allowlist_externals = *
deps = deps =
-r{toxinidir}/requirements.txt -r{toxinidir}/requirements.txt
-r{toxinidir}/test-requirements.txt -r{toxinidir}/test-requirements.txt
commands =
find . -type f -name "*.pyc" -delete
stestr run {posargs}
[testenv:pep8] [testenv:pep8]
commands = commands =

View File

@ -24,7 +24,6 @@ from tempest.lib.common.utils import data_utils
from tempest.lib.common.utils import test_utils from tempest.lib.common.utils import test_utils
from whitebox_tempest_plugin.services import clients from whitebox_tempest_plugin.services import clients
from whitebox_tempest_plugin import utils as whitebox_utils
if six.PY2: if six.PY2:
import contextlib2 as contextlib import contextlib2 as contextlib
@ -125,17 +124,15 @@ class BaseWhiteboxComputeTest(base.BaseV2ComputeAdminTest):
def get_server_xml(self, server_id): def get_server_xml(self, server_id):
server = self.os_admin.servers_client.show_server(server_id)['server'] server = self.os_admin.servers_client.show_server(server_id)['server']
host = server['OS-EXT-SRV-ATTR:host'] 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'] 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) xml = virshxml.dumpxml(server_instance_name)
return ET.fromstring(xml) return ET.fromstring(xml)
def get_server_blockdevice_path(self, server_id, device_name): def get_server_blockdevice_path(self, server_id, device_name):
host = self.get_host_for_server(server_id) host = self.get_host_for_server(server_id)
cntrlplane_addr = whitebox_utils.get_ctlplane_address(host) virshxml = clients.VirshXMLClient(host)
virshxml = clients.VirshXMLClient(cntrlplane_addr)
blklist = virshxml.domblklist(server_id).splitlines() blklist = virshxml.domblklist(server_id).splitlines()
source = None source = None
for line in blklist: for line in blklist:

View File

@ -277,7 +277,6 @@ class CPUThreadPolicyTest(BasePinningTest):
""" """
siblings = {} siblings = {}
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)

View File

@ -19,7 +19,6 @@ 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.utils import get_ctlplane_address
CONF = config.CONF CONF = config.CONF
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
@ -66,8 +65,7 @@ class TestRBDDirectDownload(base.BaseWhiteboxComputeTest):
log_query_string = f"Attempting to export RBD image: " \ log_query_string = f"Attempting to export RBD image: " \
f"[[]pool_name: {rbd_pool}[]] [[]image_uuid: " \ f"[[]pool_name: {rbd_pool}[]] [[]image_uuid: " \
f"{image_id}[]]" f"{image_id}[]]"
host_ip = get_ctlplane_address(host) logs_client = clients.LogParserClient(host)
logs_client = clients.LogParserClient(host_ip)
# Assert if log with specified image is found # Assert if log with specified image is found
self.assertTrue(len(logs_client.parse(log_query_string))) self.assertTrue(len(logs_client.parse(log_query_string)))
path = self.get_server_blockdevice_path(server['id'], 'vda') path = self.get_server_blockdevice_path(server['id'], 'vda')

View File

@ -18,7 +18,6 @@ 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.clients import QEMUImgClient from whitebox_tempest_plugin.services.clients import QEMUImgClient
from whitebox_tempest_plugin.utils import get_ctlplane_address
CONF = config.CONF CONF = config.CONF
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
@ -107,7 +106,7 @@ class TestQEMUVolumeEncryption(base.BaseWhiteboxComputeTest):
# Get volume details from qemu-img info with the previously generated # Get volume details from qemu-img info with the previously generated
# volume path # 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_img_client = QEMUImgClient(host)
qemu_info = qemu_img_client.info(path) qemu_info = qemu_img_client.info(path)

View File

@ -22,7 +22,6 @@ 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
@ -71,8 +70,7 @@ 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 = whitebox_utils.get_ctlplane_address( host = self.get_host_for_server(server['id'])
self.get_host_for_server(server['id']))
with clients.ServiceManager(host, 'libvirt').stopped(): with clients.ServiceManager(host, 'libvirt').stopped():
# While this call to n-api will return successfully the underlying # While this call to n-api will return successfully the underlying

View File

@ -22,6 +22,9 @@ general_group = cfg.OptGroup(
title='General Whitebox Tempest plugin config options') title='General Whitebox Tempest plugin config options')
general_opts = [ general_opts = [
cfg.StrOpt(
'nodes_yaml',
help='File path to the yaml description file of the compute hosts '),
cfg.StrOpt( cfg.StrOpt(
'ctlplane_ssh_username', 'ctlplane_ssh_username',
help='Username to use when accessing controllers and/or compute hosts ' 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') title='Config options to manage the nova-compute service')
nova_compute_opts = [ 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( cfg.StrOpt(
'log_query_command', 'log_query_command',
default="journalctl", default="journalctl",
choices=["journalctl", "zgrep"], choices=["journalctl", "zgrep"],
help="Name of the utility to run LogParserClient commands. " help="Name of the utility to run LogParserClient commands. "
"Currently, supported values are 'journalctl' (default) " "Currently, supported values are 'journalctl' (default) "
"for devstack and 'zgrep' for TripleO"), "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'),
] ]
database_group = cfg.OptGroup( database_group = cfg.OptGroup(

View File

@ -38,8 +38,6 @@ class WhiteboxTempestPlugin(plugins.TempestPlugin):
whitebox_config.nova_compute_opts) whitebox_config.nova_compute_opts)
config.register_opt_group(conf, whitebox_config.database_group, config.register_opt_group(conf, whitebox_config.database_group,
whitebox_config.database_opts) 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, config.register_opt_group(conf, whitebox_config.hardware_group,
whitebox_config.hardware_opts) whitebox_config.hardware_opts)
config.register_opt_group(conf, config.compute_features_group, config.register_opt_group(conf, config.compute_features_group,
@ -50,8 +48,6 @@ class WhiteboxTempestPlugin(plugins.TempestPlugin):
whitebox_config.general_opts), whitebox_config.general_opts),
(whitebox_config.nova_compute_group.name, (whitebox_config.nova_compute_group.name,
whitebox_config.nova_compute_opts), whitebox_config.nova_compute_opts),
(whitebox_config.libvirt_group.name,
whitebox_config.libvirt_opts),
(whitebox_config.database_group.name, (whitebox_config.database_group.name,
whitebox_config.database_opts), whitebox_config.database_opts),
(whitebox_config.hardware_group.name, (whitebox_config.hardware_group.name,

View File

@ -36,10 +36,11 @@ 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, ctlplane_address): def __init__(self, host):
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.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): def execute(self, command, container_name=None, sudo=False):
ssh_client = ssh.Client(self.ctlplane_address, self.ssh_user, ssh_client = ssh.Client(self.ctlplane_address, self.ssh_user,
@ -59,9 +60,12 @@ class SSHClient(object):
class VirshXMLClient(SSHClient): class VirshXMLClient(SSHClient):
"""A client to obtain libvirt XML from a remote host.""" """A client to obtain libvirt XML from a remote host."""
def __init__(self, ctlplane_address): def __init__(self, host):
super(VirshXMLClient, self).__init__(ctlplane_address) super(VirshXMLClient, self).__init__(host)
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 dumpxml(self, domain): def dumpxml(self, domain):
command = 'virsh dumpxml %s' % domain command = 'virsh dumpxml %s' % domain
@ -96,7 +100,10 @@ class QEMUImgClient(SSHClient):
def __init__(self, ctlplane_address): def __init__(self, ctlplane_address):
super(QEMUImgClient, self).__init__(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): def info(self, path):
command = 'qemu-img info --output=json --force-share %s' % 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. this must match the binary in the Nova os-services API.
""" """
super(ServiceManager, self).__init__(hostname) super(ServiceManager, self).__init__(hostname)
conf = getattr(CONF, 'whitebox-%s' % service, None) service_dict = self.host_parameters.get('services', {}).get(service)
if conf is None: if service_dict is None:
raise exceptions.MissingServiceSectionException(service=service) raise exceptions.MissingServiceSectionException(service=service)
self.service = service self.service = service
self.config_path = getattr(conf, 'config_path', None) self.config_path = service_dict.get('config_path')
self.start_command = getattr(conf, 'start_command', None) self.start_command = service_dict.get('start_command')
self.stop_command = getattr(conf, 'stop_command', None) self.stop_command = service_dict.get('stop_command')
self.mask_command = getattr(conf, 'mask_command', None) self.mask_command = service_dict.get('mask_command')
self.unmask_command = getattr(conf, 'unmask_command', None) self.unmask_command = service_dict.get('unmask_command')
@contextlib.contextmanager @contextlib.contextmanager
def config_options(self, *opts): def config_options(self, *opts):
@ -222,10 +229,7 @@ class NovaServiceManager(ServiceManager):
""" """
def __init__(self, host, service, services_client): def __init__(self, host, service, services_client):
super(NovaServiceManager, self).__init__( super(NovaServiceManager, self).__init__(host, service)
whitebox_utils.get_ctlplane_address(host),
service
)
self.services_client = services_client self.services_client = services_client
self.host = host self.host = host

View File

@ -17,6 +17,7 @@ import six
from oslo_serialization import jsonutils from oslo_serialization import jsonutils
from tempest import config from tempest import config
from whitebox_tempest_plugin import exceptions from whitebox_tempest_plugin import exceptions
import yaml
if six.PY2: if six.PY2:
import contextlib2 as contextlib import contextlib2 as contextlib
@ -24,6 +25,7 @@ else:
import contextlib import contextlib
CONF = config.CONF CONF = config.CONF
_nodes = None
def normalize_json(json): def normalize_json(json):
@ -70,3 +72,12 @@ def get_ctlplane_address(compute_hostname):
return CONF.whitebox.ctlplane_addresses[compute_hostname] return CONF.whitebox.ctlplane_addresses[compute_hostname]
raise exceptions.CtrlplaneAddressResolutionError(host=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)