Add test for different amount of pinned CPU

This testgroup should be run with this parameters:

export KVM_USE=True
export SLAVE_NODE_CPU=6
export DRIVER_ENABLE_ACPI=true
export NUMA_NODES=2

Change-Id: Ida99b43992c854db33eab8c60ca52b0b73297f08
Implements: blueprint test-numa-cpu-pinning
This commit is contained in:
Ksenia Demina 2016-04-12 20:17:11 +03:00
parent 7113129b1a
commit 338c465370
3 changed files with 276 additions and 10 deletions

View File

@ -202,14 +202,26 @@ class Common(object):
self.nova.servers.delete(server) self.nova.servers.delete(server)
def create_flavor(self, name, ram, vcpus, disk, flavorid="auto", def create_flavor(self, name, ram, vcpus, disk, flavorid="auto",
ephemeral=0): ephemeral=0, extra_specs=None):
flavor = self.nova.flavors.create(name, ram, vcpus, disk, flavorid, flavor = self.nova.flavors.create(name, ram, vcpus, disk, flavorid,
ephemeral=ephemeral) ephemeral=ephemeral)
if extra_specs:
flavor.set_keys(extra_specs)
return flavor return flavor
def delete_flavor(self, flavor): def delete_flavor(self, flavor):
return self.nova.flavors.delete(flavor) return self.nova.flavors.delete(flavor)
def create_aggregate(self, name, availability_zone=None,
metadata=None, hosts=None):
aggregate = self.nova.aggregates.create(
name=name, availability_zone=availability_zone)
for host in hosts or []:
aggregate.add_host(host)
if metadata:
aggregate.set_metadata(metadata)
return aggregate
@staticmethod @staticmethod
def _get_keystoneclient(username, password, tenant_name, auth_url, def _get_keystoneclient(username, password, tenant_name, auth_url,
retries=3, ca_cert=None, insecure=False): retries=3, ca_cert=None, insecure=False):

View File

@ -30,13 +30,17 @@ TIME_ZONE = 'UTC'
ENV_NAME = os.environ.get("ENV_NAME", "fuel_system_test") ENV_NAME = os.environ.get("ENV_NAME", "fuel_system_test")
VIRTUAL_ENV = os.environ.get("VIRTUAL_ENV", "") VIRTUAL_ENV = os.environ.get("VIRTUAL_ENV", "")
ACPI_ENABLE = get_var_as_bool('DRIVER_ENABLE_ACPI', False)
nic_name_mask = 'enp0s{}' if not ACPI_ENABLE else 'ens{}'
INTERFACES_DICT = { INTERFACES_DICT = {
'eth0': os.environ.get('IFACE_0', 'enp0s3'), 'eth0': os.environ.get('IFACE_0', nic_name_mask.format(3)),
'eth1': os.environ.get('IFACE_1', 'enp0s4'), 'eth1': os.environ.get('IFACE_1', nic_name_mask.format(4)),
'eth2': os.environ.get('IFACE_2', 'enp0s5'), 'eth2': os.environ.get('IFACE_2', nic_name_mask.format(5)),
'eth3': os.environ.get('IFACE_3', 'enp0s6'), 'eth3': os.environ.get('IFACE_3', nic_name_mask.format(6)),
'eth4': os.environ.get('IFACE_4', 'enp0s7'), 'eth4': os.environ.get('IFACE_4', nic_name_mask.format(7)),
'eth5': os.environ.get('IFACE_5', 'enp0s8'), 'eth5': os.environ.get('IFACE_5', nic_name_mask.format(8)),
} }
@ -98,6 +102,7 @@ HARDWARE = {
"admin_node_memory": os.environ.get("ADMIN_NODE_MEMORY", 3072), "admin_node_memory": os.environ.get("ADMIN_NODE_MEMORY", 3072),
"admin_node_cpu": os.environ.get("ADMIN_NODE_CPU", 2), "admin_node_cpu": os.environ.get("ADMIN_NODE_CPU", 2),
"slave_node_cpu": os.environ.get("SLAVE_NODE_CPU", 1), "slave_node_cpu": os.environ.get("SLAVE_NODE_CPU", 1),
"numa_nodes": os.environ.get("NUMA_NODES", 0),
} }
if OPENSTACK_RELEASE_UBUNTU in OPENSTACK_RELEASE: if OPENSTACK_RELEASE_UBUNTU in OPENSTACK_RELEASE:
slave_mem_default = 2560 slave_mem_default = 2560

View File

@ -12,21 +12,101 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
import random
from proboscis import asserts from proboscis import asserts
from proboscis import test from proboscis import test
from fuelweb_test.helpers.decorators import log_snapshot_after_test from fuelweb_test.helpers.decorators import log_snapshot_after_test
from fuelweb_test.helpers import os_actions
from fuelweb_test.helpers import utils from fuelweb_test.helpers import utils
from fuelweb_test import logger from fuelweb_test import logger
from fuelweb_test import logwrap
from fuelweb_test import settings from fuelweb_test import settings
from fuelweb_test.tests.base_test_case import SetupEnvironment from fuelweb_test.tests.base_test_case import SetupEnvironment
from fuelweb_test.tests.base_test_case import TestBasic from fuelweb_test.tests.base_test_case import TestBasic
from gates_tests.helpers import exceptions
@test(groups=["numa_cpu_pinning"]) @test(groups=["numa_cpu_pinning"])
class NumaCpuPinning(TestBasic): class NumaCpuPinning(TestBasic):
"""NumaCpuPinning.""" """NumaCpuPinning."""
@staticmethod
@logwrap
def assert_entry_in_config(conf, conf_name, section, option, value):
"""Check entry of parameter with a proper value.
:param conf: a ConfigParser object
:param conf_name: a string of full file path
:param section: a string of section name in configuration file
:param option: a string of option name in configuration file
:param value: a string of value that has entry in configuration file
:return:
"""
current_value = conf.get(section, option)
asserts.assert_true(value in current_value,
'Expected that the option "{0}" contains value '
'"{1}" in config file "{2}", but actually has '
'value "{3}": FAIL'.format(option,
value,
conf_name,
current_value))
@staticmethod
@logwrap
def assert_quantity_in_config(conf, conf_name, section, option,
value):
"""Check number of parameters in option section.
:param conf: a ConfigParser object
:param conf_name: a string of full file path
:param section: a string of section name in configuration file
:param option: a string of option name in configuration file
:param value: an int number of values in specific option
:return:
"""
current_value = conf.get(section, option)
asserts.assert_equal(len(current_value.split(',')), value,
'Expected that the option "{0}" has "{1}"'
'values in config file {2} but actually has '
'value "{3}": FAIL'.format(option,
value,
conf_name,
current_value))
@logwrap
def create_pinned_instance(self, os_conn, cluster_id,
name, vcpus, hostname, meta):
"""Boot VM on specific compute with CPU pinning
:param os_conn: an object of connection to openstack services
:param cluster_id: an integer number of cluster id
:param name: a string name of flavor and aggregate
:param vcpus: an integer number of vcpus for flavor
:param hostname: a string fqdn name of compute
:param meta: a dict with metadata for aggregate
:return:
"""
os_conn.create_aggregate(name, metadata=meta, hosts=[hostname])
extra_specs = {'aggregate_instance_extra_specs:pinned': 'true',
'hw:cpu_policy': 'dedicated'}
net_name = self.fuel_web.get_cluster_predefined_networks_name(
cluster_id)['private_net']
flavor_id = random.randint(10, 10000)
flavor = os_conn.create_flavor(name=name, ram=64, vcpus=vcpus, disk=1,
flavorid=flavor_id,
extra_specs=extra_specs)
server = os_conn.create_server_for_migration(neutron=True,
label=net_name,
flavor=flavor_id)
os_conn.verify_instance_status(server, 'ACTIVE')
os_conn.delete_instance(server)
os_conn.delete_flavor(flavor)
@test(depends_on=[SetupEnvironment.prepare_slaves_5], @test(depends_on=[SetupEnvironment.prepare_slaves_5],
groups=["numa_cpu_pinning", groups=["numa_cpu_pinning",
"basic_env_for_numa_cpu_pinning"]) "basic_env_for_numa_cpu_pinning"])
@ -46,14 +126,54 @@ class NumaCpuPinning(TestBasic):
self.check_run(snapshot_name) self.check_run(snapshot_name)
self.env.revert_snapshot("ready_with_5_slaves") self.env.revert_snapshot("ready_with_5_slaves")
self.show_step(1, initialize=True) # TODO(kdemina) Use commomn function for variables asserts
if not settings.KVM_USE:
raise exceptions.FuelQAVariableNotSet(
'KVM_USE', 'true')
if int(settings.HARDWARE['slave_node_cpu']) < 6:
raise exceptions.FuelQAVariableNotSet(
'SLAVE_NODE_CPU', 6)
if int(settings.HARDWARE['numa_nodes']) < 2:
raise exceptions.FuelQAVariableNotSet(
'NUMA_NODES', 2)
if not settings.INTERFACES_DICT['eth0'] == 'ens3':
raise exceptions.FuelQAVariableNotSet(
'IFACE_0', 'ens3')
if not settings.INTERFACES_DICT['eth1'] == 'ens4':
raise exceptions.FuelQAVariableNotSet(
'IFACE_1', 'ens4')
if not settings.INTERFACES_DICT['eth2'] == 'ens5':
raise exceptions.FuelQAVariableNotSet(
'IFACE_2', 'ens5')
elif not settings.INTERFACES_DICT['eth3'] == 'ens6':
raise exceptions.FuelQAVariableNotSet(
'IFACE_3', 'ens6')
elif not settings.INTERFACES_DICT['eth4'] == 'ens7':
raise exceptions.FuelQAVariableNotSet(
'IFACE_4', 'ens7')
elif not settings.INTERFACES_DICT['eth5'] == 'ens8':
raise exceptions.FuelQAVariableNotSet(
'IFACE_5', 'ens8')
elif not settings.ACPI_ENABLE:
raise exceptions.FuelQAVariableNotSet(
'DRIVER_ENABLE_ACPI', 'true')
self.show_step(1)
cluster_id = self.fuel_web.create_cluster( cluster_id = self.fuel_web.create_cluster(
name=self.__class__.__name__, name=self.__class__.__name__,
mode=settings.DEPLOYMENT_MODE, mode=settings.DEPLOYMENT_MODE,
settings={ settings={
"net_provider": 'neutron', "net_provider": 'neutron',
"net_segment_type": settings.NEUTRON_SEGMENT_TYPE, "net_segment_type": settings.NEUTRON_SEGMENT_TYPE
"KVM_USE": True
} }
) )
self.show_step(2) self.show_step(2)
@ -95,3 +215,132 @@ class NumaCpuPinning(TestBasic):
logger.info("There is {0} NUMA nodes on node {1}".format( logger.info("There is {0} NUMA nodes on node {1}".format(
numas_on_remote, target_node['ip'])) numas_on_remote, target_node['ip']))
self.env.make_snapshot(snapshot_name, is_make=True) self.env.make_snapshot(snapshot_name, is_make=True)
@test(depends_on_groups=['basic_env_for_numa_cpu_pinning'],
groups=["cpu_pinning_on_two_compute"])
@log_snapshot_after_test
def cpu_pinning_on_two_compute(self):
"""Check different amount of pinned CPU
Scenario:
1. Revert snapshot "basic_env_for_numa_cpu_pinning"
2. Pin maximum CPU for the nova on the first compute
3. Pin minimun CPU for the nova on the second compute
4. Verify setting was successfully applied
5. Deploy cluster
6. Check new filters are enabled in nova.conf at controller
7. Check nova.conf contains pinned CPU at compute
8. Run OSTF
9. Boot VM with pinned CPU on the first compute
10. Boot VM with pinned CPU on the second compute
Snapshot: cpu_pinning_on_two_compute
"""
self.show_step(1)
self.env.revert_snapshot("basic_env_for_numa_cpu_pinning")
cluster_id = self.fuel_web.get_last_created_cluster()
self.show_step(2)
first_compute = self.fuel_web.get_nailgun_node_by_name('slave-01')
first_compute_cpu = first_compute['meta']['cpu']['total']
first_config = self.fuel_web.client.get_node_attributes(
first_compute['id'])
first_config['cpu_pinning']['nova']['value'] = first_compute_cpu - 1
self.fuel_web.client.upload_node_attributes(
first_config, first_compute['id'])
self.show_step(3)
second_compute = self.fuel_web.get_nailgun_node_by_name('slave-02')
second_config = self.fuel_web.client.get_node_attributes(
second_compute['id'])
second_config['cpu_pinning']['nova']['value'] = 1
self.fuel_web.client.upload_node_attributes(
second_config, second_compute['id'])
self.show_step(4)
first_config = self.fuel_web.client.get_node_attributes(
first_compute['id'])
asserts.assert_equal(
first_config['cpu_pinning']['nova']['value'],
first_compute_cpu - 1,
"CPU pinning wasn't applied on '{0}': "
"Expected value '{1}', actual '{2}'"
.format(first_compute['ip'], first_compute_cpu - 1,
first_config['cpu_pinning']['nova']['value']))
second_config = self.fuel_web.client.get_node_attributes(
second_compute['id'])
asserts.assert_equal(
second_config['cpu_pinning']['nova']['value'],
1,
"CPU pinning wasn't applied on '{0}': "
"Expected value '{1}', actual '{2}'"
.format(second_compute['ip'], 1,
second_config['cpu_pinning']['nova']['value']))
self.show_step(5)
self.fuel_web.deploy_cluster_wait(cluster_id)
self.show_step(6)
controllers = self.fuel_web.get_nailgun_cluster_nodes_by_roles(
cluster_id,
roles=['controller'])
nova_conf_path = "/etc/nova/nova.conf"
for controller in controllers:
nova_conf = utils.get_ini_config(self.ssh_manager.open_on_remote(
ip=controller['ip'],
path=nova_conf_path))
self.assert_entry_in_config(nova_conf,
nova_conf_path,
"DEFAULT",
"scheduler_default_filters",
"NUMATopologyFilter")
self.show_step(7)
nova_conf = utils.get_ini_config(self.ssh_manager.open_on_remote(
ip=first_compute['ip'],
path=nova_conf_path))
self.assert_quantity_in_config(nova_conf,
nova_conf_path,
"DEFAULT",
"vcpu_pin_set",
first_compute_cpu - 1)
nova_conf = utils.get_ini_config(self.ssh_manager.open_on_remote(
ip=second_compute['ip'],
path=nova_conf_path))
self.assert_quantity_in_config(nova_conf,
nova_conf_path,
"DEFAULT",
"vcpu_pin_set",
1)
self.show_step(8)
self.fuel_web.run_ostf(cluster_id=cluster_id)
self.show_step(9)
os_conn = os_actions.OpenStackActions(
self.fuel_web.get_public_vip(cluster_id))
meta = {'pinned': 'true'}
self.create_pinned_instance(os_conn=os_conn,
cluster_id=cluster_id,
name='cpu_3',
vcpus=3,
hostname=first_compute['fqdn'],
meta=meta)
self.show_step(10)
self.create_pinned_instance(os_conn=os_conn,
cluster_id=cluster_id,
name='cpu_1',
vcpus=1,
hostname=second_compute['fqdn'],
meta=meta)
self.env.make_snapshot("cpu_pinning_on_two_compute")