diff --git a/templates/kilo/neutron-openvswitch-networking-sriov.service b/files/neutron-openvswitch-networking-sriov.service similarity index 75% rename from templates/kilo/neutron-openvswitch-networking-sriov.service rename to files/neutron-openvswitch-networking-sriov.service index a6edf40a..c78e5453 100644 --- a/templates/kilo/neutron-openvswitch-networking-sriov.service +++ b/files/neutron-openvswitch-networking-sriov.service @@ -13,7 +13,7 @@ WantedBy=network-online.target [Service] Type=oneshot EnvironmentFile=-/etc/default/networking-sriov -ExecStart=/etc/init.d/neutron-openvswitch-networking-sriov.sh systemd-start -ExecStop=/etc/init.d/neutron-openvswitch-networking-sriov.sh systemd-stop +ExecStart=/usr/local/bin/neutron-openvswitch-networking-sriov.sh systemd-start +ExecStop=/usr/local/bin/neutron-openvswitch-networking-sriov.sh systemd-stop RemainAfterExit=true TimeoutStartSec=5min diff --git a/files/neutron-openvswitch-networking-sriov.sh b/files/neutron-openvswitch-networking-sriov.sh new file mode 100755 index 00000000..7e1045ef --- /dev/null +++ b/files/neutron-openvswitch-networking-sriov.sh @@ -0,0 +1,73 @@ +#!/bin/sh + +### BEGIN INIT INFO +# Provides: networking-sriov +# Required-Start: mountkernfs $local_fs +# Required-Stop: $local_fs +# Default-Start: S +# Default-Stop: 0 6 +# Short-Description: Configure SRIOV Virtual Functions +### END INIT INFO + +# Authors: Frode Nordahl + +DESC="Configure SRIOV Virtual Functions" + +. /lib/lsb/init-functions + +# Include defaults if available +if [ -f /etc/default/neutron-openvswitch-networking-sriov ] ; then + . /etc/default/neutron-openvswitch-networking-sriov +fi + +if [ -z "${ENABLE}" ]; then + ENABLE=0 +fi + +if [ -z "${VFS_BLANKET}" ]; then + VFS_BLANKET=auto +fi + +# Exit if feature is not enabled +[ $ENABLE -gt 0 ] || exit 0 + +do_start() { + /usr/local/bin/neutron_openvswitch_networking_sriov.py --start --vfs "${VFS_LIST}" --vfs-blanket "${VFS_BLANKET}" +} + +do_restart() { + /usr/local/bin/neutron_openvswitch_networking_sriov.py --restart --vfs "${VFS_LIST}" --vfs-blanket "${VFS_BLANKET}" +} + +do_stop() { + /usr/local/bin/neutron_openvswitch_networking_sriov.py --stop --vfs "${VFS_LIST}" --vfs-blanket "${VFS_BLANKET}" +} + + +case "$1" in + start) + log_daemon_msg "$DESC" + do_start + ;; + stop) + log_daemon_msg "Un-$DESC" + do_stop + ;; + systemd-start) + do_start + ;; + systemd-stop) + do_stop + ;; + restart) + log_daemon_msg "Re-$DESC" + do_stop + do_start + ;; + *) + N=/usr/local/bin/neutron-openvswitch-networking-sriov.sh + echo "Usage: $N {start|stop|restart|systemd-start|systemd-stop}" >&2 + ;; +esac + +exit 0 diff --git a/files/neutron_openvswitch_networking_sriov.py b/files/neutron_openvswitch_networking_sriov.py new file mode 100755 index 00000000..2ea03aef --- /dev/null +++ b/files/neutron_openvswitch_networking_sriov.py @@ -0,0 +1,220 @@ +#!/usr/bin/env python3 +# +# Copyright 2019 Canonical Ltd +# +# 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 argparse +import os +import sys +import time + + +def wait_for_vfs(device, numvfs): + """Wait for the VFs to be created. + + :param str device: path to the NIC + :param str numvfs: number of VFs to wait for + :returns: None + """ + numvfs = int(numvfs) + created_vfs_number = 0 + while created_vfs_number < numvfs: + created_vfs = [f for f in os.listdir(device) if f.startswith('virtfn')] + created_vfs_number = len(created_vfs) + print('Waiting for {numvfs} VFs to appear in {dev}'.format( + numvfs=numvfs, dev=device)) + time.sleep(0.05) + + +def get_totalvfs(device): + """Get the number of allowed VFs. + + :param str device: Path of the device + :returns: number of allowed VFs. + :rtype: str + """ + path = os.path.join(device, 'sriov_totalvfs') + with open(path) as f: + total_vfs = f.read() + return total_vfs + + +def find_sriov_devices(): + """Find SR-IOV devices. + + :returns: list of SR-IOV devices' paths + :rtype: list + """ + f_name = 'sriov_totalvfs' + devices = [] + for path, dirs, files in os.walk('/sys/devices'): + if f_name in files: + devices.append(path) + return devices + + +def write_sriov_numvfs(base_path, numvfs): + """Write the number of VFs to file. + + :param str base_path: Path of the device + :param str numvfs: Number of VFs + :returns: None + """ + path = os.path.join(base_path, 'sriov_numvfs') + print('Configuring {numvfs} VFs for {path}'.format( + numvfs=numvfs, path=path)) + try: + with open(path, 'w') as f: + f.write(numvfs) + wait_for_vfs(base_path, numvfs) + except OSError as err: + print( + 'Error while configuring VFs: {err}'.format(err=err)) + + +def configure_vfs(vfs, vfs_blanket='auto', stop=False): + """Configure the VFs. + + :param dict vfs: list of VFs as dict (e.g. {'eth0': '8', 'eth1': '4'} + :param str vfs_blanket: blanket config for the VFs + :param bool stop: If we are stopping + :returns: None + """ + if vfs: + for device, numvfs in vfs.items(): + if stop: + numvfs = '0' + base_path = '/sys/class/net/{dev}/device/'.format(dev=device) + write_sriov_numvfs(base_path, numvfs) + else: + sriov_devices = find_sriov_devices() + for device in sriov_devices: + total_vfs = get_totalvfs(device) + if stop: + vfs_blanket = '0' + if vfs_blanket == 'auto': + numvfs = total_vfs + elif int(vfs_blanket) > int(total_vfs): + numvfs = total_vfs + else: + numvfs = vfs_blanket + write_sriov_numvfs(device, numvfs) + + +def restart(vfs, vfs_blanket): + """Restart the VFs + + :param dict vfs: list of VFs as dict (e.g. {'eth0': '8', 'eth1': '4'} + :returns: None + """ + stop(vfs) + start(vfs, vfs_blanket) + + +def start(vfs, vfs_blanket): + """Start the VFs. + + :param dict vfs: list of VFs as dict (e.g. {'eth0': '8', 'eth1': '4'} + :returns: None + """ + configure_vfs(vfs, vfs_blanket) + + +def stop(vfs): + """Stop the VFs. + + :param dict vfs: list of VFs as dict (e.g. {'eth0': '8', 'eth1': '4'} + :returns: None + """ + configure_vfs(vfs, stop=True) + + +def parse_vfs(vfs=None): + """Parse VFs from string + + :param dict vfs: string containing the VFs + :returns: dict of VFs + :rtype: dict { + 'eth0': '8', + 'eth1': '2' + } + """ + if not vfs: + return {} + parsed_vfs = {} + for vf in vfs.split(): + k, v = vf.split(':') + parsed_vfs[k] = v + return parsed_vfs + + +def parse_args(args): + """Parse the arguments. + + : param list args: list of args + : returns: the parsed arguments + : rtype: Namespace + : raises SystemExit: if there are missing required args + """ + parser = argparse.ArgumentParser() + parser.add_argument( + '--start', + help='Start', + action='store_true', + default=False + ) + parser.add_argument( + '--stop', + help='Stop', + action='store_true', + default=False + ) + parser.add_argument( + '--restart', + help='Restart', + action='store_true', + default=False + ) + parser.add_argument( + '--vfs', + help='VFS List' + ) + parser.add_argument( + '--vfs-blanket', + help='VFS Blanket', + default='auto' + ) + return parser.parse_args(args) + + +def main(args): + """Main function. + + : param list args: list of arguments + : returns: 0 + : rtype: int + """ + args = parse_args(args) + vfs = parse_vfs(args.vfs) + if args.restart: + restart(vfs, args.vfs_blanket) + elif args.start: + start(vfs, args.vfs_blanket) + elif args.stop: + stop(vfs) + return 0 + + +if __name__ == '__main__': + sys.exit(main(sys.argv[1:])) diff --git a/hooks/neutron_ovs_hooks.py b/hooks/neutron_ovs_hooks.py index 48e265cb..bcb0fcc9 100755 --- a/hooks/neutron_ovs_hooks.py +++ b/hooks/neutron_ovs_hooks.py @@ -66,6 +66,8 @@ from neutron_ovs_utils import ( pause_unit_helper, resume_unit_helper, determine_purge_packages, + install_sriov_systemd_files, + enable_sriov, ) hooks = Hooks() @@ -100,6 +102,10 @@ def upgrade_charm(): # migrating. if 'Service restart triggered' not in f.read(): CONFIGS.write(OVS_DEFAULT) + # Ensure that the SR-IOV systemd files are copied if a charm-upgrade + # happens + if enable_sriov(): + install_sriov_systemd_files() @hooks.hook('neutron-plugin-relation-changed') @@ -148,7 +154,7 @@ def neutron_plugin_api_changed(): # per 17.08 release notes L3HA + DVR is a Newton+ feature _os_release = os_release('neutron-common', base='icehouse') if (use_l3ha() and - CompareOpenStackReleases(_os_release) >= 'newton'): + CompareOpenStackReleases(_os_release) >= 'newton'): install_l3ha_packages() # NOTE(hopem): don't uninstall keepalived if not using l3ha since that diff --git a/hooks/neutron_ovs_utils.py b/hooks/neutron_ovs_utils.py index 66655149..7450865d 100644 --- a/hooks/neutron_ovs_utils.py +++ b/hooks/neutron_ovs_utils.py @@ -129,9 +129,12 @@ OVS_DEFAULT = '/etc/default/openvswitch-switch' DPDK_INTERFACES = '/etc/dpdk/interfaces' NEUTRON_SRIOV_AGENT_CONF = os.path.join(NEUTRON_CONF_DIR, 'plugins/ml2/sriov_agent.ini') -NEUTRON_SRIOV_INIT_SCRIPT = os.path.join('/etc/init.d', +NEUTRON_SRIOV_INIT_SCRIPT = os.path.join('/usr/local/bin', 'neutron-openvswitch-' 'networking-sriov.sh') +NEUTRON_SRIOV_INIT_PY_SCRIPT = os.path.join('/usr/local/bin', + 'neutron_openvswitch_' + 'networking_sriov.py') NEUTRON_SRIOV_INIT_DEFAULT = os.path.join('/etc/default', 'neutron-openvswitch-' 'networking-sriov') @@ -216,14 +219,6 @@ SRIOV_RESOURCE_MAP = OrderedDict([ 'services': [], 'contexts': [neutron_ovs_context.OVSPluginContext()], }), - (NEUTRON_SRIOV_INIT_SCRIPT, { - 'services': [], - 'contexts': [], - }), - (NEUTRON_SRIOV_SYSTEMD_UNIT, { - 'services': [], - 'contexts': [], - }), (NEUTRON_SRIOV_UPSTART_CONF, { 'services': [], 'contexts': [], @@ -288,7 +283,7 @@ def determine_packages(): _os_release = os_release('neutron-common', base='icehouse') # per 17.08 release notes L3HA + DVR is a Newton+ feature if (use_l3ha() and - CompareOpenStackReleases(_os_release) >= 'newton'): + CompareOpenStackReleases(_os_release) >= 'newton'): pkgs.extend(L3HA_PACKAGES) if enable_local_dhcp(): pkgs.extend(DHCP_PACKAGES) @@ -502,6 +497,16 @@ def install_tmpfilesd(): subprocess.check_call(['systemd-tmpfiles', '--create']) +def install_sriov_systemd_files(): + '''Install SR-IOV systemd files''' + shutil.copy('files/neutron_openvswitch_networking_sriov.py', + '/usr/local/bin') + shutil.copy('files/neutron-openvswitch-networking-sriov.sh', + '/usr/local/bin') + shutil.copy('files/neutron-openvswitch-networking-sriov.service', + '/lib/systemd/system') + + def configure_ovs(): status_set('maintenance', 'Configuring ovs') if not service_running('openvswitch-switch'): @@ -624,9 +629,8 @@ def configure_sriov(): if not enable_sriov(): return - # make sure init script has correct mode and that boot time execution - # is enabled - os.chmod(NEUTRON_SRIOV_INIT_SCRIPT, 0o755) + install_sriov_systemd_files() + # make sure that boot time execution is enabled service('enable', 'neutron-openvswitch-networking-sriov') devices = PCINetDevices() diff --git a/templates/kilo/neutron-openvswitch-networking-sriov b/templates/kilo/neutron-openvswitch-networking-sriov index c627c419..6d398c14 100644 --- a/templates/kilo/neutron-openvswitch-networking-sriov +++ b/templates/kilo/neutron-openvswitch-networking-sriov @@ -20,7 +20,7 @@ VFS_BLANKET=auto # List of : tuples for configuration of specific NICs # -#VFS_LIST=ens3p0:16 ens4p0:16 +#VFS_LIST="ens3p0:16 ens4p0:16" {% if sriov_vfs_list -%} VFS_LIST="{{ sriov_vfs_list }}" {% endif -%} diff --git a/templates/kilo/neutron-openvswitch-networking-sriov.conf b/templates/kilo/neutron-openvswitch-networking-sriov.conf index a66cc5d6..d05a8d4b 100644 --- a/templates/kilo/neutron-openvswitch-networking-sriov.conf +++ b/templates/kilo/neutron-openvswitch-networking-sriov.conf @@ -6,6 +6,6 @@ start on virtual-filesystems task console log script - [ -x "/etc/init.d/neutron-openvswitch-networking-sriov.sh" ] || exit 0 - exec /etc/init.d/neutron-openvswitch-networking-sriov.sh start + [ -x "/usr/local/bin/neutron-openvswitch-networking-sriov.sh" ] || exit 0 + exec /usr/local/bin/neutron-openvswitch-networking-sriov.sh start end script diff --git a/templates/kilo/neutron-openvswitch-networking-sriov.sh b/templates/kilo/neutron-openvswitch-networking-sriov.sh deleted file mode 100755 index 3576e278..00000000 --- a/templates/kilo/neutron-openvswitch-networking-sriov.sh +++ /dev/null @@ -1,121 +0,0 @@ -#!/bin/sh - -### BEGIN INIT INFO -# Provides: networking-sriov -# Required-Start: mountkernfs $local_fs -# Required-Stop: $local_fs -# Default-Start: S -# Default-Stop: 0 6 -# Short-Description: Configure SRIOV Virtual Functions -### END INIT INFO - -# Authors: Frode Nordahl - -DESC="Configure SRIOV Virtual Functions" - -. /lib/lsb/init-functions - -# Include defaults if available -if [ -f /etc/default/neutron-openvswitch-networking-sriov ] ; then - . /etc/default/neutron-openvswitch-networking-sriov -fi - -if [ -z "${ENABLE}" ]; then - ENABLE=0 -fi - -if [ -z "${VFS_BLANKET}" ]; then - VFS_BLANKET=auto -fi - -# Exit if feature is not enabled -[ $ENABLE -gt 0 ] || exit 0 - -do_wait_for_vfs() { - # Wait for VFs to be created - PCI_DEVICE_PATH=$1 - NUMVFS=$2 - [ -z "${DEBUG}" ] || echo "do_wait_for_vfs ${PCI_DEVICE_PATH} ${NUMVFS}" - while [ `ls -1 "${PCI_DEVICE_PATH}/" | wc -l` -lt "${NUMVFS}" ]; do - [ -z "${DEBUG}" ] || echo wait... - sleep 0.05 - done -} - -do_configure_vfs() { - if [ ! -z "${VFS_LIST}" ]; then - # Do configuration according to list of : tuples - OIFS="$IFS" - echo "${VFS_LIST}" | \ - while IFS=':' read DEVICE NUMVFS; do - # Check whether we should stop - [ -z "${STOP}" ] || NUMVFS=0 - [ -z "${DEBUG}" ] || echo "echo ${NUMVFS} \> /sys/class/net/${DEVICE}/device/sriov_numvfs" - echo "${NUMVFS}" > "/sys/class/net/${DEVICE}/device/sriov_numvfs" - do_wait_for_vfs "/sys/class/net/${DEVICE}/device" "${NUMVFS}" - done - IFS="$OIFS" - else - # Do blanket configuration - SYSFS_LST=`find /sys/devices -name sriov_totalvfs` - for ENT in $SYSFS_LST; do - DENT=`dirname $ENT` - if [ -d ${DENT}/net ]; then - TOTALVFS=`cat "${ENT}"` - [ -z "${STOP}" ] || VFS_BLANKET=0 - if [ "${VFS_BLANKET}" = "auto" ]; then - # Set sriov_numvfs to value of sriov_toatlvfs for "auto" - NUMVFS=$TOTALVFS - elif [ "${VFS_BLANKET}" -gt "${TOTALVFS}" ]; then - # Set sriov_numvfs to value of sriov_totalvfs if - # requested number is larger than sriov_totalvfs - NUMVFS=$TOTALVFS - else - NUMVFS=$VFS_BLANKET - fi - # Set sriov_numvfs to requested number - [ -z "${DEBUG}" ] || echo "echo ${NUMVFS} \> ${DENT}/sriov_numvfs" - echo "${NUMVFS}" > "${DENT}/sriov_numvfs" - do_wait_for_vfs "${DENT}" "${NUMVFS}" - fi - done - fi -} - -do_start() { - do_configure_vfs -} - -do_stop() { - STOP=1 - do_configure_vfs -} - - -case "$1" in - start) - log_daemon_msg "$DESC" - do_start - ;; - stop) - log_daemon_msg "Un-$DESC" - do_stop - ;; - systemd-start) - do_start - ;; - systemd-stop) - do_stop - ;; - restart) - log_daemon_msg "Re-$DESC" - do_stop - do_start - ;; - *) - N=/etc/init.d/neutron-openvswitch-networking-sriov.sh - echo "Usage: $N {start|stop|restart|systemd-start|systemd-stop}" >&2 - ;; -esac - -exit 0 diff --git a/tox.ini b/tox.ini index 8862afd8..1bccf483 100644 --- a/tox.ini +++ b/tox.ini @@ -41,7 +41,7 @@ deps = -r{toxinidir}/requirements.txt basepython = python3 deps = -r{toxinidir}/requirements.txt -r{toxinidir}/test-requirements.txt -commands = flake8 {posargs} hooks unit_tests tests actions lib +commands = flake8 {posargs} hooks unit_tests tests actions lib files charm-proof [testenv:cover] diff --git a/unit_tests/test_neutron_openvswitch_networking_sriov.py b/unit_tests/test_neutron_openvswitch_networking_sriov.py new file mode 100644 index 00000000..91f49413 --- /dev/null +++ b/unit_tests/test_neutron_openvswitch_networking_sriov.py @@ -0,0 +1,223 @@ +# Copyright 2019 Canonical Ltd +# +# 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 unittest +from unittest.mock import ( + call, + mock_open, + patch +) +import files.neutron_openvswitch_networking_sriov as net_sriov + + +class NeutronOVSNetworkingSriovTest(unittest.TestCase): + + def test_parse_args(self): + results = net_sriov.parse_args(['--start']) + self.assertEqual(results.start, True) + results = net_sriov.parse_args(['--stop']) + self.assertEqual(results.stop, True) + results = net_sriov.parse_args(['--restart']) + self.assertEqual(results.restart, True) + args = [ + '--start', + '--vfs', + 'eth0:8 eth1:4', + '--vfs-blanket', + '8' + ] + results = net_sriov.parse_args(args) + self.assertEqual(results.vfs, 'eth0:8 eth1:4') + self.assertEqual(results.vfs_blanket, '8') + + def test_parse_vfs(self): + vfs = '' + result = net_sriov.parse_vfs(vfs) + expected = {} + self.assertEqual(result, expected) + vfs = 'eth0:8 eth1:8' + result = net_sriov.parse_vfs(vfs) + expected = { + 'eth0': '8', + 'eth1': '8' + } + self.assertEqual(result, expected) + + @patch('files.neutron_openvswitch_networking_sriov.write_sriov_numvfs') + def test_configure_vfs_with_vfs(self, m_write_vfs): + vfs = { + 'eth0': '8' + } + net_sriov.configure_vfs(vfs) + m_write_vfs.assert_called_once_with( + '/sys/class/net/eth0/device/', + '8' + ) + + @patch('files.neutron_openvswitch_networking_sriov.get_totalvfs') + @patch('files.neutron_openvswitch_networking_sriov.find_sriov_devices') + @patch('files.neutron_openvswitch_networking_sriov.write_sriov_numvfs') + def test_configure_vfs_with_blanket_auto( + self, + m_write_vfs, + m_find, + m_totvfs): + m_find.return_value = [ + '/dev/one', + '/dev/two' + ] + m_totvfs.return_value = '8' + net_sriov.configure_vfs({}, 'auto') + m_write_vfs.assert_has_calls([ + call('/dev/one', '8'), + call('/dev/two', '8') + ]) + + @patch('files.neutron_openvswitch_networking_sriov.get_totalvfs') + @patch('files.neutron_openvswitch_networking_sriov.find_sriov_devices') + @patch('files.neutron_openvswitch_networking_sriov.write_sriov_numvfs') + def test_configure_vfs_with_blanket_too_big( + self, + m_write_vfs, + m_find, + m_totvfs): + m_find.return_value = [ + '/dev/one', + '/dev/two' + ] + m_totvfs.return_value = '2' + net_sriov.configure_vfs({}, '8') + m_write_vfs.assert_has_calls([ + call('/dev/one', '2'), + call('/dev/two', '2') + ]) + + @patch('files.neutron_openvswitch_networking_sriov.get_totalvfs') + @patch('files.neutron_openvswitch_networking_sriov.find_sriov_devices') + @patch('files.neutron_openvswitch_networking_sriov.write_sriov_numvfs') + def test_configure_vfs_with_blanket_smaller_than_totalvfs( + self, + m_write_vfs, + m_find, + m_totvfs): + m_find.return_value = [ + '/dev/one', + '/dev/two' + ] + m_totvfs.return_value = '8' + net_sriov.configure_vfs({}, '2') + m_write_vfs.assert_has_calls([ + call('/dev/one', '2'), + call('/dev/two', '2') + ]) + + @patch('time.sleep') + @patch('os.listdir') + def test_wait_for_vfs(self, m_listdir, m_sleep): + dev_list = [ + 'subsystem_device', + 'subsystem_vendor', + 'uevent', + 'vendor', + 'virtfn0', + 'virtfn1', + 'virtfn2', + 'virtfn3', + ] + m_listdir.return_value = dev_list + net_sriov.wait_for_vfs('dev', '4') + m_sleep.assert_called_once_with(0.05) + + @patch('os.walk') + def test_find_sriov_devices(self, m_walk): + m_walk.return_value = [ + ('/one', ('dir1', 'dir2'), ('file')), + ('/one/dir1', (), ('sriov_totalvfs')), + ('/one/dir2', (), ('sriov_totalvfs')) + ] + results = net_sriov.find_sriov_devices() + expected = [ + '/one/dir1', + '/one/dir2' + ] + self.assertEqual(results, expected) + + @patch('builtins.open', new_callable=mock_open) + def test_get_totalvfs(self, m_open): + net_sriov.get_totalvfs('/dev/some') + m_open.assert_called_once_with( + '/dev/some/sriov_totalvfs' + ) + + @patch('files.neutron_openvswitch_networking_sriov.wait_for_vfs') + @patch('builtins.open', new_callable=mock_open) + def test_write_sriov_numvfs(self, m_open, m_wait_vfs): + net_sriov.write_sriov_numvfs('/dev/sriov', '8') + m_open.assert_called_once_with( + '/dev/sriov/sriov_numvfs', + 'w' + ) + m_wait_vfs.assert_called_once_with( + '/dev/sriov', + '8' + ) + + @patch('files.neutron_openvswitch_networking_sriov.start') + @patch('files.neutron_openvswitch_networking_sriov.stop') + def test_restart(self, m_stop, m_start): + net_sriov.restart([], 'auto') + m_stop.assert_called_once_with([]) + m_start.assert_called_once_with([], 'auto') + + @patch('files.neutron_openvswitch_networking_sriov.configure_vfs') + def test_start(self, m_config): + net_sriov.start([], 'auto') + m_config.assert_called_once_with([], 'auto') + + @patch('files.neutron_openvswitch_networking_sriov.configure_vfs') + def test_stop(self, m_config): + net_sriov.stop([]) + m_config.assert_called_once_with([], stop=True) + + @patch('files.neutron_openvswitch_networking_sriov.restart') + def test_main_with_restart(self, m_restart): + args = ['--restart', '--vfs', 'eth0:8 eth1:2', '--vfs-blanket', 'auto'] + net_sriov.main(args) + vfs = { + 'eth0': '8', + 'eth1': '2' + } + vfs_blanket = 'auto' + m_restart.assert_called_once_with(vfs, vfs_blanket) + + @patch('files.neutron_openvswitch_networking_sriov.start') + def test_main_with_start(self, m_start): + args = ['--start', '--vfs', 'eth0:8 eth1:2', '--vfs-blanket', 'auto'] + net_sriov.main(args) + vfs = { + 'eth0': '8', + 'eth1': '2' + } + vfs_blanket = 'auto' + m_start.assert_called_once_with(vfs, vfs_blanket) + + @patch('files.neutron_openvswitch_networking_sriov.stop') + def test_main_with_stop(self, m_stop): + args = ['--stop', '--vfs', 'eth0:8 eth1:2', '--vfs-blanket', 'auto'] + net_sriov.main(args) + vfs = { + 'eth0': '8', + 'eth1': '2' + } + m_stop.assert_called_once_with(vfs) diff --git a/unit_tests/test_neutron_ovs_hooks.py b/unit_tests/test_neutron_ovs_hooks.py index 27691ab8..12312aca 100644 --- a/unit_tests/test_neutron_ovs_hooks.py +++ b/unit_tests/test_neutron_ovs_hooks.py @@ -75,6 +75,7 @@ class NeutronOVSHooksTests(CharmTestCase): self._call_hook('install') self.install_packages.assert_called_with() + @patch('neutron_ovs_hooks.enable_sriov', MagicMock(return_value=False)) @patch.object(hooks, 'restart_map') @patch.object(hooks, 'restart_on_change') def test_migrate_ovs_default_file(self, mock_restart, mock_restart_map): diff --git a/unit_tests/test_neutron_ovs_utils.py b/unit_tests/test_neutron_ovs_utils.py index f9386cb5..d09dd79c 100644 --- a/unit_tests/test_neutron_ovs_utils.py +++ b/unit_tests/test_neutron_ovs_utils.py @@ -862,8 +862,9 @@ class TestNeutronOVSUtils(CharmTestCase): mock_pci_devices.get_device_from_interface_name.side_effect = \ lambda x: self.pci_devices.get(x) + @patch('shutil.copy') @patch('os.chmod') - def test_configure_sriov_auto(self, _os_chmod): + def test_configure_sriov_auto(self, _os_chmod, _sh_copy): self.os_release.return_value = 'mitaka' _config = { 'enable-sriov': True, @@ -881,8 +882,9 @@ class TestNeutronOVSUtils(CharmTestCase): ) self.assertTrue(self.remote_restart.called) + @patch('shutil.copy') @patch('os.chmod') - def test_configure_sriov_auto_mapping(self, _os_chmod): + def test_configure_sriov_auto_mapping(self, _os_chmod, _sh_copy): self.os_release.return_value = 'mitaka' _config = { 'enable-sriov': True, @@ -899,8 +901,9 @@ class TestNeutronOVSUtils(CharmTestCase): ) self.assertTrue(self.remote_restart.called) + @patch('shutil.copy') @patch('os.chmod') - def test_configure_sriov_numvfs(self, _os_chmod): + def test_configure_sriov_numvfs(self, _os_chmod, _sh_copy): self.os_release.return_value = 'mitaka' _config = { 'enable-sriov': True, @@ -915,8 +918,9 @@ class TestNeutronOVSUtils(CharmTestCase): self.assertTrue(self.remote_restart.called) + @patch('shutil.copy') @patch('os.chmod') - def test_configure_sriov_numvfs_per_device(self, _os_chmod): + def test_configure_sriov_numvfs_per_device(self, _os_chmod, _sh_copy): self.os_release.return_value = 'kilo' _config = { 'enable-sriov': True, @@ -931,8 +935,9 @@ class TestNeutronOVSUtils(CharmTestCase): self.assertTrue(self.remote_restart.called) + @patch('shutil.copy') @patch('os.chmod') - def test_configure_sriov_auto_avoid_recall(self, _os_chmod): + def test_configure_sriov_auto_avoid_recall(self, _os_chmod, _sh_copy): self.os_release.return_value = 'mitaka' _config = { 'enable-sriov': True,