From d814f680410191233f8f857e9f9afa316ce28f72 Mon Sep 17 00:00:00 2001 From: Karthik S Date: Tue, 19 Nov 2019 17:15:10 +0000 Subject: [PATCH] Adding testcases for sriov_config Backport of commit 3e938d82f57e89cc57596bbd8f5a87ba480149f6 is also added here to fix the CI errors for stable/train Change-Id: Ibc5be431434870d8424bcc3e405daa3d4913308d (cherry picked from commit 04bea45124473bc75b1d053c5062e08f9861fbe0) --- os_net_config/sriov_config.py | 29 ++- os_net_config/tests/test_sriov_config.py | 275 +++++++++++++++++++++++ 2 files changed, 298 insertions(+), 6 deletions(-) create mode 100644 os_net_config/tests/test_sriov_config.py diff --git a/os_net_config/sriov_config.py b/os_net_config/sriov_config.py index cd054b4f..bd917435 100644 --- a/os_net_config/sriov_config.py +++ b/os_net_config/sriov_config.py @@ -38,6 +38,9 @@ _SYS_CLASS_NET = '/sys/class/net' _UDEV_RULE_FILE = '/etc/udev/rules.d/80-persistent-os-net-config.rules' _UDEV_LEGACY_RULE_FILE = '/etc/udev/rules.d/70-os-net-config-sriov.rules' _IFUP_LOCAL_FILE = '/sbin/ifup-local' +_RESET_SRIOV_RULES_FILE = '/etc/udev/rules.d/70-tripleo-reset-sriov.rules' +_ALLOCATE_VFS_FILE = '/etc/sysconfig/allocate_vfs' + MAX_RETRIES = 10 PF_FUNC_RE = re.compile(r"\.(\d+)$", 0) # In order to keep VF representor name consistent specially after the upgrade @@ -126,10 +129,10 @@ def restart_ovs_and_pfs_netdevs(): def cleanup_puppet_config(): file_contents = "" - if os.path.exists('/etc/udev/rules.d/70-tripleo-reset-sriov.rules'): - os.remove('/etc/udev/rules.d/70-tripleo-reset-sriov.rules') - if os.path.exists('/etc/sysconfig/allocate_vfs'): - os.remove('/etc/sysconfig/allocate_vfs') + if os.path.exists(_RESET_SRIOV_RULES_FILE): + os.remove(_RESET_SRIOV_RULES_FILE) + if os.path.exists(_ALLOCATE_VFS_FILE): + os.remove(_ALLOCATE_VFS_FILE) if os.path.exists(_IFUP_LOCAL_FILE): # Remove the invocation of allocate_vfs script generated by puppet # After the removal of allocate_vfs, if the ifup-local file has just @@ -145,14 +148,27 @@ def cleanup_puppet_config(): newfile.write(file_contents) -def configure_sriov_pf(execution_from_cli=False, restart_openvswitch=False): +def udev_monitor_setup(): # Create a context for pyudev and observe udev events for network context = pyudev.Context() monitor = pyudev.Monitor.from_netlink(context) monitor.filter_by('net') observer = pyudev.MonitorObserver(monitor, udev_event_handler) + return observer + + +def udev_monitor_start(observer): observer.start() + +def udev_monitor_stop(observer): + observer.stop() + + +def configure_sriov_pf(execution_from_cli=False, restart_openvswitch=False): + observer = udev_monitor_setup() + udev_monitor_start(observer) + sriov_map = _get_sriov_map() MLNX_UNBIND_FILE_PATH = "/sys/bus/pci/drivers/mlx5_core/unbind" MLNX_VENDOR_ID = "0x15b3" @@ -224,7 +240,7 @@ def configure_sriov_pf(execution_from_cli=False, restart_openvswitch=False): if trigger_udev_rule: trigger_udev_rules() - observer.stop() + udev_monitor_stop(observer) if restart_openvswitch: restart_ovs_and_pfs_netdevs() @@ -574,6 +590,7 @@ def main(argv=sys.argv): _write_numvfs(device_name, int(numvfs)) else: logging.error("Invalid arguments for --numvfs %s" % opts.numvfs) + return 1 else: # Configure the PF's configure_sriov_pf() diff --git a/os_net_config/tests/test_sriov_config.py b/os_net_config/tests/test_sriov_config.py new file mode 100644 index 00000000..4564e0b4 --- /dev/null +++ b/os_net_config/tests/test_sriov_config.py @@ -0,0 +1,275 @@ +# -*- coding: utf-8 -*- + +# Copyright 2019 Red Hat, Inc. +# +# 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 os +import os.path +import random +import shutil +import tempfile + + +from os_net_config import sriov_config +from os_net_config.tests import base +from os_net_config import utils + + +class TestSriovConfig(base.TestCase): + """Unit tests for methods defined in sriov_config.py""" + + def setUp(self): + super(TestSriovConfig, self).setUp() + rand = str(int(random.random() * 100000)) + + tmpdir = tempfile.mkdtemp() + self.stub_out('os_net_config.sriov_config._SYS_CLASS_NET', tmpdir) + + sriov_config._UDEV_RULE_FILE = '/tmp/' + rand + 'etc_udev_rules.d'\ + '80-persistent-os-net-config.rules' + sriov_config._UDEV_LEGACY_RULE_FILE = '/tmp/' + rand + 'etc_udev_'\ + 'rules.d_70-os-net-config-sriov.rules' + sriov_config._IFUP_LOCAL_FILE = '/tmp/' + rand + 'sbin_ifup-local' + sriov_config._RESET_SRIOV_RULES_FILE = '/tmp/' + rand + 'etc_udev_'\ + 'rules.d_70-tripleo-reset-sriov.rules' + sriov_config._ALLOCATE_VFS_FILE = '/tmp/' + rand + 'etc_sysconfig_'\ + 'allocate_vfs' + sriov_config._SRIOV_CONFIG_FILE = '/tmp/' + rand + 'sriov_config'\ + '.yaml' + + def tearDown(self): + super(TestSriovConfig, self).tearDown() + if os.path.isfile(sriov_config._SRIOV_CONFIG_FILE): + os.remove(sriov_config._SRIOV_CONFIG_FILE) + if os.path.isfile(sriov_config._IFUP_LOCAL_FILE): + os.remove(sriov_config._IFUP_LOCAL_FILE) + shutil.rmtree(sriov_config._SYS_CLASS_NET) + if os.path.isfile(sriov_config._RESET_SRIOV_RULES_FILE): + os.remove(sriov_config._RESET_SRIOV_RULES_FILE) + if os.path.isfile(sriov_config._ALLOCATE_VFS_FILE): + os.remove(sriov_config._ALLOCATE_VFS_FILE) + if os.path.isfile(sriov_config._UDEV_LEGACY_RULE_FILE): + os.remove(sriov_config._UDEV_LEGACY_RULE_FILE) + + def test_configure_sriov_pf(self): + """Test the numvfs setting for SR-IOV PF + + Test the udev rules created for legacy mode of SR-IOV PF + """ + + exp_udev_content = '# This file is autogenerated by os-net-config\n'\ + 'KERNEL=="p2p1", RUN+="/bin/os-net-config-sriov -n %k:10"\n'\ + 'KERNEL=="p2p2", RUN+="/bin/os-net-config-sriov -n %k:12"\n' + + run_cmd = [] + + def run_ip_config_cmd_stub(*args, **kwargs): + run_cmd.append(' '.join(args)) + self.stub_out('os_net_config.sriov_config.run_ip_config_cmd', + run_ip_config_cmd_stub) + + def udev_monitor_setup_stub(): + return + self.stub_out('os_net_config.sriov_config.udev_monitor_setup', + udev_monitor_setup_stub) + + def udev_monitor_start_stub(observer): + return + self.stub_out('os_net_config.sriov_config.udev_monitor_start', + udev_monitor_start_stub) + + def udev_monitor_stop_stub(observer): + return + self.stub_out('os_net_config.sriov_config.udev_monitor_stop', + udev_monitor_stop_stub) + + def cleanup_puppet_config_stub(): + return + self.stub_out('os_net_config.sriov_config.cleanup_puppet_config', + cleanup_puppet_config_stub) + + def trigger_udev_rules_stub(): + return + self.stub_out('os_net_config.sriov_config.trigger_udev_rules', + trigger_udev_rules_stub) + + def reload_udev_rules_stub(): + return + self.stub_out('os_net_config.sriov_config.reload_udev_rules', + reload_udev_rules_stub) + + def _wait_for_vf_creation_stub(pf_name, numvfs): + numvfs_pair = {"p2p1": 10, "p2p2": 12} + self.assertEqual(numvfs_pair[pf_name], numvfs) + self.stub_out('os_net_config.sriov_config._wait_for_vf_creation', + _wait_for_vf_creation_stub) + + def get_vendor_id_stub(ifname): + return "0x8086" + self.stub_out('os_net_config.sriov_config.get_vendor_id', + get_vendor_id_stub) + + pf_config = [{"device_type": "pf", "name": "p2p1", "numvfs": 10, + "promisc": "on", "link_mode": "legacy"}, + {"device_type": "pf", "name": "p2p2", "numvfs": 12, + "promisc": "off", "link_mode": "legacy"}] + + os.makedirs(sriov_config._SYS_CLASS_NET + "/p2p1/device") + os.makedirs(sriov_config._SYS_CLASS_NET + "/p2p2/device") + f = open(sriov_config._SYS_CLASS_NET + "/p2p1/device/sriov_numvfs", + "w+") + f.write("0") + f.close() + + f = open(sriov_config._SYS_CLASS_NET + "/p2p2/device/sriov_numvfs", + "w+") + f.write("0") + f.close() + + utils.write_yaml_config(sriov_config._SRIOV_CONFIG_FILE, pf_config) + sriov_config.configure_logger(debug=True) + sriov_config.configure_sriov_pf() + + f = open(sriov_config._UDEV_LEGACY_RULE_FILE, 'r') + self.assertEqual(exp_udev_content, f.read()) + self.assertEqual(10, sriov_config.get_numvfs('p2p1')) + self.assertEqual(12, sriov_config.get_numvfs('p2p2')) + + def test_cleanup_puppet_config_deprecation(self): + """Test the cleanup of puppet-tripleo generated config file. + + Usecase: The ifup-local has the default content generated by + puppet-tripleo + """ + + content = '#!/bin/bash\n'\ + '/etc/sysconfig/allocate_vfs $1' + f = open(sriov_config._RESET_SRIOV_RULES_FILE, "w+") + f.close() + f = open(sriov_config._ALLOCATE_VFS_FILE, "w+") + f.close() + f = open(sriov_config._IFUP_LOCAL_FILE, "w+") + f.write(content) + f.close() + + sriov_config.cleanup_puppet_config() + self.assertEqual(False, + os.path.exists(sriov_config._RESET_SRIOV_RULES_FILE)) + self.assertEqual(False, + os.path.exists(sriov_config._ALLOCATE_VFS_FILE)) + self.assertEqual(False, + os.path.exists(sriov_config._IFUP_LOCAL_FILE)) + + def test_cleanup_puppet_config_new(self): + """Test the cleanup of puppet-tripleo generated config file. + + Usecase: When os-net-config is run on fresh deployments, all these + files will not exist. + """ + + sriov_config.cleanup_puppet_config() + self.assertEqual(False, + os.path.exists(sriov_config._RESET_SRIOV_RULES_FILE)) + self.assertEqual(False, + os.path.exists(sriov_config._ALLOCATE_VFS_FILE)) + self.assertEqual(False, + os.path.exists(sriov_config._IFUP_LOCAL_FILE)) + + def test_cleanup_puppet_config_modified(self): + """Test the cleanup of puppet-tripleo generated config file + + Usecase: When os-net-config is run first time after the deprecation + of NeutronSriovNumVFs and ifup-local has contents other than invoking + allocate_vfs. + """ + + content = '#!/bin/bash\n'\ + '/etc/sysconfig/allocate_vfs $1\n'\ + '/usr/sbin/ifup eth0' + mod_content = '#!/bin/bash\n'\ + '/usr/sbin/ifup eth0' + f = open(sriov_config._IFUP_LOCAL_FILE, "w+") + f.write(content) + f.close() + + sriov_config.cleanup_puppet_config() + self.assertEqual(False, + os.path.exists(sriov_config._RESET_SRIOV_RULES_FILE)) + self.assertEqual(False, + os.path.exists(sriov_config._ALLOCATE_VFS_FILE)) + self.assertEqual(True, + os.path.exists(sriov_config._IFUP_LOCAL_FILE)) + + f = open(sriov_config._IFUP_LOCAL_FILE, "r") + self.assertEqual(mod_content, f.read()) + + def test_numvfs_config(self): + """Test the numvfs config with valid arguments""" + + os.makedirs(sriov_config._SYS_CLASS_NET + "/p2p1/device") + f = open(sriov_config._SYS_CLASS_NET + "/p2p1/device/sriov_numvfs", + "w+") + f.write("0") + f.close() + self.assertEqual(None, sriov_config.main(['ARG0', '-n', 'p2p1:15'])) + self.assertEqual(15, sriov_config.get_numvfs('p2p1')) + + def test_numvfs_invalid_params(self): + """Test the numvfs config with invalid arguments""" + + os.makedirs(sriov_config._SYS_CLASS_NET + "/p2p1/device") + f = open(sriov_config._SYS_CLASS_NET + "/p2p1/device/sriov_numvfs", + "w+") + f.write("0") + f.close() + self.assertEqual(1, sriov_config.main(['ARG0', '-n', 'p2p1:15a'])) + self.assertEqual(0, sriov_config.get_numvfs('p2p1')) + + def test_numvfs_preconfigured(self): + """Test the numvfs config while its already configured""" + + os.makedirs(sriov_config._SYS_CLASS_NET + "/p2p1/device") + f = open(sriov_config._SYS_CLASS_NET + "/p2p1/device/sriov_numvfs", + "w+") + f.write("10") + f.close() + self.assertEqual(None, sriov_config.main(['ARG0', '-n', 'p2p1:15'])) + self.assertEqual(10, sriov_config.get_numvfs('p2p1')) + + def test_configure_sriov_vf(self): + """Test configuration of SR-IOV VF settings""" + + vf_config = [{"device_type": "vf", "device": {"name": "p2p1", + "vfid": 1}, "promisc": "on", "vlan_id": 101, + "qos": 5, "macaddr": "AA:BB:CC:DD:EE:FF", + "spoofcheck": "on", "state": "auto", "trust": "on", + "name": "p2p1_1"}] + exp_cmds = ["ip link set dev p2p1 vf 1 mac AA:BB:CC:DD:EE:FF", + "ip link set dev p2p1 vf 1 vlan 101 qos 5", + "ip link set dev p2p1_1 promisc on", + "ip link set dev p2p1 vf 1 spoofchk on", + "ip link set dev p2p1 vf 1 state auto", + "ip link set dev p2p1 vf 1 trust on"] + run_cmd = [] + + def run_ip_config_cmd_stub(*args, **kwargs): + run_cmd.append(' '.join(args)) + self.stub_out('os_net_config.sriov_config.run_ip_config_cmd', + run_ip_config_cmd_stub) + + utils.write_yaml_config(sriov_config._SRIOV_CONFIG_FILE, vf_config) + sriov_config.configure_sriov_vf() + + for cmd in exp_cmds: + self.assertIn(cmd, run_cmd)