Rework 'test_numa_topology'

This is a little cludgy and could make better use of existing test
classes found in Tempest. The main changes are:

- The test class is no longer unnecessarily split into a base class and
  test class
- The test class now inherits from 'base.BaseV2ComputeAdminTest' rather
  than from 'manager.NetworkScenarioTest', allowing us to remove a lot
  of boilerplate
- 'remote_client.RemoteClient' is subclassed to indicate how we're
   retrieving host NUMA topology
- Some variables are renamed to clarify their purpose.

This adds a dependency of the '[validation] run_validation' config
option, which must now be set to 'True' to ensure networks are
provisioned for guest instances that need them.

Change-Id: Idfa521e81aba93c863b8996b6f42645711312759
This commit is contained in:
Stephen Finucane 2016-12-09 13:59:01 +00:00 committed by Waldemar Znoinski
parent a8807613ad
commit ba71d6ed58
1 changed files with 79 additions and 102 deletions

View File

@ -14,17 +14,16 @@
# under the License.
from oslo_concurrency import processutils
from oslo_log import log as logging
from tempest.api.compute import base
from tempest.common.utils.linux import remote_client
from tempest import config
from tempest.lib.common.utils import data_utils
from tempest.scenario import manager
from tempest import test
import testtools
CONF = config.CONF
LOG = logging.getLogger(__name__)
def get_host_numa_placement(instance, vcpus):
"""Get placement of instance CPUs on host.
@ -54,34 +53,49 @@ def get_host_numa_placement(instance, vcpus):
return placement
class TestServerNumaBase(manager.NetworkScenarioTest):
credentials = ['admin']
class NUMARemoteClient(remote_client.RemoteClient):
def get_numa_topology(self):
nodes = []
node_count = self.exec_command(
'ls /sys/devices/system/node | grep node | wc -l')
for i in range(int(node_count)):
node_cmd = 'cat /sys/devices/system/node/node%d/' % i
node = {}
node['cpu'] = self.exec_command(node_cmd + 'cpulist')
node['mem'] = self.exec_command(node_cmd + 'meminfo')
nodes.append(node)
return nodes
class NUMAServersTest(base.BaseV2ComputeAdminTest):
disk_config = 'AUTO'
@classmethod
def setup_credentials(cls):
cls.prepare_instance_network()
super(NUMAServersTest, cls).setup_credentials()
@classmethod
def setup_clients(cls):
cls.manager = cls.admin_manager
super(manager.NetworkScenarioTest, cls).setup_clients()
# Use admin client by default
super(NUMAServersTest, cls).setup_clients()
cls.flavors_client = cls.os_adm.flavors_client
cls.client = cls.servers_client
def setUp(self):
super(TestServerNumaBase, self).setUp()
# Setup image and flavor the test instance
# Support both configured and injected values
self.image_ref = CONF.compute.image_ref
self.flavor_ref = CONF.compute.flavor_ref
self.run_ssh = CONF.validation.run_validation
self.ssh_user = CONF.validation.image_ssh_user
self.keypair = self.create_keypair()
@classmethod
def resource_setup(cls):
# TODO(stephenfin): Do we need this?
cls.set_validation_resources()
super(NUMAServersTest, cls).resource_setup()
LOG.debug('Starting test for i:{image}, f:{flavor}. '
'Run ssh: {ssh}, user: {ssh_user}'.format(
image=self.image_ref, flavor=self.flavor_ref,
ssh=self.run_ssh, ssh_user=self.ssh_user))
def create_flavor_with_numa(self):
flavor_with_numa = data_utils.rand_name('numa_flavor')
flavor_with_numa_id = data_utils.rand_int_id(start=1000)
def create_flavor(self):
flavor_name = data_utils.rand_name('numa_flavor')
flavor_id = data_utils.rand_int_id(start=1000)
# TODO(stephenfin): Consider dropping this to 512 or similar
ram = 2048
vcpus = 4
disk = 0
@ -90,90 +104,53 @@ class TestServerNumaBase(manager.NetworkScenarioTest):
}
# Create a flavor with extra specs
resp = (self.flavors_client.create_flavor(name=flavor_with_numa,
ram=ram, vcpus=vcpus,
disk=disk,
id=flavor_with_numa_id))
self.flavors_client.set_flavor_extra_spec(flavor_with_numa_id,
**extra_specs)
self.addCleanup(self.flavor_clean_up, flavor_with_numa_id)
self.assertEqual(200, resp.response.status)
flavor = self.flavors_client.create_flavor(name=flavor_name, ram=ram,
vcpus=vcpus, disk=disk,
id=flavor_id)['flavor']
self.flavors_client.set_flavor_extra_spec(flavor['id'], **extra_specs)
self.addCleanup(self.flavor_clean_up, flavor['id'])
return flavor_with_numa_id
return flavor['id']
def flavor_clean_up(self, flavor_id):
resp = self.flavors_client.delete_flavor(flavor_id)
self.assertEqual(resp.response.status, 202)
self.flavors_client.delete_flavor(flavor_id)
self.flavors_client.wait_for_resource_deletion(flavor_id)
def boot_instance(self):
# Create server with image and flavor from input scenario
security_groups = [{'name': self.security_group['name']}]
create_kwargs = {
'key_name': self.keypair['name'],
'security_groups': security_groups
}
flavor = self.create_flavor_with_numa()
self.instance = self.create_server(
image_id=self.image_ref,
flavor=flavor,
@testtools.skipUnless(CONF.validation.run_validation,
'Instance validation tests are disabled.')
def test_verify_created_server_numa_topology(self):
"""Smoke test NUMA support.
Validates NUMA support by launching an instance with a NUMA
topology defined and validating the correctness of the topology
on both host and guest.
"""
flavor_id = self.create_flavor()
admin_pass = self.image_ssh_password
server = self.create_test_server(
validatable=True,
wait_until='ACTIVE',
**create_kwargs)
adminPass=admin_pass,
flavor=flavor_id)
def verify_ssh(self):
# Obtain a floating IP
floating_ip = self.compute_floating_ips_client.create_floating_ip()[
'floating_ip']
self.addCleanup(self.compute_floating_ips_client.delete_floating_ip,
floating_ip['id'])
# Attach a floating IP
self.compute_floating_ips_client.associate_floating_ip_to_server(
floating_ip['ip'], self.instance['id'])
# Check ssh
return self.get_remote_client(
ip_address=floating_ip['ip'],
username='cirros',
private_key=self.keypair['private_key'])
server = self.client.show_server(server['id'])['server']
linux_client = NUMARemoteClient(
self.get_server_ip(server),
self.ssh_user,
admin_pass,
self.validation_resources['keypair']['private_key'],
server=server,
servers_client=self.client)
# Validate guest topology
# TODO(stephenfin): Validate more of the NUMA topology than this
numa_nodes = linux_client.get_numa_nodes()
self.assertEqual(2, len(numa_nodes))
class TestServerNumaTopo(TestServerNumaBase):
"""
This smoke test case follows this basic set of operations:
* Create a keypair for use in launching an instance
* Create a security group to control network access in instance
* Add simple permissive rules to the security group
* Launch an instance with numa topology defined
* Perform ssh to instance
* Get numa topology from VM, check correctness
* Get numa placement info for VM from HOST
* Check if placement is correct
* Terminate the instance
"""
def get_numa_topology(self, rmt):
topo = {'nodes': []}
nodes = int(rmt.exec_command("ls /sys/devices/system/node"
" | grep node | wc -l"))
for i in range(nodes):
node = {}
node['cpu'] = rmt.exec_command("cat /sys/devices/system/node/"
"node%s/cpulist" % i)
node['mem'] = rmt.exec_command("cat /sys/devices/system/node/"
"node%s/meminfo" % i)
topo["nodes"].append(node)
return topo
@test.services('compute', 'network')
def test_server_numa(self):
self.security_group = self._create_security_group()
self.boot_instance()
rmt_client = self.verify_ssh()
topo = self.get_numa_topology(rmt_client)
self.assertEqual(2, len(topo['nodes']))
self.assertNotEqual(None, rmt_client)
placement = get_host_numa_placement(self.instance, 4)
# Validate host topology
placement = self.get_placement(server, 4)
self.assertEqual(placement[0], placement[1])
self.assertNotEqual(placement[1], placement[2])
self.assertEqual(placement[2], placement[3])
self.servers_client.delete_server(self.instance['id'])