Improve base compute and utils helper organization

Move helpers out of base compute class that do not actually need a
test class, and create a new hardware.py file to keep helpers that
have to do with hardware.

Change-Id: I7a8ae901dce68f4d858aa2062820fd7405c87f45
This commit is contained in:
Artom Lifshitz 2021-08-19 15:23:18 -04:00
parent a3ff33907a
commit 4491e3db8a
6 changed files with 150 additions and 108 deletions

View File

@ -23,6 +23,7 @@ from tempest import config
from tempest.lib.common.utils import data_utils
from tempest.lib.common.utils import test_utils
from whitebox_tempest_plugin import hardware
from whitebox_tempest_plugin.services import clients
from whitebox_tempest_plugin import utils as whitebox_utils
@ -142,29 +143,14 @@ class BaseWhiteboxComputeTest(base.BaseV2ComputeAdminTest):
self.assertEqual(target_host, self.get_host_for_server(server_id),
msg)
def get_all_cpus(self):
"""Aggregate the dictionary values of [whitebox]/cpu_topology from
tempest.conf into a list of pCPU ids.
"""
topology_dict = CONF.whitebox_hardware.cpu_topology
cpus = []
[cpus.extend(c) for c in topology_dict.values()]
return cpus
def get_pinning_as_set(self, server_id):
pinset = set()
root = self.get_server_xml(server_id)
vcpupins = root.findall('./cputune/vcpupin')
for pin in vcpupins:
pinset |= whitebox_utils.parse_cpu_spec(pin.get('cpuset'))
pinset |= hardware.parse_cpu_spec(pin.get('cpuset'))
return pinset
def _get_cpu_spec(self, cpu_list):
"""Returns a libvirt-style CPU spec from the provided list of integers. For
example, given [0, 2, 3], returns "0,2,3".
"""
return ','.join(map(str, cpu_list))
# TODO(lyarwood): Refactor all of this into a common module between
# tempest.api.{compute,volume} and tempest.scenario.manager where this
# has been copied from to avoid mixing api and scenario classes.

View File

@ -37,6 +37,7 @@ from tempest.exceptions import BuildErrorException
from tempest.lib import decorators
from whitebox_tempest_plugin.api.compute import base
from whitebox_tempest_plugin import hardware
from whitebox_tempest_plugin.services import clients
from whitebox_tempest_plugin import utils as whitebox_utils
@ -65,7 +66,7 @@ class BasePinningTest(base.BaseWhiteboxComputeTest):
cell_pins = {}
for memnode in memnodes:
cell_pins[int(memnode.get('cellid'))] = \
whitebox_utils.parse_cpu_spec(memnode.get('nodeset'))
hardware.parse_cpu_spec(memnode.get('nodeset'))
return cell_pins
@ -82,7 +83,7 @@ class BasePinningTest(base.BaseWhiteboxComputeTest):
emulator_threads = set()
for pin in emulatorpins:
emulator_threads |= \
whitebox_utils.parse_cpu_spec(pin.get('cpuset'))
hardware.parse_cpu_spec(pin.get('cpuset'))
return emulator_threads
@ -413,7 +414,7 @@ class EmulatorExtraCPUTest(BasePinningTest):
emulatorpins = root.findall('./cputune/emulatorpin')
emulator_threads = set()
for pin in emulatorpins:
emulator_threads |= whitebox_utils.parse_cpu_spec(
emulator_threads |= hardware.parse_cpu_spec(
pin.get('cpuset'))
return emulator_threads
@ -438,10 +439,10 @@ class EmulatorExtraCPUTest(BasePinningTest):
if len(CONF.whitebox_hardware.cpu_topology[self.numa_to_use]) < 3:
raise self.skipException('Test requires NUMA Node with 3 or more '
'CPUs to run')
dedicated_set = self._get_cpu_spec(
dedicated_set = hardware.format_cpu_spec(
CONF.whitebox_hardware.cpu_topology[self.numa_to_use][:2])
cpu_shared_set_str = self._get_cpu_spec(
cpu_shared_set_str = hardware.format_cpu_spec(
CONF.whitebox_hardware.cpu_topology[self.numa_to_use][2:])
hostname = self.list_compute_hosts()[0]
@ -477,7 +478,7 @@ class EmulatorExtraCPUTest(BasePinningTest):
# Confirm the emulator threads from server's A and B are both equal
# to cpu_shared_set
cpu_shared_set = whitebox_utils.parse_cpu_spec(cpu_shared_set_str)
cpu_shared_set = hardware.parse_cpu_spec(cpu_shared_set_str)
self.assertEqual(
emulator_threads_a, cpu_shared_set,
'Emulator threads for server A %s are not the same as CPU set '
@ -504,7 +505,7 @@ class EmulatorExtraCPUTest(BasePinningTest):
raise self.skipException('Test requires NUMA Node with 2 or more '
'CPUs to run')
dedicated_set = self._get_cpu_spec(
dedicated_set = hardware.format_cpu_spec(
CONF.whitebox_hardware.cpu_topology[self.numa_to_use][:2])
hostname = self.list_compute_hosts()[0]
host_sm = clients.NovaServiceManager(
@ -575,7 +576,7 @@ class EmulatorExtraCPUTest(BasePinningTest):
'CPUs to run')
dedicated_set = \
set(CONF.whitebox_hardware.cpu_topology[self.numa_to_use])
dedicated_set_str = self._get_cpu_spec(dedicated_set)
dedicated_set_str = hardware.format_cpu_spec(dedicated_set)
hostname = self.list_compute_hosts()[0]
host_sm = clients.NovaServiceManager(
@ -652,7 +653,7 @@ class EmulatorExtraCPUTest(BasePinningTest):
if len(CONF.whitebox_hardware.cpu_topology[self.numa_to_use]) < 2:
raise self.skipException('Test requires NUMA Node with 2 or more '
'CPUs to run')
dedicated_set = self._get_cpu_spec(
dedicated_set = hardware.format_cpu_spec(
CONF.whitebox_hardware.cpu_topology[self.numa_to_use][:2])
hostname = self.list_compute_hosts()[0]
@ -805,7 +806,7 @@ class NUMALiveMigrationBase(BasePinningTest):
"""
root = self.get_server_xml(server_id)
cpuset = root.find('./vcpu').attrib.get('cpuset', None)
return whitebox_utils.parse_cpu_spec(cpuset)
return hardware.parse_cpu_spec(cpuset)
def _get_hugepage_xml_element(self, server_id):
"""Gather and return all instances of the page element from XML element
@ -872,9 +873,9 @@ class NUMALiveMigrationTest(NUMALiveMigrationBase):
self.os_admin.services_client)
with whitebox_utils.multicontext(
host1_sm.config_options(('DEFAULT', 'vcpu_pin_set',
self._get_cpu_spec(topo_1[0]))),
hardware.format_cpu_spec(topo_1[0]))),
host2_sm.config_options(('DEFAULT', 'vcpu_pin_set',
self._get_cpu_spec(topo_2[0])))
hardware.format_cpu_spec(topo_2[0])))
):
# Boot 2 servers such that their vCPUs "fill" a NUMA node.
specs = {'hw:cpu_policy': 'dedicated'}
@ -920,7 +921,7 @@ class NUMALiveMigrationTest(NUMALiveMigrationBase):
topo_a = numaclient_a.get_host_topology()
with host_a_sm.config_options(
('DEFAULT', 'vcpu_pin_set',
self._get_cpu_spec(topo_a[0] + topo_a[1]))
hardware.format_cpu_spec(topo_a[0] + topo_a[1]))
):
self.live_migrate(server_b['id'], host_a, 'ACTIVE')
@ -1141,7 +1142,7 @@ class NUMACPUDedicatedLiveMigrationTest(NUMALiveMigrationBase):
raise cls.skipException(msg)
def test_collocation_migration(self):
cpu_list = self.get_all_cpus()
cpu_list = hardware.get_all_cpus()
if len(cpu_list) < 4:
raise self.skipException('Requires at least 4 pCPUs to run')
@ -1165,16 +1166,18 @@ class NUMACPUDedicatedLiveMigrationTest(NUMALiveMigrationBase):
self.os_admin.services_client)
with whitebox_utils.multicontext(
host1_sm.config_options(('compute', 'cpu_dedicated_set',
self._get_cpu_spec(host1_dedicated_set)),
('compute', 'cpu_shared_set',
self._get_cpu_spec(host1_shared_set))
),
host2_sm.config_options(('compute', 'cpu_dedicated_set',
self._get_cpu_spec(host2_dedicated_set)),
('compute', 'cpu_shared_set',
self._get_cpu_spec(host2_shared_set))
)
host1_sm.config_options(
('compute', 'cpu_dedicated_set',
hardware.format_cpu_spec(host1_dedicated_set)),
('compute', 'cpu_shared_set',
hardware.format_cpu_spec(host1_shared_set))
),
host2_sm.config_options(
('compute', 'cpu_dedicated_set',
hardware.format_cpu_spec(host2_dedicated_set)),
('compute', 'cpu_shared_set',
hardware.format_cpu_spec(host2_shared_set))
)
):
# Create a total of four instances, with each compute host holding
# a server with a cpu_dedicated policy and a server that will

View File

@ -22,6 +22,7 @@ from tempest import config
from tempest.lib import decorators
from whitebox_tempest_plugin.api.compute import base
from whitebox_tempest_plugin import hardware
from whitebox_tempest_plugin.services import clients
from whitebox_tempest_plugin import utils as whitebox_utils
@ -107,7 +108,7 @@ class LiveMigrationAndReboot(LiveMigrationBase):
def _migrate_and_reboot_instance(self, section, cpu_set_parameter):
flavor_vcpu_size = 2
cpu_list = self.get_all_cpus()
cpu_list = hardware.get_all_cpus()
if len(cpu_list) < 4:
raise self.skipException('Requires 4 or more pCPUs to execute '
'the test')
@ -130,10 +131,12 @@ class LiveMigrationAndReboot(LiveMigrationBase):
self.os_admin.services_client)
with whitebox_utils.multicontext(
host1_sm.config_options((section, cpu_set_parameter,
self._get_cpu_spec(host1_dedicated_set))),
host2_sm.config_options((section, cpu_set_parameter,
self._get_cpu_spec(host2_dedicated_set)))
host1_sm.config_options(
(section, cpu_set_parameter,
hardware.format_cpu_spec(host1_dedicated_set))),
host2_sm.config_options(
(section, cpu_set_parameter,
hardware.format_cpu_spec(host2_dedicated_set)))
):
# Create a server with a dedicated cpu policy
server = self.create_test_server(

View File

@ -18,6 +18,7 @@ from tempest import exceptions as tempest_exc
from tempest.lib.common.utils import data_utils
from whitebox_tempest_plugin.api.compute import base
from whitebox_tempest_plugin import hardware
from whitebox_tempest_plugin.services import clients
from oslo_log import log as logging
@ -287,7 +288,7 @@ class SRIOVNumaAffinity(SRIOVBase):
cpu_dedicated_set = \
CONF.whitebox_hardware.cpu_topology[affinity_node] + \
CONF.whitebox_hardware.cpu_topology[second_node]
cpu_dedicated_str = self._get_cpu_spec(cpu_dedicated_set)
cpu_dedicated_str = hardware.format_cpu_spec(cpu_dedicated_set)
host_sm = clients.NovaServiceManager(host,
'nova-compute',
@ -376,7 +377,7 @@ class SRIOVNumaAffinity(SRIOVBase):
# Node
cpu_dedicated_set = CONF.whitebox_hardware.cpu_topology[
str(CONF.whitebox_hardware.physnet_numa_affinity)]
cpu_dedicated_str = self._get_cpu_spec(cpu_dedicated_set)
cpu_dedicated_str = hardware.format_cpu_spec(cpu_dedicated_set)
host_sm = clients.NovaServiceManager(host, 'nova-compute',
self.os_admin.services_client)

View File

@ -0,0 +1,110 @@
# Copyright 2020 Red Hat
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
from tempest import config
from whitebox_tempest_plugin import exceptions
CONF = config.CONF
def get_all_cpus():
"""Aggregate the dictionary values of [whitebox]/cpu_topology from
tempest.conf into a list of pCPU ids.
"""
topology_dict = CONF.whitebox_hardware.cpu_topology
cpus = []
[cpus.extend(c) for c in topology_dict.values()]
return cpus
def parse_cpu_spec(spec):
"""Parse a CPU set specification.
NOTE(artom): This has been lifted from Nova with minor
exceptions-related adjustments.
Each element in the list is either a single CPU number, a range of
CPU numbers, or a caret followed by a CPU number to be excluded
from a previous range.
:param spec: cpu set string eg "1-4,^3,6"
:returns: a set of CPU indexes
"""
cpuset_ids = set()
cpuset_reject_ids = set()
for rule in spec.split(','):
rule = rule.strip()
# Handle multi ','
if len(rule) < 1:
continue
# Note the count limit in the .split() call
range_parts = rule.split('-', 1)
if len(range_parts) > 1:
reject = False
if range_parts[0] and range_parts[0][0] == '^':
reject = True
range_parts[0] = str(range_parts[0][1:])
# So, this was a range; start by converting the parts to ints
try:
start, end = [int(p.strip()) for p in range_parts]
except ValueError:
raise exceptions.InvalidCPUSpec(spec=spec)
# Make sure it's a valid range
if start > end:
raise exceptions.InvalidCPUSpec(spec=spec)
# Add available CPU ids to set
if not reject:
cpuset_ids |= set(range(start, end + 1))
else:
cpuset_reject_ids |= set(range(start, end + 1))
elif rule[0] == '^':
# Not a range, the rule is an exclusion rule; convert to int
try:
cpuset_reject_ids.add(int(rule[1:].strip()))
except ValueError:
raise exceptions.InvalidCPUSpec(spec=spec)
else:
# OK, a single CPU to include; convert to int
try:
cpuset_ids.add(int(rule))
except ValueError:
raise exceptions.InvalidCPUSpec(spec=spec)
# Use sets to handle the exclusion rules for us
cpuset_ids -= cpuset_reject_ids
return cpuset_ids
def format_cpu_spec(cpu_list):
"""Returns a libvirt-style CPU spec from the provided list of integers. For
example, given [0, 2, 3], returns "0,2,3".
"""
return ','.join(map(str, cpu_list))
def get_pci_address(domain, bus, slot, func):
"""Assembles PCI address components into a fully-specified PCI address.
NOTE(jparker): This has been lifted from nova.pci.utils with no
adjustments
Does not validate that the components are valid hex or wildcard values.
:param domain, bus, slot, func: Hex or wildcard strings.
:return: A string of the form "<domain>:<bus>:<slot>.<function>".
"""
return '%s:%s:%s.%s' % (domain, bus, slot, func)

View File

@ -70,64 +70,3 @@ def get_ctlplane_address(compute_hostname):
return CONF.whitebox.ctlplane_addresses[compute_hostname]
raise exceptions.CtrlplaneAddressResolutionError(host=compute_hostname)
def parse_cpu_spec(spec):
"""Parse a CPU set specification.
NOTE(artom): This has been lifted from Nova with minor
exceptions-related adjustments.
Each element in the list is either a single CPU number, a range of
CPU numbers, or a caret followed by a CPU number to be excluded
from a previous range.
:param spec: cpu set string eg "1-4,^3,6"
:returns: a set of CPU indexes
"""
cpuset_ids = set()
cpuset_reject_ids = set()
for rule in spec.split(','):
rule = rule.strip()
# Handle multi ','
if len(rule) < 1:
continue
# Note the count limit in the .split() call
range_parts = rule.split('-', 1)
if len(range_parts) > 1:
reject = False
if range_parts[0] and range_parts[0][0] == '^':
reject = True
range_parts[0] = str(range_parts[0][1:])
# So, this was a range; start by converting the parts to ints
try:
start, end = [int(p.strip()) for p in range_parts]
except ValueError:
raise exceptions.InvalidCPUSpec(spec=spec)
# Make sure it's a valid range
if start > end:
raise exceptions.InvalidCPUSpec(spec=spec)
# Add available CPU ids to set
if not reject:
cpuset_ids |= set(range(start, end + 1))
else:
cpuset_reject_ids |= set(range(start, end + 1))
elif rule[0] == '^':
# Not a range, the rule is an exclusion rule; convert to int
try:
cpuset_reject_ids.add(int(rule[1:].strip()))
except ValueError:
raise exceptions.InvalidCPUSpec(spec=spec)
else:
# OK, a single CPU to include; convert to int
try:
cpuset_ids.add(int(rule))
except ValueError:
raise exceptions.InvalidCPUSpec(spec=spec)
# Use sets to handle the exclusion rules for us
cpuset_ids -= cpuset_reject_ids
return cpuset_ids