Migrate sriov provider and routed prov net tests
Moved downstream tests with minor adjustments, changed uuids. Added a config value to disable/enable the tests when needed. Reimplemented function that retrieves data from nova database in order to fit podified environment. All tests will be skipped on devstack environments. Change-Id: I4557207e397271430ec209a44d0ff62e5cf5116b
This commit is contained in:
parent
ca0fa6fe11
commit
9bb38100ba
@ -264,12 +264,13 @@ class BaseTempestWhiteboxTestCase(base.BaseTempestTestCase):
|
||||
|
||||
@classmethod
|
||||
def get_pod_of_service(cls, service='neutron'):
|
||||
# (rsafrono) at this moment only neutron service pod handled
|
||||
# since it's the only that existing tests are using
|
||||
pods_list = "oc get pods"
|
||||
if service == 'neutron':
|
||||
return cls.proxy_host_client.exec_command(
|
||||
"oc get pods | grep neutron | grep -v meta | "
|
||||
"cut -d' ' -f1").strip()
|
||||
filters = "grep neutron | grep -v meta | cut -d' ' -f1"
|
||||
else:
|
||||
filters = "grep {} | cut -d' ' -f1".format(service)
|
||||
return cls.proxy_host_client.exec_command(
|
||||
"{} | {}".format(pods_list, filters)).strip()
|
||||
|
||||
@classmethod
|
||||
def get_configs_of_service(cls, service='neutron'):
|
||||
|
@ -0,0 +1,851 @@
|
||||
# Copyright 2024 Red Hat, Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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.
|
||||
import re
|
||||
import time
|
||||
|
||||
import netaddr
|
||||
from neutron_lib import constants as lib_constants
|
||||
from neutron_tempest_plugin.common import ssh
|
||||
from neutron_tempest_plugin import config
|
||||
from neutron_tempest_plugin.scenario import constants
|
||||
from oslo_log import log
|
||||
from oslo_serialization import jsonutils
|
||||
from tempest.common.utils.linux import remote_client
|
||||
from tempest.common import waiters
|
||||
from tempest import exceptions
|
||||
from tempest.lib import decorators
|
||||
from tempest.lib import exceptions as lib_exc
|
||||
import testtools
|
||||
|
||||
from whitebox_neutron_tempest_plugin.common import utils
|
||||
from whitebox_neutron_tempest_plugin.tests.scenario import base
|
||||
|
||||
|
||||
CONF = config.CONF
|
||||
WB_CONF = config.CONF.whitebox_neutron_plugin_options
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
||||
class ProviderNetworkSriovBaseTest(base.ProviderBaseTest):
|
||||
"""Base class for SRIOV tests using provisioning networks
|
||||
Admin user is needed to create ports on the existing provisioning network
|
||||
"""
|
||||
MAC_RE = re.compile(r'([0-9a-f]{2}(?::[0-9a-f]{2}){5})', re.IGNORECASE)
|
||||
# Interface names might match any of these patterns
|
||||
# Examples:
|
||||
# 1) PF: p5p7, p2p1; VF: p5p7_2, p2p1_0
|
||||
# 2) PF: enp6s0f3, enp1s1f2; VF: enp6s0f3v4, enp1s1f2v1
|
||||
# 3) PF: enp6s0, enp1s1; VF: enp6s0v4, enp1s1v1
|
||||
PF_NAME_REGEX1 = re.compile(r'p\d+p\d+')
|
||||
VF_NAME_SEPARATOR1 = '_'
|
||||
PF_NAME_REGEX2 = re.compile(r'enp\d+s\d+f\d+')
|
||||
VF_NAME_SEPARATOR2 = 'v'
|
||||
PF_NAME_REGEX3 = re.compile(r'enp\d+s\d+')
|
||||
VF_NAME_SEPARATOR3 = 'v'
|
||||
PF_VF_REGEX_LIST = [{'pf_name_regex': PF_NAME_REGEX1,
|
||||
'vf_name_separator': VF_NAME_SEPARATOR1},
|
||||
{'pf_name_regex': PF_NAME_REGEX2,
|
||||
'vf_name_separator': VF_NAME_SEPARATOR2},
|
||||
{'pf_name_regex': PF_NAME_REGEX3,
|
||||
'vf_name_separator': VF_NAME_SEPARATOR3}]
|
||||
|
||||
@classmethod
|
||||
def skip_checks(cls):
|
||||
super(ProviderNetworkSriovBaseTest, cls).skip_checks()
|
||||
if not (CONF.neutron_plugin_options.advanced_image_ref or
|
||||
CONF.neutron_plugin_options.default_image_is_advanced):
|
||||
raise cls.skipException(
|
||||
'Advanced image is required to run these tests.')
|
||||
if WB_CONF.openstack_type == 'devstack':
|
||||
raise cls.skipException("The tests are currently not supported "
|
||||
"on devstack environment")
|
||||
|
||||
@classmethod
|
||||
def setup_clients(cls):
|
||||
super(ProviderNetworkSriovBaseTest, cls).setup_clients()
|
||||
cls.client = cls.os_adm.network_client
|
||||
cls.keypairs_client = cls.os_adm.keypairs_client
|
||||
cls.servers_client = cls.os_adm.servers_client
|
||||
|
||||
@classmethod
|
||||
def resource_setup(cls):
|
||||
super(ProviderNetworkSriovBaseTest, cls).resource_setup()
|
||||
if not cls.has_sriov_support:
|
||||
raise cls.skipException("Skipped because SRIOV is not supported")
|
||||
# info about SRIOV MACs
|
||||
cls._get_initial_sriov_macs()
|
||||
# initialize mysql nova command
|
||||
cls._get_nova_db_cmd()
|
||||
# verify number of available VF and PF before tests start
|
||||
cls._check_vfpf_nova_db()
|
||||
# set image and flavor in case default image is not advanced
|
||||
if not CONF.neutron_plugin_options.default_image_is_advanced:
|
||||
cls.flavor_ref = \
|
||||
CONF.neutron_plugin_options.advanced_image_flavor_ref
|
||||
cls.image_ref = CONF.neutron_plugin_options.advanced_image_ref
|
||||
cls.username = CONF.neutron_plugin_options.advanced_image_ssh_user
|
||||
|
||||
@classmethod
|
||||
def _get_nova_db_cmd(cls):
|
||||
# check that number of available VF and PF ports is correct before
|
||||
# any server has been created
|
||||
# (rsafrono) This code below experimental.
|
||||
# I tried to avoid hard-coding existing cell1 that exist on VA1
|
||||
# podified environment since the name can probably change
|
||||
# or more than one cell will be supported in the future
|
||||
nova_scheduler_pod = cls.get_pod_of_service("nova-scheduler")
|
||||
cells = cls.proxy_host_client.exec_command(
|
||||
"oc rsh {} nova-manage cell_v2 list_hosts | grep compute | "
|
||||
"tr -d '|' | tr -s ' ' ".format(nova_scheduler_pod) + "| "
|
||||
"awk '{print $1}' | uniq").strip().split()
|
||||
if len(cells) != 1:
|
||||
cls.fail("Currently only environments with a single cell "
|
||||
"are supported")
|
||||
galera_pod = cls.get_pod_of_service(
|
||||
'openstack-{}-galera-0'.format(cells[0]))
|
||||
galera_db_exec = "oc rsh {}".format(galera_pod)
|
||||
data_filter = ".data.Nova{}DatabasePassword|base64decode".format(
|
||||
cells[0].capitalize())
|
||||
db_password = cls.proxy_host_client.exec_command(
|
||||
"oc get secret osp-secret -o go-template --template="
|
||||
"\"{{" + data_filter + "}}\"").strip()
|
||||
db_credentials = "-u root -p{}".format(db_password)
|
||||
mysql_cmd = ('mysql --skip-column-names {} nova_{} -e '
|
||||
'"select pci_stats from compute_nodes;"'.format(
|
||||
db_credentials, cells[0]))
|
||||
cls.nova_db_cmd = "{} {} ".format(galera_db_exec, mysql_cmd)
|
||||
|
||||
@classmethod
|
||||
def _check_vfpf_nova_db(cls, timeout=60, interval=5):
|
||||
def _get_nova_objects():
|
||||
output = cls.run_on_master_controller(cls.nova_db_cmd)
|
||||
LOG.debug("pci_stats obtained from nova DB:")
|
||||
LOG.debug(output)
|
||||
output_rows = output.splitlines()
|
||||
# number of expected rows equals number of compute nodes
|
||||
assert (len(output_rows) == CONF.compute.min_compute_nodes)
|
||||
nova_objects = [
|
||||
jsonutils.loads(row)['nova_object.data']['objects']
|
||||
for row in output_rows
|
||||
]
|
||||
return nova_objects
|
||||
# check all PF and all VF are available
|
||||
db_checkings = []
|
||||
start = time.time()
|
||||
while time.time() - start < timeout:
|
||||
nova_objects = _get_nova_objects()
|
||||
db_checkings = [False for i in range(len(nova_objects))]
|
||||
for i, nova_object in enumerate(nova_objects):
|
||||
# each nova_object includes:
|
||||
# - an entry with information about the available PF ports
|
||||
# - an entry for each PF port, with information about their
|
||||
# available VF ports
|
||||
bool_nova_objects = len(nova_object) == (
|
||||
WB_CONF.sriov_pfs_per_host + 1)
|
||||
# initialize other booleans to False
|
||||
pf_bool_count = False
|
||||
vf_bool_count = False
|
||||
pf_found = False
|
||||
vf_found = False
|
||||
for pci_device_pool in nova_object:
|
||||
dev_type = pci_device_pool['nova_object.data']['tags'][
|
||||
'dev_type']
|
||||
count = pci_device_pool['nova_object.data']['count']
|
||||
if dev_type == 'type-PF':
|
||||
pf_found = True
|
||||
# expected available PF ports
|
||||
pf_bool_count = count == WB_CONF.sriov_pfs_per_host
|
||||
elif dev_type == 'type-VF':
|
||||
vf_found = True
|
||||
# expected available VF ports
|
||||
vf_bool_count = count == WB_CONF.sriov_vfs_per_pf
|
||||
else:
|
||||
raise RuntimeError('Unexpected dev_type %s' % dev_type)
|
||||
db_checkings[i] = (bool_nova_objects and pf_bool_count and
|
||||
vf_bool_count and pf_found and vf_found)
|
||||
if not all(checking for checking in db_checkings):
|
||||
time.sleep(interval)
|
||||
else:
|
||||
break
|
||||
# TODO(eolivare): uncomment this assert when rhbz#2101328 is resolved
|
||||
# assert(db_checkings and all(checking for checking in db_checkings))
|
||||
|
||||
@classmethod
|
||||
def _get_initial_sriov_macs(cls):
|
||||
cls.sriov_macs = {}
|
||||
compute_nodes = [node for node in cls.nodes
|
||||
if node['is_compute'] is True]
|
||||
cls.compute_ssh_clients = {}
|
||||
for node in compute_nodes:
|
||||
cls.sriov_macs[node['name']] = []
|
||||
cls.compute_ssh_clients.update(
|
||||
{node['name']: node['client']})
|
||||
nova_config_path = (
|
||||
'/var/lib/config-data/nova_libvirt/etc/nova/nova.conf')
|
||||
passthrough_whitelist_str = node['client'].exec_command(
|
||||
('sudo cat %s | grep "^passthrough_whitelist" | '
|
||||
'cut -d"=" -f2') % nova_config_path)
|
||||
for line in passthrough_whitelist_str.splitlines():
|
||||
passthrough_whitelist = eval(line.rstrip())
|
||||
pf_name = passthrough_whitelist.get('devname')
|
||||
trusted = passthrough_whitelist.get('trusted', 'false')
|
||||
cmd_pf = 'PATH=$PATH:/usr/sbin ip link show %s' % pf_name
|
||||
try:
|
||||
output_pf = node['client'].exec_command(
|
||||
cmd_pf).rstrip()
|
||||
except lib_exc.SSHExecCommandFailed:
|
||||
# This interface does not exist on current compute node
|
||||
continue
|
||||
else:
|
||||
# Multiple SRIOV interfaces can be found per compute node
|
||||
cls.sriov_macs[node['name']].append({
|
||||
'pf_name': pf_name,
|
||||
'trusted': trusted})
|
||||
mac_pf = re.findall(cls.MAC_RE, output_pf)[0]
|
||||
cls.sriov_macs[node['name']][-1].update(
|
||||
{pf_name: mac_pf})
|
||||
# Obtain MAC for each VF associated to the PF
|
||||
for i in range(WB_CONF.sriov_vfs_per_pf):
|
||||
cmd_vf = None
|
||||
for pf_vf_regex in cls.PF_VF_REGEX_LIST:
|
||||
if pf_vf_regex['pf_name_regex'].findall(pf_name):
|
||||
cmd_vf = (cmd_pf +
|
||||
pf_vf_regex['vf_name_separator'] +
|
||||
str(i))
|
||||
if cmd_vf is None:
|
||||
raise RuntimeError('Unexpected pf_name %s' % pf_name)
|
||||
output_vf = node['client'].exec_command(
|
||||
cmd_vf).rstrip()
|
||||
mac_vf = re.findall(cls.MAC_RE, output_vf)[0]
|
||||
cls.sriov_macs[node['name']][-1].update(
|
||||
{cmd_vf.split()[-1]: mac_vf})
|
||||
|
||||
def check_port_status(self, port_type,
|
||||
port_index=-1, server_index=-1):
|
||||
port_details = (super(ProviderNetworkSriovBaseTest, self)
|
||||
.check_port_status(port_type,
|
||||
port_index,
|
||||
server_index))
|
||||
self._check_port_mac(port_details=port_details,
|
||||
port_index=port_index,
|
||||
server_index=server_index)
|
||||
|
||||
@classmethod
|
||||
def resource_cleanup(cls):
|
||||
super(ProviderNetworkSriovBaseTest, cls).resource_cleanup()
|
||||
# verify number of available VF and PF after tests end
|
||||
cls._check_vfpf_nova_db()
|
||||
|
||||
def _test_create_instance_with_network_port(self, port_type,
|
||||
vm_name=None,
|
||||
reuse_port=False):
|
||||
security_groups, port, user_data, config_drive = \
|
||||
self._create_network_port(port_type,
|
||||
reuse_port=reuse_port,
|
||||
use_provider_net=True)
|
||||
vm_name = vm_name or "vm-" + port_type + "-" + self._testMethodName
|
||||
self._create_server(
|
||||
security_groups=security_groups, networks=[port],
|
||||
user_data=user_data, config_drive=config_drive,
|
||||
name=vm_name)
|
||||
|
||||
def _test_create_instance_with_two_ports(self, port_types, cidr,
|
||||
vm_name=None,
|
||||
reused_tenant_net=None):
|
||||
"""Two ports will be used for each VM. The first one will be connected
|
||||
to the existing provider network and the second one to a tenant network
|
||||
"""
|
||||
# Fist, create external port (connected to provider network)
|
||||
_, port1, user_data, config_drive = \
|
||||
self._create_network_port(port_types[0],
|
||||
use_provider_net=True)
|
||||
# Second, create internal port (connected to tenant network)
|
||||
# This port should not have a default route, hence its subnet is
|
||||
# created without a default gateway
|
||||
_, port2, user_data, config_drive = \
|
||||
self._create_network_port(port_types[1],
|
||||
use_provider_net=False,
|
||||
reused_tenant_net=reused_tenant_net,
|
||||
cidr=cidr,
|
||||
gateway=False)
|
||||
vm_name = vm_name or "vm-" + self._testMethodName
|
||||
self._create_server(
|
||||
networks=[port1, port2], user_data=user_data,
|
||||
config_drive=config_drive, name=vm_name)
|
||||
|
||||
def _test_instance_with_one_port_prov_net(self, port_type):
|
||||
self._test_create_instance_with_network_port(port_type)
|
||||
self.check_connectivity(self.ports[-1]['fixed_ips'][0]['ip_address'],
|
||||
self.username,
|
||||
self.keypair['private_key'])
|
||||
self.check_port_status(port_type)
|
||||
|
||||
def _check_port_mac(self, port_details, port_index=-1, server_index=-1):
|
||||
# MAC obtained via OSP API after server creation/migration/reboot
|
||||
port_mac_api = port_details['mac_address']
|
||||
# Initial port MAC assigned by neutron at port creation
|
||||
mac_neutron_port_creation = self.ports[port_index]['mac_address']
|
||||
# MAC obtained from VM instance
|
||||
vm_ip = port_details['fixed_ips'][0]['ip_address']
|
||||
cmd = 'PATH=$PATH:/usr/sbin ; ip link show %s' % \
|
||||
WB_CONF.default_instance_interface
|
||||
vm_ssh_client = ssh.Client(vm_ip,
|
||||
self.username,
|
||||
pkey=self.keypair['private_key'])
|
||||
output = vm_ssh_client.exec_command(cmd).rstrip()
|
||||
vm_mac = re.findall(self.MAC_RE, output)[0]
|
||||
self.assertEqual(port_mac_api, vm_mac)
|
||||
# MAC obtained from hypervisor
|
||||
server_id = self.servers[server_index]['id']
|
||||
host = self.servers_client.show_server(
|
||||
server_id)['server']['OS-EXT-SRV-ATTR:host'].split('.')[0]
|
||||
host_macs = self.sriov_macs[host]
|
||||
if port_details['binding:vnic_type'] in ('direct', 'macvtap'):
|
||||
vf_pci_slot = port_details['binding:profile']['pci_slot']
|
||||
vf_id = vf_pci_slot.split('.')[-1]
|
||||
pf_name = self._get_pf_name_from_vf(host, vf_pci_slot)
|
||||
host_macs_this_pf = None
|
||||
for host_macs_item in host_macs:
|
||||
if host_macs_item['pf_name'] == pf_name:
|
||||
host_macs_this_pf = host_macs_item
|
||||
self.assertIsNotNone(host_macs_this_pf)
|
||||
if host_macs_this_pf['trusted'].lower() == 'true':
|
||||
# Verify initial neutron MAC has not changed
|
||||
self.assertEqual(port_mac_api, mac_neutron_port_creation)
|
||||
# Updated VF MAC value applies when trusted (neutron applies
|
||||
# its own MAC value to the interface)
|
||||
vf_mac = self._get_updated_sriov_mac(host, host_macs_this_pf,
|
||||
vf_id=vf_id)
|
||||
else:
|
||||
# Verify initial neutron MAC has changed
|
||||
self.assertNotEqual(port_mac_api, mac_neutron_port_creation)
|
||||
# Initial VF MAC value applies when not trusted
|
||||
vf_name = None
|
||||
for pf_vf_regex in self.PF_VF_REGEX_LIST:
|
||||
if pf_vf_regex['pf_name_regex'].findall(pf_name):
|
||||
vf_name = (pf_name +
|
||||
pf_vf_regex['vf_name_separator'] +
|
||||
vf_id)
|
||||
if vf_name is None:
|
||||
raise RuntimeError('Unexpected pf_name %s' % pf_name)
|
||||
vf_mac = host_macs_this_pf[vf_name]
|
||||
self.assertEqual(port_mac_api, vf_mac)
|
||||
elif 'direct-physical' == port_details['binding:vnic_type']:
|
||||
# Verify initial neutron MAC has changed
|
||||
self.assertNotEqual(port_mac_api, mac_neutron_port_creation)
|
||||
# Initial PF MAC value applies
|
||||
pf_macs = [host_macs_item[host_macs_item['pf_name']]
|
||||
for host_macs_item in host_macs]
|
||||
self.assertIn(port_mac_api, pf_macs)
|
||||
# HV MAC does not need to be validated for normal ports
|
||||
|
||||
def _get_updated_sriov_mac(self, host, host_macs, vf_id=None):
|
||||
compute_ssh_client = self.compute_ssh_clients[host]
|
||||
cmd = 'PATH=$PATH:/usr/sbin ; ip link show %s' % host_macs['pf_name']
|
||||
if vf_id is not None:
|
||||
cmd += ' | grep "vf %s"' % vf_id
|
||||
output = compute_ssh_client.exec_command(cmd).rstrip()
|
||||
mac = re.findall(self.MAC_RE, output)[0]
|
||||
return mac
|
||||
|
||||
def _get_pf_name_from_vf(self, host, vf_pci_slot):
|
||||
compute_ssh_client = self.compute_ssh_clients[host]
|
||||
cmd = 'ls /sys/bus/pci/devices/%s/physfn/net' % vf_pci_slot
|
||||
output = compute_ssh_client.exec_command(cmd).rstrip()
|
||||
return output
|
||||
|
||||
|
||||
class OneServerProvNetSriovTest(ProviderNetworkSriovBaseTest):
|
||||
"""Class for tests creating one VM with one SRIOV port on provisioning
|
||||
networks
|
||||
These tests can be executed without needing to cleanup VMs or ports between
|
||||
them
|
||||
"""
|
||||
@decorators.idempotent_id('bd4d51e7-49d3-4b56-9ef6-7ba68761d122')
|
||||
def test_create_instance_with_normal_port_prov_net(self):
|
||||
self._test_instance_with_one_port_prov_net('normal')
|
||||
|
||||
@decorators.idempotent_id('a6df084c-b1ff-46a8-a269-47d3008c0572')
|
||||
def test_create_instance_with_direct_port_prov_net(self):
|
||||
self._test_instance_with_one_port_prov_net('direct')
|
||||
|
||||
@decorators.idempotent_id('fef94a15-ddf5-4094-9b3f-ecac370dafad')
|
||||
def test_create_instance_with_direct_physical_port_prov_net(self):
|
||||
self._test_instance_with_one_port_prov_net('direct-physical')
|
||||
|
||||
@decorators.idempotent_id('3f3a4ec4-9a92-41d3-836c-59b5809d1d18')
|
||||
def test_create_instance_with_macvtap_port_prov_net(self):
|
||||
self._test_instance_with_one_port_prov_net('macvtap')
|
||||
|
||||
|
||||
class ExtraDhcpOptsSriovTest(ProviderNetworkSriovBaseTest):
|
||||
"""Class for tests creating one VM with one SRIOV port on provisioning
|
||||
networks using extra_dhcp_options parameter at port creation
|
||||
These tests can be executed without needing to cleanup VMs or ports between
|
||||
them
|
||||
"""
|
||||
required_extensions = ['extra_dhcp_opt']
|
||||
extra_dhcp_opts = [
|
||||
{'opt_value': '8.8.8.8',
|
||||
'opt_name': 'dns-server',
|
||||
'ip_version': lib_constants.IP_VERSION_4},
|
||||
{'opt_value': '1600',
|
||||
'opt_name': 'mtu'}]
|
||||
|
||||
def _validate_dhcp_opts(self):
|
||||
vm_ip = self.ports[-1]['fixed_ips'][0]['ip_address']
|
||||
vm_ssh_client = ssh.Client(vm_ip,
|
||||
self.username,
|
||||
pkey=self.keypair['private_key'])
|
||||
if self.ports[-1]['binding:vnic_type'] == 'direct-physical':
|
||||
network_id = self.ports[-1]['network_id']
|
||||
vlan = self.client.show_network(
|
||||
network_id)['network']['provider:segmentation_id']
|
||||
else:
|
||||
vlan = None
|
||||
obtained_dhcp_options = utils.parse_dhcp_options_from_nmcli(
|
||||
vm_ssh_client, lib_constants.IP_VERSION_4, vlan=vlan)
|
||||
for extra_dhcp_opt in self.extra_dhcp_opts:
|
||||
self.assertIn(extra_dhcp_opt['opt_name'], obtained_dhcp_options)
|
||||
self.assertEqual(extra_dhcp_opt['opt_value'],
|
||||
obtained_dhcp_options[extra_dhcp_opt['opt_name']])
|
||||
|
||||
@decorators.idempotent_id('5f676e8a-3d74-49c7-ab87-1679b9b7155a')
|
||||
def test_extra_dhcp_opts_direct_port(self):
|
||||
self._test_instance_with_one_port_prov_net('direct')
|
||||
self._validate_dhcp_opts()
|
||||
|
||||
@decorators.idempotent_id('0bd26a9e-9c0f-40e4-a317-374ef48ac5b6')
|
||||
def test_extra_dhcp_opts_direct_physical_port(self):
|
||||
self._test_instance_with_one_port_prov_net('direct-physical')
|
||||
self._validate_dhcp_opts()
|
||||
|
||||
@decorators.idempotent_id('667df5dd-9302-4bfd-8d8c-7326abff2bb6')
|
||||
def test_extra_dhcp_opts_macvtap_port(self):
|
||||
self._test_instance_with_one_port_prov_net('macvtap')
|
||||
self._validate_dhcp_opts()
|
||||
|
||||
|
||||
class RebootServerProvNetSriovTest(ProviderNetworkSriovBaseTest):
|
||||
"""Class for SRIOV test that creates VM instances with different SRIOV port
|
||||
types, reboot those instances and checks their behavior is correct after
|
||||
reboot
|
||||
"""
|
||||
@decorators.idempotent_id('a44deae1-2f84-48fd-a644-faf6390e88e7')
|
||||
def test_reboot_vm_with_sriov_port_prov_net(self):
|
||||
for port_type in ('direct', 'direct-physical'):
|
||||
self._test_instance_with_one_port_prov_net(port_type)
|
||||
for reboot_type in ('SOFT', 'HARD'):
|
||||
self.servers_client.reboot_server(self.servers[-1]['id'],
|
||||
type=reboot_type)
|
||||
waiters.wait_for_server_status(self.servers_client,
|
||||
self.servers[-1]['id'],
|
||||
constants.SERVER_STATUS_ACTIVE)
|
||||
self.check_connectivity(
|
||||
self.ports[-1]['fixed_ips'][0]['ip_address'],
|
||||
self.username,
|
||||
self.keypair['private_key'])
|
||||
self.check_port_status(port_type)
|
||||
|
||||
|
||||
class UnshelveServerProvNetSriovTest(ProviderNetworkSriovBaseTest):
|
||||
"""Class for SRIOV test that creates VM instances with different SRIOV port
|
||||
types, shelves those instances, unshelves them and checks their behavior
|
||||
is correct after unshelve
|
||||
"""
|
||||
@decorators.idempotent_id('7b9d6590-c61e-46e1-834b-c02e6be71866')
|
||||
def test_unshelve_vm_with_sriov_port_prov_net(self):
|
||||
# TODO(eolivare): remove the skip exception once the BZ is fixed
|
||||
raise self.skipException("Not supported due to BZ1767797")
|
||||
offload_time = CONF.compute.shelved_offload_time
|
||||
for port_type in ('direct', 'direct-physical'):
|
||||
self._test_instance_with_one_port_prov_net(port_type)
|
||||
self.servers_client.shelve_server(self.servers[-1]['id'])
|
||||
waiters.wait_for_server_status(self.servers_client,
|
||||
self.servers[-1]['id'],
|
||||
'SHELVED_OFFLOADED',
|
||||
extra_timeout=offload_time)
|
||||
self.servers_client.unshelve_server(self.servers[-1]['id'])
|
||||
waiters.wait_for_server_status(self.servers_client,
|
||||
self.servers[-1]['id'],
|
||||
constants.SERVER_STATUS_ACTIVE)
|
||||
self.check_connectivity(
|
||||
self.ports[-1]['fixed_ips'][0]['ip_address'],
|
||||
self.username,
|
||||
self.keypair['private_key'])
|
||||
self.check_port_status(port_type)
|
||||
|
||||
|
||||
class ReusePortProvNetSriovTest(ProviderNetworkSriovBaseTest):
|
||||
"""Class for SRIOV test that creates VM instances with different SRIOV port
|
||||
types, delete those instances and reuse the existing SRIOV ports on new VM
|
||||
instances
|
||||
"""
|
||||
@decorators.idempotent_id('9ad4aed9-0985-49fa-886e-fa6286a26492')
|
||||
def test_reuse_sriov_port_prov_net(self):
|
||||
# TODO(eolivare): only VF ports supported at this moment
|
||||
# for port_type in ('direct', 'direct-physical'):
|
||||
for port_type in ('direct',):
|
||||
self._test_instance_with_one_port_prov_net(port_type)
|
||||
old_port_id = self.ports[-1]['id']
|
||||
old_num_ports = len(self.ports)
|
||||
# delete the server and create a new server attached to the
|
||||
# existing port
|
||||
self.servers_client.delete_server(self.servers[-1]['id'])
|
||||
waiters.wait_for_server_termination(self.servers_client,
|
||||
self.servers[-1]['id'])
|
||||
del self.servers[-1]
|
||||
# validate port is not reachable anymore
|
||||
self.assertRaises(
|
||||
lib_exc.SSHTimeout,
|
||||
self.check_connectivity,
|
||||
host=self.ports[-1]['fixed_ips'][0]['ip_address'],
|
||||
ssh_user=self.username,
|
||||
ssh_key=self.keypair['private_key'],
|
||||
ssh_timeout=10)
|
||||
# create a new server reusing the existing port
|
||||
self._test_create_instance_with_network_port(
|
||||
port_type, reuse_port=True)
|
||||
# check no new ports have been created
|
||||
self.assertEqual(old_num_ports, len(self.ports))
|
||||
self.assertEqual(old_port_id, self.ports[-1]['id'])
|
||||
# validate port is reachable again
|
||||
self.check_connectivity(
|
||||
self.ports[-1]['fixed_ips'][0]['ip_address'],
|
||||
self.username,
|
||||
self.keypair['private_key'])
|
||||
self.check_port_status(port_type)
|
||||
|
||||
@decorators.idempotent_id('6a382f97-50ea-4a1b-b012-aaa955f1a51a')
|
||||
def test_detach_attach_sriov_port_prov_net(self):
|
||||
interfaces_client = self.os_adm.interfaces_client
|
||||
# TODO(eolivare): only VF ports supported at this moment
|
||||
# for port_type in ('direct', 'direct-physical'):
|
||||
for port_type in ('direct',):
|
||||
self._test_instance_with_one_port_prov_net(port_type)
|
||||
old_port_id = self.ports[-1]['id']
|
||||
old_num_ports = len(self.ports)
|
||||
# remove port from server
|
||||
req_id = interfaces_client.delete_interface(
|
||||
self.servers[-1]['id'],
|
||||
self.ports[-1]['id']).response['x-openstack-request-id']
|
||||
# wait until port is really removed
|
||||
waiters.wait_for_interface_detach(
|
||||
self.servers_client,
|
||||
self.servers[-1]['id'],
|
||||
self.ports[-1]['id'],
|
||||
req_id)
|
||||
# validate port is not reachable anymore
|
||||
self.assertRaises(
|
||||
lib_exc.SSHTimeout,
|
||||
self.check_connectivity,
|
||||
host=self.ports[-1]['fixed_ips'][0]['ip_address'],
|
||||
ssh_user=self.username,
|
||||
ssh_key=self.keypair['private_key'],
|
||||
ssh_timeout=10)
|
||||
# reattach port on a new server (reattaching to a running VM is
|
||||
# not supported)
|
||||
self._test_create_instance_with_network_port(
|
||||
port_type, reuse_port=True)
|
||||
# check no new ports have been created
|
||||
self.assertEqual(old_num_ports, len(self.ports))
|
||||
self.assertEqual(old_port_id, self.ports[-1]['id'])
|
||||
# validate port is reachable again
|
||||
self.check_connectivity(
|
||||
self.ports[-1]['fixed_ips'][0]['ip_address'],
|
||||
self.username,
|
||||
self.keypair['private_key'])
|
||||
self.check_port_status(port_type)
|
||||
|
||||
|
||||
class NegativeProvNetSriovTest(ProviderNetworkSriovBaseTest):
|
||||
"""Assuming we have hosts with one SRIOV port and it is already occupied,
|
||||
then creation of VM with VF or PF port should fail
|
||||
We create a VM with PF port on each compute node in order to occupy SRIOV
|
||||
port; creating VM with PF or VF should fail as there is no available port
|
||||
"""
|
||||
@decorators.idempotent_id('3cf78b03-c88d-41c3-a891-d446518aca9f')
|
||||
def test_try_create_instance_with_sriov_port(self):
|
||||
for i in range(CONF.compute.min_compute_nodes *
|
||||
WB_CONF.sriov_pfs_per_host):
|
||||
self._test_create_instance_with_network_port('direct-physical')
|
||||
for port_type in ('direct-physical', 'direct'):
|
||||
vm_name = 'vm-%s-error' % port_type
|
||||
self.assertRaises(
|
||||
exceptions.BuildErrorException,
|
||||
self._test_create_instance_with_network_port,
|
||||
port_type=port_type,
|
||||
vm_name=vm_name)
|
||||
|
||||
|
||||
class TwoServersProvNetSriovTest(ProviderNetworkSriovBaseTest):
|
||||
"""Base class for SRIOV tests creating two VMs with one SRIOV port on
|
||||
provisioning networks
|
||||
"""
|
||||
|
||||
def _test_create_two_instances_with_ports(self, port_type1, port_type2):
|
||||
self._test_create_instance_with_network_port(port_type1)
|
||||
ip1 = self.ports[-1]['fixed_ips'][0]['ip_address']
|
||||
self._test_create_instance_with_network_port(port_type2)
|
||||
ip2 = self.ports[-1]['fixed_ips'][0]['ip_address']
|
||||
for ip in (ip1, ip2):
|
||||
self.check_connectivity(ip,
|
||||
self.username,
|
||||
self.keypair['private_key'])
|
||||
server_ssh_client = remote_client.RemoteClient(
|
||||
ip1,
|
||||
self.username,
|
||||
pkey=self.keypair['private_key'], server=self.servers[0])
|
||||
server_ssh_client.ping_host(ip2)
|
||||
|
||||
|
||||
class NormalAndOtherServersProvNetSriovTest(TwoServersProvNetSriovTest):
|
||||
"""The following tests can be executed without needing to cleanup between
|
||||
them because in total one normal, one VF and one PF ports are created
|
||||
"""
|
||||
@decorators.idempotent_id('61a19404-b819-4451-b4a6-d834bed6f3e9')
|
||||
def test_create_instances_with_normal_normal_ports_prov_net(self):
|
||||
self._test_create_two_instances_with_ports('normal', 'normal')
|
||||
|
||||
@decorators.idempotent_id('060c08ed-dc11-41e3-a2aa-1b28702986f2')
|
||||
def test_create_instances_with_normal_vf_ports_prov_net(self):
|
||||
self._test_create_two_instances_with_ports('direct', 'normal')
|
||||
|
||||
@decorators.idempotent_id('c12bee5f-7d11-425c-b75e-1a198793d7b0')
|
||||
def test_create_instances_with_normal_pf_ports_prov_net(self):
|
||||
self._test_create_two_instances_with_ports('direct-physical', 'normal')
|
||||
|
||||
|
||||
class VfServersProvNetSriovTest(TwoServersProvNetSriovTest):
|
||||
"""The following test need to be executed isolated (VM and port cleanup is
|
||||
needed before and after its execution)
|
||||
"""
|
||||
@decorators.idempotent_id('97c176c7-f1a0-45d5-879f-e8715b320d06')
|
||||
def test_create_instances_with_vf_vf_ports_prov_net(self):
|
||||
self._test_create_two_instances_with_ports('direct', 'direct')
|
||||
|
||||
|
||||
class VfAndPfServersProvNetSriovTest(TwoServersProvNetSriovTest):
|
||||
"""The following test need to be executed isolated (VM and port cleanup is
|
||||
needed before and after its execution)
|
||||
"""
|
||||
@decorators.idempotent_id('98f8b841-8193-4b68-90e5-030879cbf774')
|
||||
def test_create_instances_with_vf_pf_ports_prov_net(self):
|
||||
self._test_create_two_instances_with_ports('direct', 'direct-physical')
|
||||
|
||||
|
||||
class PfServersProvNetSriovTest(TwoServersProvNetSriovTest):
|
||||
"""The following test need to be executed isolated (VM and port cleanup is
|
||||
needed before and after it)
|
||||
"""
|
||||
@decorators.idempotent_id('935fb660-763f-4393-a780-f519c8f7dc95')
|
||||
def test_create_instances_with_pf_pf_ports_prov_net(self):
|
||||
self._test_create_two_instances_with_ports('direct-physical',
|
||||
'direct-physical')
|
||||
|
||||
|
||||
class BaseMigrationProvNetSriovTest(ProviderNetworkSriovBaseTest):
|
||||
"""Class for SRIOV test that verify that connectivity with SRIOV ports is
|
||||
correct after the server where this port was attached has been migrated to
|
||||
a different compute node
|
||||
"""
|
||||
|
||||
def _test_migration_with_sriov_port_prov_net(self, migration_method):
|
||||
# TODO(eolivare): PF ports do not support neither cold nor live
|
||||
# migration at this moment - see BZ1851417 and BZ1745842
|
||||
# for port_type in ('direct', 'macvtap', 'direct-physical'):
|
||||
for port_type in ('direct', 'macvtap'):
|
||||
self._test_instance_with_one_port_prov_net(port_type)
|
||||
server_id = self.servers[-1]['id']
|
||||
# get the host where the VM is running
|
||||
host = self.servers_client.show_server(
|
||||
server_id)['server']['OS-EXT-SRV-ATTR:host']
|
||||
# migrate the VM
|
||||
if migration_method == 'cold-migration':
|
||||
self.servers_client.migrate_server(server_id)
|
||||
waiters.wait_for_server_status(self.servers_client,
|
||||
server_id,
|
||||
'VERIFY_RESIZE')
|
||||
# confirm migration
|
||||
self.servers_client.confirm_resize_server(server_id)
|
||||
elif migration_method == 'live-migration':
|
||||
block_migration = (CONF.compute_feature_enabled.
|
||||
block_migration_for_live_migration)
|
||||
self.servers_client.live_migrate_server(
|
||||
server_id, host=None,
|
||||
block_migration=block_migration, disk_over_commit=False)
|
||||
else:
|
||||
raise RuntimeError('Unsupported migration method %s'
|
||||
% migration_method)
|
||||
# wait until server status is active
|
||||
waiters.wait_for_server_status(self.servers_client,
|
||||
server_id,
|
||||
constants.SERVER_STATUS_ACTIVE)
|
||||
# get the host where the VM after the migration
|
||||
new_host = self.servers_client.show_server(
|
||||
server_id)['server']['OS-EXT-SRV-ATTR:host']
|
||||
self.assertNotEqual(host, new_host, 'VM did not migrate')
|
||||
# check port connectivity and status after migration
|
||||
waiters.wait_for_interface_status(self.os_adm.interfaces_client,
|
||||
server_id,
|
||||
self.ports[-1]['id'],
|
||||
lib_constants.PORT_STATUS_ACTIVE)
|
||||
self.check_connectivity(
|
||||
self.ports[-1]['fixed_ips'][0]['ip_address'],
|
||||
self.username,
|
||||
self.keypair['private_key'])
|
||||
# delete the server before next iteration
|
||||
# otherwise PF server could not be migrated
|
||||
self.servers_client.delete_server(server_id)
|
||||
waiters.wait_for_server_termination(self.servers_client,
|
||||
server_id)
|
||||
del self.servers[-1]
|
||||
|
||||
|
||||
class ColdMigrationProvNetSriovTest(BaseMigrationProvNetSriovTest):
|
||||
"""Class for SRIOV test that verify that connectivity with SRIOV ports is
|
||||
correct after the server where this port was attached has been migrated to
|
||||
a different compute node, by means of cold migration procedure
|
||||
"""
|
||||
@decorators.idempotent_id('339f3bf7-48a6-461c-99d7-2b3b49126e65')
|
||||
@testtools.skipUnless(CONF.compute_feature_enabled.cold_migration,
|
||||
'Cold migration is not available.')
|
||||
def test_cold_migration_with_sriov_port_prov_net(self):
|
||||
self._test_migration_with_sriov_port_prov_net('cold-migration')
|
||||
|
||||
|
||||
class LiveMigrationProvNetSriovTest(BaseMigrationProvNetSriovTest):
|
||||
"""Class for SRIOV test that verify that connectivity with SRIOV ports is
|
||||
correct after the server where this port was attached has been migrated to
|
||||
a different compute node, by means of live migration procedure
|
||||
"""
|
||||
@decorators.idempotent_id('1a261026-3ef9-4254-a309-37e9db3dc33a')
|
||||
@testtools.skipUnless(CONF.compute_feature_enabled.live_migration,
|
||||
'Live migration is not available.')
|
||||
def test_live_migration_with_sriov_port_prov_net(self):
|
||||
self._test_migration_with_sriov_port_prov_net('live-migration')
|
||||
|
||||
|
||||
class AdminStateProvNetSriovTest(BaseMigrationProvNetSriovTest):
|
||||
"""Class for SRIOV test that verify that both SRIOV agents and ports
|
||||
behave properly when admin state is set to UP or DOWN
|
||||
"""
|
||||
@decorators.idempotent_id('f884cb13-05e0-48a1-a393-20fc47bc458e')
|
||||
def test_sriov_agent_admin_state_with_sriov_port_prov_net(self):
|
||||
# TODO(eolivare): not supported due to LP1892741 (both ovs and ovn)
|
||||
raise self.skipException("Not supported due to LP1892741")
|
||||
for port_type in ('direct', 'direct-physical'):
|
||||
self._test_instance_with_one_port_prov_net(port_type)
|
||||
# get all SRIOV agents
|
||||
sriov_agents = self.client.list_agents(
|
||||
binary='neutron-sriov-nic-agent')['agents']
|
||||
# set agents admin-state to DOWN
|
||||
state_up = {"admin_state_up": True}
|
||||
state_down = {"admin_state_up": False}
|
||||
for sriov_agent in sriov_agents:
|
||||
self.addCleanup(self.client.update_agent,
|
||||
sriov_agent['id'], agent_info=state_up)
|
||||
self.client.update_agent(sriov_agent['id'],
|
||||
agent_info=state_down)
|
||||
server_id = self.servers[-1]['id']
|
||||
port_id = self.ports[-1]['id']
|
||||
# check port connectivity and status after update
|
||||
self.check_connectivity(
|
||||
self.ports[-1]['fixed_ips'][0]['ip_address'],
|
||||
self.username,
|
||||
self.keypair['private_key'])
|
||||
waiters.wait_for_interface_status(self.os_adm.interfaces_client,
|
||||
server_id,
|
||||
port_id,
|
||||
lib_constants.PORT_STATUS_ACTIVE)
|
||||
vm_name = 'vm-%s-error' % port_type
|
||||
self.assertRaises(
|
||||
exceptions.BuildErrorException,
|
||||
self._test_create_instance_with_network_port,
|
||||
port_type=port_type,
|
||||
vm_name=vm_name)
|
||||
# set agents admin-state to UP before next iteration
|
||||
for sriov_agent in sriov_agents:
|
||||
self.client.update_agent(sriov_agent['id'],
|
||||
agent_info=state_up)
|
||||
|
||||
@decorators.idempotent_id('d1599884-c507-4724-ae17-c5034b532543')
|
||||
def test_port_admin_state_with_sriov_port_prov_net(self):
|
||||
# TODO(eolivare): only VF ports supported at this moment
|
||||
# for port_type in ('direct', 'direct-physical'):
|
||||
for port_type in ('direct',):
|
||||
self._test_instance_with_one_port_prov_net(port_type)
|
||||
# set port admin-state to DOWN
|
||||
server_id = self.servers[-1]['id']
|
||||
port_id = self.ports[-1]['id']
|
||||
self.client.update_port(port_id, admin_state_up=False)
|
||||
# check port connectivity and status after update
|
||||
waiters.wait_for_interface_status(self.os_adm.interfaces_client,
|
||||
server_id,
|
||||
port_id,
|
||||
lib_constants.PORT_STATUS_DOWN)
|
||||
# validate port is not reachable
|
||||
# it might take a moment until the port becomes unreachable
|
||||
time.sleep(.5)
|
||||
self.assertRaises(
|
||||
lib_exc.SSHTimeout,
|
||||
self.check_connectivity,
|
||||
host=self.ports[-1]['fixed_ips'][0]['ip_address'],
|
||||
ssh_user=self.username,
|
||||
ssh_key=self.keypair['private_key'],
|
||||
ssh_timeout=10)
|
||||
# set port admin-state to UP
|
||||
self.client.update_port(port_id, admin_state_up=True)
|
||||
# check port connectivity and status after update
|
||||
waiters.wait_for_interface_status(self.os_adm.interfaces_client,
|
||||
server_id,
|
||||
port_id,
|
||||
lib_constants.PORT_STATUS_ACTIVE)
|
||||
# validate port is reachable again
|
||||
self.check_connectivity(
|
||||
self.ports[-1]['fixed_ips'][0]['ip_address'],
|
||||
self.username,
|
||||
self.keypair['private_key'])
|
||||
|
||||
|
||||
class TwoPortsServersProvNetSriovTest(ProviderNetworkSriovBaseTest):
|
||||
"""VM instances are created with two ports:
|
||||
- an SRIOV VF port on a provider network
|
||||
- a normal port on a tenant network
|
||||
"""
|
||||
@decorators.idempotent_id('21ffa191-26a7-4f83-99f6-56ed09b4116a')
|
||||
def test_create_two_instances_with_two_ports(self):
|
||||
port_types = ['direct', 'normal']
|
||||
int_cidr = netaddr.IPNetwork('10.100.0.0/16')
|
||||
self._test_create_instance_with_two_ports(port_types, int_cidr)
|
||||
ext_ip1 = self.ports[-2]['fixed_ips'][0]['ip_address']
|
||||
self._test_create_instance_with_two_ports(
|
||||
port_types, int_cidr, reused_tenant_net=self.networks[-1])
|
||||
ext_ip2 = self.ports[-2]['fixed_ips'][0]['ip_address']
|
||||
# check connectivity with external IPs (SRIOV ports connected to
|
||||
# provider network)
|
||||
for ip in (ext_ip1, ext_ip2):
|
||||
self.check_connectivity(ip,
|
||||
self.username,
|
||||
self.keypair['private_key'])
|
||||
# check connectivity from both VMs to all 4 ports
|
||||
for ip in (ext_ip1, ext_ip2):
|
||||
server_ssh_client = remote_client.RemoteClient(
|
||||
ip,
|
||||
self.username,
|
||||
pkey=self.keypair['private_key'])
|
||||
for port in self.ports:
|
||||
fixed_ip = port['fixed_ips'][0]['ip_address']
|
||||
server_ssh_client.ping_host(fixed_ip)
|
@ -0,0 +1,484 @@
|
||||
# Copyright 2024 Red Hat, Inc.
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# 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 neutron_tempest_plugin import config
|
||||
from neutron_tempest_plugin.scenario import constants
|
||||
from oslo_log import log
|
||||
from tempest.common.utils.linux import remote_client
|
||||
from tempest.common import waiters
|
||||
from tempest import exceptions
|
||||
from tempest.lib import decorators
|
||||
from tempest.lib import exceptions as lib_exc
|
||||
|
||||
from whitebox_neutron_tempest_plugin.tests.scenario import \
|
||||
test_sriov_provider_network
|
||||
|
||||
|
||||
CONF = config.CONF
|
||||
WB_CONF = config.CONF.whitebox_neutron_plugin_options
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
||||
class RoutedProviderNetworkSriovBaseTest(
|
||||
test_sriov_provider_network.ProviderNetworkSriovBaseTest):
|
||||
"""Base class for tests using provisioning networks
|
||||
These tests need the same deployment:
|
||||
- datacentre with controllers and 1 compute over subnet 1
|
||||
- leaf1 1 computes over subnet 2
|
||||
and the next preconfiguration:
|
||||
- Admin user is needed to create ports on the existing provisioning network
|
||||
- 2 network segments called segment1, segment2
|
||||
- 2 subnets segment1(subnet1), segment2(subnet2)
|
||||
- 2 AZs segment1(central site), segment2(leaf1)
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def skip_checks(cls):
|
||||
super(RoutedProviderNetworkSriovBaseTest, cls).skip_checks()
|
||||
if not WB_CONF.provroutednetwork_support:
|
||||
raise cls.skipException(
|
||||
"Skipped since according to provroutednetwork_support config "
|
||||
"value Provider Routed Networks are not supported")
|
||||
|
||||
def _test_create_instance_with_network_port(self, port_type,
|
||||
subnet_id='1',
|
||||
vm_name=None,
|
||||
reuse_port=False):
|
||||
# Create vm in segmentX, az segmentX, subnet segmentX
|
||||
# Vm created directly over the external network
|
||||
# az segmentX
|
||||
# X=subnet_id
|
||||
security_groups, port, user_data, config_drive = \
|
||||
self._create_network_port(port_type,
|
||||
reuse_port=reuse_port,
|
||||
use_provider_net=True,
|
||||
subnet_id=subnet_id)
|
||||
vm_name = vm_name or "vm-" + port_type + "-" + self._testMethodName
|
||||
subnet_name = 'segment' + subnet_id
|
||||
self._create_server(
|
||||
security_groups=security_groups, networks=[port],
|
||||
user_data=user_data, config_drive=config_drive,
|
||||
availability_zone=subnet_name, name=vm_name)
|
||||
|
||||
def _test_instance_with_one_port_prov_net(self, port_type, subnet_id='1'):
|
||||
# Create vm in segmentX, az segmentX, subnet segmentX
|
||||
# Vm created over a pre created port in subnet
|
||||
# segmentX and az segmentX
|
||||
# X=subnet_id
|
||||
self._test_create_instance_with_network_port(port_type, subnet_id)
|
||||
self.check_connectivity(self.ports[-1]['fixed_ips'][0]['ip_address'],
|
||||
self.username,
|
||||
self.keypair['private_key'], ssh_timeout=60)
|
||||
self.check_port_status(port_type)
|
||||
|
||||
|
||||
class OneServerRoutedProvNetSriovTest(RoutedProviderNetworkSriovBaseTest):
|
||||
"""Class for SRIOV tests creating one VM with one SRIOV port on routed
|
||||
provider networks
|
||||
These tests can be executed without needing to cleanup VMs or ports between
|
||||
them
|
||||
"""
|
||||
|
||||
@decorators.idempotent_id('57bc00f2-d166-41c1-8a87-600bb087f248')
|
||||
def test_create_instance_with_normal_port_prov_net_seg1(self):
|
||||
# Create vm in datacentre network, segment1, az segment1,
|
||||
# subnet segment1
|
||||
# Vm created over a pre created port in subnet
|
||||
# segment1 and az segment1
|
||||
self._test_instance_with_one_port_prov_net('normal', '1')
|
||||
|
||||
@decorators.idempotent_id('d6356ffd-e521-48d2-bc69-13f2cd72690a')
|
||||
def test_create_instance_with_normal_port_prov_net_seg2(self):
|
||||
# Create vm in leaf1 network, segment2, az segment2,
|
||||
# subnet segment2
|
||||
# Vm created over a pre created port in subnet
|
||||
# segment2 and az segment2
|
||||
self._test_instance_with_one_port_prov_net('normal', '2')
|
||||
|
||||
@decorators.idempotent_id('aea44620-6e01-4cf1-8b9c-9171717092b0')
|
||||
def test_create_instance_with_direct_port_prov_net_seg1(self):
|
||||
# Create vm in datacentre network, segment1, az segment1,
|
||||
# subnet segment1
|
||||
# Vm created over a pre created port in subnet
|
||||
# segment1 and az segment1
|
||||
self._test_instance_with_one_port_prov_net('direct', '1')
|
||||
|
||||
@decorators.idempotent_id('17e96d5d-c5b6-4731-8027-7810a0761da3')
|
||||
def test_create_instance_with_direct_port_prov_net_seg2(self):
|
||||
# Create vm in leaf1 network, segment2, az segment2,
|
||||
# subnet segment2
|
||||
# Vm created over a pre created port in subnet
|
||||
# segment2 and az segment2
|
||||
self._test_instance_with_one_port_prov_net('direct', '2')
|
||||
|
||||
@decorators.idempotent_id('6d2f7a06-6f41-42e7-a239-f722914914da')
|
||||
def test_create_instance_with_macvtap_port_prov_net_seg1(self):
|
||||
# Create vm in datacentre network, segment1, az segment1,
|
||||
# subnet segment1
|
||||
# Vm created over a pre created port in subnet
|
||||
# segment1 and az segment1
|
||||
self._test_instance_with_one_port_prov_net('macvtap', '1')
|
||||
|
||||
@decorators.idempotent_id('955c6502-9cc6-4c7e-bb12-726be03a2c75')
|
||||
def test_create_instance_with_macvtap_port_prov_net_seg2(self):
|
||||
# Create vm in leaf1 network, segment2, az segment2,
|
||||
# subnet segment2
|
||||
# Vm created over a pre created port in subnet
|
||||
# segment2 and az segment2
|
||||
self._test_instance_with_one_port_prov_net('macvtap', '2')
|
||||
|
||||
|
||||
class OneServerPfRoutedProvNetSriovTest(RoutedProviderNetworkSriovBaseTest):
|
||||
"""The following test need to be executed isolated (VM and port cleanup is
|
||||
needed before and after its execution)
|
||||
"""
|
||||
|
||||
@decorators.idempotent_id('18e1fc90-440a-43ea-b269-524064332bb0')
|
||||
def test_create_instance_with_direct_physical_port_prov_net_seg1(self):
|
||||
# Create vm in datacentre network, segment1, az segment1,
|
||||
# subnet segment1
|
||||
# Vm created over a pre created port in subnet
|
||||
# segment1 and az segment1
|
||||
self._test_instance_with_one_port_prov_net('direct-physical', '1')
|
||||
|
||||
@decorators.idempotent_id('a9fa54d0-ec91-4d46-978d-48e3cef4492e')
|
||||
def test_create_instance_with_direct_physical_port_prov_net_seg2(self):
|
||||
# Create vm in leaf1 network, segment2, az segment2,
|
||||
# subnet segment2
|
||||
# Vm created over a pre created port in subnet
|
||||
# segment2 and az segment2
|
||||
self._test_instance_with_one_port_prov_net('direct-physical', '2')
|
||||
|
||||
|
||||
class RebootServerRoutedProvNetSriovRoutedTest(
|
||||
RoutedProviderNetworkSriovBaseTest):
|
||||
"""Class for SRIOV test that creates VM instances with different SRIOV port
|
||||
types, reboot those instances and checks their behavior is correct after
|
||||
reboot
|
||||
"""
|
||||
|
||||
@decorators.idempotent_id('43482b46-bb26-422f-96cd-99508eb30a75')
|
||||
def test_reboot_vm_with_sriov_port_prov_net_seg2(self):
|
||||
# Create vm in datacentre network, segment1, az segment1,
|
||||
# subnet segment1
|
||||
# Vm created over a pre created port in subnet
|
||||
# segment1 and az segment1
|
||||
# Reboot the vm
|
||||
# Create vm in leaf1 network, segment2, az segment2,
|
||||
# subnet segment2
|
||||
# Vm created over a pre created port in subnet
|
||||
# segment2 and az segment2
|
||||
# Reboot the vm
|
||||
cont = 0
|
||||
for port_type in ('direct', 'direct-physical'):
|
||||
cont += 1
|
||||
self._test_instance_with_one_port_prov_net(port_type, str(cont))
|
||||
for reboot_type in ('SOFT', 'HARD'):
|
||||
self.servers_client.reboot_server(self.servers[-1]['id'],
|
||||
type=reboot_type)
|
||||
waiters.wait_for_server_status(self.servers_client,
|
||||
self.servers[-1]['id'],
|
||||
constants.SERVER_STATUS_ACTIVE)
|
||||
self.check_connectivity(
|
||||
self.ports[-1]['fixed_ips'][0]['ip_address'],
|
||||
self.username,
|
||||
self.keypair['private_key'])
|
||||
self.check_port_status(port_type)
|
||||
|
||||
|
||||
class ReusePortRoutedProvNetSriovRoutedTest(
|
||||
RoutedProviderNetworkSriovBaseTest):
|
||||
"""Class for SRIOV test that creates VM instances with different SRIOV port
|
||||
types, delete those instances and reuse the existing SRIOV ports on new VM
|
||||
instances
|
||||
"""
|
||||
|
||||
@decorators.idempotent_id('eab71091-8a5f-4609-b259-91de74ad8744')
|
||||
def test_reuse_sriov_port_prov_net_seg2(self):
|
||||
# TODO(eolivare): only VF ports supported at this moment
|
||||
# for port_type in ('direct', 'direct-physical'):
|
||||
# Create vm in leaf1 network, segment2, az segment2,
|
||||
# subnet segment2
|
||||
# Vm created over a pre created port in subnet
|
||||
# segment2 and az segment2
|
||||
# Delete vm
|
||||
# Create vm reusing the port
|
||||
for port_type in ('direct',):
|
||||
self._test_instance_with_one_port_prov_net(port_type, '2')
|
||||
old_port_id = self.ports[-1]['id']
|
||||
old_num_ports = len(self.ports)
|
||||
# delete the server and create a new server attached to the
|
||||
# existing port
|
||||
self.servers_client.delete_server(self.servers[-1]['id'])
|
||||
waiters.wait_for_server_termination(self.servers_client,
|
||||
self.servers[-1]['id'])
|
||||
del self.servers[-1]
|
||||
# validate port is not reachable anymore
|
||||
self.assertRaises(
|
||||
lib_exc.SSHTimeout,
|
||||
self.check_connectivity,
|
||||
host=self.ports[-1]['fixed_ips'][0]['ip_address'],
|
||||
ssh_user=self.username,
|
||||
ssh_key=self.keypair['private_key'],
|
||||
ssh_timeout=10)
|
||||
|
||||
# create a new server reusing the existing port
|
||||
self._test_create_instance_with_network_port(
|
||||
port_type, '2', reuse_port=True)
|
||||
waiters.wait_for_server_status(self.servers_client,
|
||||
self.servers[-1]['id'],
|
||||
constants.SERVER_STATUS_ACTIVE)
|
||||
# check no new ports have been created
|
||||
self.assertEqual(old_num_ports, len(self.ports))
|
||||
self.assertEqual(old_port_id, self.ports[-1]['id'])
|
||||
# validate port is reachable again
|
||||
self.check_connectivity(
|
||||
self.ports[-1]['fixed_ips'][0]['ip_address'],
|
||||
self.username,
|
||||
self.keypair['private_key'])
|
||||
self.check_port_status(port_type)
|
||||
|
||||
@decorators.idempotent_id('b7bed0e4-9c34-4bf1-b411-246345ef7338')
|
||||
def test_detach_attach_sriov_port_prov_net_seg2(self):
|
||||
interfaces_client = self.os_adm.interfaces_client
|
||||
# TODO(eolivare): only VF ports supported at this moment
|
||||
# for port_type in ('direct', 'direct-physical'):
|
||||
# Create vm in leaf1 network, segment2, az segment2,
|
||||
# subnet segment2
|
||||
# Vm created over a pre created port in subnet
|
||||
# segment2 and az segment2
|
||||
# Remove interface from vm
|
||||
# Create vm reusing the port
|
||||
for port_type in ('direct',):
|
||||
self._test_instance_with_one_port_prov_net(port_type, '2')
|
||||
old_port_id = self.ports[-1]['id']
|
||||
old_num_ports = len(self.ports)
|
||||
# remove port from server
|
||||
interfaces_client.delete_interface(self.servers[-1]['id'],
|
||||
self.ports[-1]['id'])
|
||||
# validate port is not reachable anymore
|
||||
self.assertRaises(
|
||||
lib_exc.SSHTimeout,
|
||||
self.check_connectivity,
|
||||
host=self.ports[-1]['fixed_ips'][0]['ip_address'],
|
||||
ssh_user=self.username,
|
||||
ssh_key=self.keypair['private_key'],
|
||||
ssh_timeout=10)
|
||||
|
||||
# reattach port on a new server (reattaching to a running VM is
|
||||
# not supported)
|
||||
self._test_create_instance_with_network_port(
|
||||
port_type, '2', reuse_port=True)
|
||||
waiters.wait_for_server_status(self.servers_client,
|
||||
self.servers[-1]['id'],
|
||||
constants.SERVER_STATUS_ACTIVE)
|
||||
# check no new ports have been created
|
||||
self.assertEqual(old_num_ports, len(self.ports))
|
||||
self.assertEqual(old_port_id, self.ports[-1]['id'])
|
||||
# validate port is reachable again
|
||||
self.check_connectivity(
|
||||
self.ports[-1]['fixed_ips'][0]['ip_address'],
|
||||
self.username,
|
||||
self.keypair['private_key'])
|
||||
self.check_port_status(port_type)
|
||||
|
||||
|
||||
class NegativeProvRoutedNetSriovRoutedTest(RoutedProviderNetworkSriovBaseTest):
|
||||
"""Assuming we have hosts with one SRIOV port and it is already occupied,
|
||||
then creation of VM with VF or PF port should fail
|
||||
|
||||
We create a VM with PF port on each compute node in order to occupy SRIOV
|
||||
port; creating VM with PF or VF should fail as there is no available port
|
||||
"""
|
||||
|
||||
@decorators.idempotent_id('94dd5592-3cc9-4636-bad7-077c0b6b5558')
|
||||
def test_try_create_instance_with_sriov_port_seg2(self):
|
||||
# Create vm in leaf1 network, segment2, az segment2,
|
||||
# subnet segment2
|
||||
# Vm created over a pre created port in subnet
|
||||
# segment2 and az segment2
|
||||
# Try to create a second vm with vp and pf
|
||||
self._test_create_instance_with_network_port('direct-physical', '2')
|
||||
|
||||
for port_type in ('direct-physical', 'direct'):
|
||||
vm_name = 'vm-%s-error' % port_type
|
||||
self.assertRaises(
|
||||
exceptions.BuildErrorException,
|
||||
self._test_create_instance_with_network_port,
|
||||
port_type=port_type, subnet_id='2',
|
||||
vm_name=vm_name)
|
||||
|
||||
|
||||
class TwoServersProvNetSriovRoutedTest(RoutedProviderNetworkSriovBaseTest):
|
||||
"""Base class for SRIOV tests creating two VMs with one SRIOV port on
|
||||
routed provider networks
|
||||
# Create 2 vm:
|
||||
# 1 segmentX1, az segmentX1, subnet segmentX1
|
||||
# 2 segmentX2, az segmentX2, subnet segmentX2
|
||||
# Vms created over a pre created port
|
||||
# az segmentX, subnet segmentX
|
||||
# X1=subnet_id1
|
||||
# X2=subnet_id2
|
||||
"""
|
||||
|
||||
def _test_create_two_instances_with_ports_seg(self,
|
||||
port_type1, segment1,
|
||||
port_type2, segment2):
|
||||
self._test_create_instance_with_network_port(port_type1, segment1)
|
||||
ip1 = self.ports[0]['fixed_ips'][0]['ip_address']
|
||||
self._test_create_instance_with_network_port(port_type2, segment2)
|
||||
ip2 = self.ports[1]['fixed_ips'][0]['ip_address']
|
||||
for ip in (ip1, ip2):
|
||||
self.check_connectivity(ip,
|
||||
self.username,
|
||||
self.keypair['private_key'])
|
||||
server_ssh_client = remote_client.RemoteClient(
|
||||
ip1,
|
||||
self.username,
|
||||
pkey=self.keypair['private_key'], server=self.servers[0])
|
||||
server_ssh_client.ping_host(ip2)
|
||||
|
||||
|
||||
class TwoPortsTwoServersRoutedProvNetSriovTest(
|
||||
TwoServersProvNetSriovRoutedTest):
|
||||
"""The following tests can be executed without needing to cleanup between
|
||||
them because only normal and Vf ports are created
|
||||
"""
|
||||
|
||||
@decorators.idempotent_id('4523524a-b70e-4f44-9984-f6e622e652d1')
|
||||
def test_create_instances_with_normal_normal_ports_seg1_seg2(self):
|
||||
# Create 2 vm:
|
||||
# 1 segment1, az segment1, subnet segment1
|
||||
# 2 segment2, az segment2, subnet segment2
|
||||
# 2 vms in the different segment(central site and leaf)
|
||||
# Vms created over a pre created port
|
||||
# az segmentX, subnet segmentX
|
||||
self._test_create_two_instances_with_ports_seg('normal', '1',
|
||||
'normal', '2')
|
||||
|
||||
@decorators.idempotent_id('b5a96f9f-68b7-4559-b98f-b2c1b3f3a940')
|
||||
def test_create_instances_with_normal_normal_ports_seg2(self):
|
||||
# Create 2 vm:
|
||||
# 1 segment2, az segment2, subnet segment2
|
||||
# 2 segment2, az segment2, subnet segment2
|
||||
# 2 vms in the same segment and compute(leaf1)
|
||||
# Vms created over a pre created port
|
||||
# az segmentX, subnet segmentX
|
||||
self._test_create_two_instances_with_ports_seg('normal', '2',
|
||||
'normal', '2')
|
||||
|
||||
@decorators.idempotent_id('6cbfaf93-b3d0-4a52-83c2-69a387e815e1')
|
||||
def test_create_instances_with_normal_vf_ports_seg1_seg2(self):
|
||||
# Create 2 vm:
|
||||
# 1 segment1, az segment1, subnet segment1
|
||||
# 2 segment2, az segment2, subnet segment2
|
||||
# 2 vms in the different segment(central site and leaf)
|
||||
# Vms created over a pre created port
|
||||
# az segmentX, subnet segmentX
|
||||
self._test_create_two_instances_with_ports_seg('direct', '1',
|
||||
'normal', '2')
|
||||
|
||||
@decorators.idempotent_id('b45d44e4-aa13-484a-86d8-19482bdd9c25')
|
||||
def test_create_instances_with_normal_vf_ports_seg2(self):
|
||||
# Create 2 vm:
|
||||
# 1 segment2, az segment2, subnet segment2
|
||||
# 2 segment2, az segment2, subnet segment2
|
||||
# 2 vms in the same segment and compute(leaf1)
|
||||
# Vms created over a pre created port
|
||||
# az segmentX, subnet segmentX
|
||||
self._test_create_two_instances_with_ports_seg('direct', '2',
|
||||
'normal', '2')
|
||||
|
||||
@decorators.idempotent_id('bec613f7-57ab-44ea-879f-e6a21647493a')
|
||||
def test_create_instances_with_vf_vf_ports_seg1_seg2(self):
|
||||
# Create 2 vm:
|
||||
# 1 segment1, az segment1, subnet segment1
|
||||
# 2 segment2, az segment2, subnet segment2
|
||||
# 2 vms in the different segment(central site and leaf)
|
||||
# Vms created over a pre created port
|
||||
# az segmentX, subnet segmentX
|
||||
self._test_create_two_instances_with_ports_seg('direct', '1',
|
||||
'direct', '2')
|
||||
|
||||
|
||||
class PfAndPfServersProvNetSriovTest(
|
||||
TwoServersProvNetSriovRoutedTest):
|
||||
"""The following test need to be executed isolated (VM and port cleanup is
|
||||
needed before and after its execution)
|
||||
"""
|
||||
|
||||
@decorators.idempotent_id('128f6070-b0fc-4dc3-a815-2aeaf43f2815')
|
||||
def test_create_instances_with_pf_pf_ports_seg1_seg2(self):
|
||||
# Create 2 vm:
|
||||
# 1 segment1, az segment1, subnet segment1
|
||||
# 2 segment2, az segment2, subnet segment2
|
||||
# 2 vms in the different segment(central site and leaf)
|
||||
# Vms created over a pre created port
|
||||
# az segmentX, subnet segmentX
|
||||
self._test_create_two_instances_with_ports_seg('direct-physical', '1',
|
||||
'direct-physical', '2')
|
||||
|
||||
|
||||
class VfAndPfServersRoutedProvNetSriovTest(
|
||||
TwoServersProvNetSriovRoutedTest):
|
||||
"""The following test need to be executed isolated (VM and port cleanup is
|
||||
needed before and after its execution)
|
||||
"""
|
||||
|
||||
@decorators.idempotent_id('7eecb5f4-2dde-4cf6-bf60-e3663bade307')
|
||||
def test_create_instances_with_vf_pf_ports_seg1_seg2(self):
|
||||
# Create 2 vm:
|
||||
# 1 segment1, az segment1, subnet segment1
|
||||
# 2 segment2, az segment2, subnet segment2
|
||||
# 2 vms in the different segment(central site and leaf)
|
||||
# Vms created over a pre created port
|
||||
# az segmentX, subnet segmentX
|
||||
self._test_create_two_instances_with_ports_seg('direct-physical', '1',
|
||||
'direct', '2')
|
||||
|
||||
|
||||
class NormalAndPf1SegServersRoutedProvNetSriovTest(
|
||||
TwoServersProvNetSriovRoutedTest):
|
||||
"""The following test need to be executed isolated (VM and port cleanup is
|
||||
needed before and after its execution)
|
||||
"""
|
||||
|
||||
@decorators.idempotent_id('9de6e9c3-b1e1-4509-b97b-dd9800fee2b9')
|
||||
def test_create_instances_with_normal_pf_ports_seg2(self):
|
||||
# Create 2 vm:
|
||||
# 1 segment2, az segment2, subnet segment2
|
||||
# 2 segment2, az segment2, subnet segment2
|
||||
# 2 vms in the same segment and compute(leaf1)
|
||||
# Vms created over a pre created port
|
||||
# az segmentX, subnet segmentX
|
||||
self._test_create_two_instances_with_ports_seg('direct-physical', '2',
|
||||
'normal', '2')
|
||||
|
||||
|
||||
class NormalAndPfServersRoutedProvNetSriovTest(
|
||||
TwoServersProvNetSriovRoutedTest):
|
||||
"""The following test need to be executed isolated (VM and port cleanup is
|
||||
needed before and after its execution)
|
||||
"""
|
||||
|
||||
@decorators.idempotent_id('5360a20f-9168-494a-9c32-72841a9f7563')
|
||||
def test_create_instances_with_normal_pf_ports_seg1_seg2(self):
|
||||
# Create 2 vm:
|
||||
# 1 segment1, az segment1, subnet segment1
|
||||
# 2 segment2, az segment2, subnet segment2
|
||||
# 2 vms in the different segment(central site and leaf)
|
||||
# Vms created over a pre created port
|
||||
# az segmentX, subnet segmentX
|
||||
self._test_create_two_instances_with_ports_seg('direct-physical', '1',
|
||||
'normal', '2')
|
Loading…
x
Reference in New Issue
Block a user