# Copyright (c) 2016 - 2017, Intel Corporation.
#
# 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.
"""``openvswitch.py``
`Class to abstract openvswitch operations`
Note:
Examples of openvswitch usage in tests::
env.lhost[1].ui.openvswitch.start()
env.lhost[1].ui.openvswitch.stop()
env.lhost[1].ui.openvswitch.restart()
env.lhost[1].ui.openvswitch.add_bridge('br0')
env.lhost[1].ui.openvswitch.get_interface_info('br0')
env.lhost[1].ui.openvswitch.del_interface('br0')
env.lhost[1].ui.openvswitch.set_ovsdb_manager(ptcp=6640)
"""
import re
from testlib.linux import service_lib
from testlib.custom_exceptions import CustomException
from testlib.linux import testpmd
[docs]class OpenvSwitch(object):
SERVICE = 'openvswitch-switch'
[docs] def __init__(self, cli_send_command, switch_map, name_to_switchid_map):
"""Initialize openvswitch class.
"""
super(OpenvSwitch, self).__init__()
self.cli_send_command = cli_send_command
self.switch_map = switch_map
self.name_to_switchid_map = name_to_switchid_map
self.service_manager = service_lib.SpecificServiceManager(self.SERVICE, self.cli_send_command)
[docs] def update_map(self, iface_name, delete=False):
"""Update switch_map and name_to_switchid_map.
Args:
iface_name(str): name of ovs bridge or interface
delete(bool): if True - remove from switch_map and name_to_switchid_map
"""
if delete:
self.switch_map.pop(iface_name)
self.name_to_switchid_map.pop(iface_name)
else:
self.switch_map.update({iface_name: iface_name})
self.name_to_switchid_map.update({iface_name: iface_name})
[docs] def start(self, ovs_dpdk_opts=None):
"""Start openvswitch service
Args:
ovs_dpdk_opts(dict | None): option indicating OVS run with or without DPDK
Note:
If ovs_dpdk_opts is provided, a dictionary with EAL parameters is expected.
Else it is assumed that vanilla OVS service start is expected.
"""
if ovs_dpdk_opts:
ovs_dpdk_opts = 'DPDK_OPTS=other-config:dpdk-init=true other-config:dpdk-extra="{}"'.format(
testpmd.reformat_dpdk_eal_options(**ovs_dpdk_opts))
else:
ovs_dpdk_opts = 'DPDK_OPTS=other-config:dpdk-init=false'
_ovs_env_fname = '/etc/default/{}'.format(self.SERVICE)
self.cli_send_command("echo '# Generated' > {1} && sed -i 'a {0}' {1}".format(ovs_dpdk_opts, _ovs_env_fname))
self.service_manager.start()
# Update switch map
bridges, interfaces = self.get_existing_bridges_interfaces()
[self.update_map(bridge) for bridge in bridges]
[self.update_map(iface) for iface in interfaces]
[docs] def stop(self):
"""Stop openvswitch service.
"""
# Update switch map
bridges, interfaces = self.get_existing_bridges_interfaces()
[self.update_map(bridge, delete=True) for bridge in bridges]
[self.update_map(iface, delete=True) for iface in interfaces]
self.service_manager.stop()
[docs] def restart(self):
"""Restart openvswitch service.
"""
return self.service_manager.restart()
[docs] def status(self, exp_rc=frozenset({0, 3})):
"""Get openvswitch process status.
Args:
exp_rc(int | set | list | frozenset): expected return code
Returns:
named tuple
"""
return self.service_manager.status(expected_rcs=exp_rc)
[docs] def get_status(self):
"""Method for get openvswitch status.
Returns:
list: ovs status 'active' or 'inactive'
"""
output = self.status().stdout
return re.findall(r'Active:\s(\S+)', output)
[docs] def add_bridge(self, br_name):
"""Add new openvswitch bridge.
Args:
br_name(str): name of ovs bridge
"""
self.cli_send_command("ovs-vsctl add-br {}".format(br_name))
self.update_map(br_name)
[docs] def del_bridge(self, br_name):
"""Delete openvswitch bridgew.
Args:
br_name(str): name of ovs bridge
"""
self.cli_send_command(command="ovs-vsctl del-br {}".format(br_name))
self.update_map(br_name, delete=True)
[docs] def add_interface(self, br_name, iface_name):
"""Add new openvswitch interface.
Args:
br_name(str): name of ovs bridge
iface_name(str): name of ovs interface
"""
# options available
command = 'ovs-vsctl add-port {0} {1}'.format(br_name, iface_name)
self.cli_send_command(command)
self.update_map(iface_name)
[docs] def set_table_record(self, table, rec, **kwargs):
"""Set column values in record in specific table.
Args:
table(str): name of ovs table
rec(str): name of ovs record
kwargs(dict): Column values dict to set
"""
cmd = ['ovs-vsctl', 'set', table, rec]
if kwargs:
cmd.append(' '.join('{}={}'.format(x, y) if not isinstance(y, tuple) else '{}:{}={}'.format(x, *y)
for x, y in kwargs.items()))
cmd = ' '.join(cmd)
self.cli_send_command(cmd)
[docs] def get_table_record(self, table, rec, column):
"""Get column value in record in specific table.
Args:
table(str): name of ovs table
rec(str): name of ovs record
column(str): Column value to get
Returns:
str: Returns StdOut of get command
"""
cmd = 'ovs-vsctl get {table} {rec} {col}'.format(table=table, rec=rec, col=column)
return self.cli_send_command(cmd).stdout
[docs] def del_interface(self, br_name, iface_name):
"""Delete interface from openvswitch.
Args:
br_name(str): name of ovs bridge
iface_name(str): name of ovs interface
"""
self.cli_send_command(command="ovs-vsctl del-port {0} {1} ".format(br_name, iface_name))
self.update_map(iface_name, delete=True)
[docs] def set_ovsdb_manager(self, **kwargs):
"""Set connection type to ovsdb.
Args:
kwargs(dict): type of ovsdb connection
Raises:
CustomException
"""
if not kwargs:
raise CustomException("Arguments are required for current method")
else:
for key, value in kwargs.items():
self.cli_send_command(command="ovs-vsctl set-manager {0}:{1}".format(key, value))
[docs] def get_interface_info(self, interface_name):
"""Get ovs interface information from ovsdb.
Args:
interface_name(str): name of ovs interface
Returns:
dict: Output of OVS interface information
"""
output = self.cli_send_command("ovs-vsctl list interface {}".format(interface_name)).stdout
return dict(re.findall(r'(\S+)\s*:\s+(.+)', output))
[docs] def get_interface_statistic(self, iface_name):
"""Get ovs interface statistic from ovsdb.
Args:
iface_name(str): name of ovs interface
Returns:
dict: Output of OVS interface statistics
"""
data = self.get_interface_info(iface_name)
return dict(re.findall(r'{?"*(\w+)"*=(\d+)', data["statistics"]))
[docs] def get_existing_bridges_interfaces(self):
"""Get already existing bridges and interfaces from ovsdb.
Returns:
list: list of existing ovs bridges and interfaces
"""
output = self.cli_send_command(command="ovs-vsctl show").stdout
bridges = re.findall(r'Bridge \"(.+)\"', output)
interfaces = re.findall(r'Interface \"(.+)\"', output)
# Get bridge interfaces
br_interfaces = [item for item in interfaces if item not in bridges]
return bridges, br_interfaces
[docs] def delete_all_bridges(self):
"""Delete all existing bridges from ovsdb.
"""
bridges, _ = self.get_existing_bridges_interfaces()
[self.del_bridge(bridge) for bridge in bridges]
[docs] def add_bond(self, br_name, bond_name, ports):
"""Bond ovs interfaces.
Args:
br_name(str): name of ovs bridge
bond_name(str): name of the bond
ports(list): ports to bond
"""
command = "ovs-vsctl add-bond {0} {1} {2}".format(br_name, bond_name, ' '.join(ports))
self.cli_send_command(command)
[docs] def set_bridge_port_interface(self, inst_type, name, **kwargs):
"""Method set parameters for bridge, port or interface.
Args:
inst_type(str): could be Bridge, Port or Interface
name(str): name of ovs bridge, port or interface
kwargs(dict): options to be set for bridge, port or interface
Raises:
CustomException
"""
if not kwargs:
raise CustomException("Arguments are required for current method")
options = ''.join(map(lambda x: ' {}={}'.format(*x), kwargs.items()))
command = "ovs-vsctl set {0} {1}{2}".format(inst_type, name, options)
self.cli_send_command(command)
[docs] def get_interface_statistic_counter(self, iface_name, counter_name):
"""Get ovs interface statistic from ovsdb.
Args:
iface_name(str): name of ovs interface
counter_name(str): name of ovs interface counter
Returns:
dict: Output of OVS interface statistics
"""
output = self.cli_send_command("ovs-vsctl get Interface {0} statistics:{1}".format(iface_name, counter_name))
return int(output.stdout.strip())