charm-neutron-openvswitch/files/neutron_openvswitch_networking_sriov.py
Nicolas Pochet d02fad482d
Replace SR-IOV systemd unit script
* The goal of this change is to enable the ability to configure only the
VFs that are configured through the charm and not fallback to the
blanket configuration.
* This python version of the script brings unit-tests that fully covers
it.
* Move the the template files to `files` and modify `neutron_ovs_utils`
accordingly.

Closes-Bug: 1832379
Depends-On: https://review.opendev.org/#/c/664837/

Change-Id: I7ad1ebc16883bda23cbad89a852e7e8f88f49c49
2019-06-28 17:37:51 +02:00

221 lines
5.4 KiB
Python
Executable File

#!/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:]))