diff options
Diffstat (limited to 'vswitches')
-rw-r--r-- | vswitches/__init__.py | 1 | ||||
-rw-r--r-- | vswitches/ovs.py | 239 | ||||
-rw-r--r-- | vswitches/ovs_dpdk_vhost.py | 32 | ||||
-rw-r--r-- | vswitches/ovs_vanilla.py | 25 | ||||
-rw-r--r-- | vswitches/vpp_dpdk_vhost.py | 116 | ||||
-rw-r--r-- | vswitches/vswitch.py | 59 |
6 files changed, 261 insertions, 211 deletions
diff --git a/vswitches/__init__.py b/vswitches/__init__.py index a34475be..20a715e0 100644 --- a/vswitches/__init__.py +++ b/vswitches/__init__.py @@ -17,4 +17,3 @@ This package contains an interface the VSPERF core uses for controlling vSwitches and vSwitch-specific implementation modules of this interface. """ - diff --git a/vswitches/ovs.py b/vswitches/ovs.py index 76cabb0d..6dbf0cf8 100644 --- a/vswitches/ovs.py +++ b/vswitches/ovs.py @@ -1,4 +1,4 @@ -# Copyright 2015-2017 Intel Corporation. +# Copyright 2015-2018 Intel Corporation., Tieto # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,12 +15,13 @@ """VSPERF Open vSwitch base class """ -import logging import os import re import time import datetime import random +import socket +import netaddr import pexpect from conf import settings @@ -28,6 +29,10 @@ from src.ovs import OFBridge, flow_key, flow_match from vswitches.vswitch import IVSwitch from tools import tasks from tools.module_manager import ModuleManager + +# enable caching of flows if their number exceeds given limit +_CACHE_FLOWS_LIMIT = 10 + # pylint: disable=too-many-public-methods class IVSwitchOvs(IVSwitch, tasks.Process): """Open vSwitch base class implementation @@ -41,23 +46,31 @@ class IVSwitchOvs(IVSwitch, tasks.Process): def __init__(self): """See IVswitch for general description """ + super().__init__() self._logfile = os.path.join(settings.getValue('LOG_DIR'), settings.getValue('LOG_FILE_VSWITCHD')) self._ovsdb_pidfile_path = os.path.join(settings.getValue('TOOLS')['ovs_var_tmp'], "ovsdb-server.pid") self._vswitchd_pidfile_path = os.path.join(settings.getValue('TOOLS')['ovs_var_tmp'], "{}.pid".format(self._proc_name)) - self._logger = logging.getLogger(__name__) # sign '|' must be escaped or avoided, otherwise it is handled as 'or' by regex self._expect = r'bridge.INFO.{}'.format(self._proc_name) - self._timeout = 30 - self._bridges = {} self._vswitchd_args = ['--pidfile=' + self._vswitchd_pidfile_path, '--overwrite-pidfile', '--log-file=' + self._logfile] - self._cmd = [] self._cmd_template = ['sudo', '-E', settings.getValue('TOOLS')['ovs-vswitchd']] - self._stamp = None self._module_manager = ModuleManager() + self._flow_template = settings.getValue('OVS_FLOW_TEMPLATE').copy() + self._flow_actions = ['output:{}'] + + # if routing tables are enabled, then flows should go into table 1 + # see design document for details about Routing Tables feature + if settings.getValue('OVS_ROUTING_TABLES'): + # flows should be added into table 1 + self._flow_template.update({'table':'1', 'priority':'1'}) + # and chosen port will be propagated via metadata + self._flow_actions = ['write_actions(output:{})', + 'write_metadata:{}', + 'goto_table:2'] def start(self): """ Start ``ovsdb-server`` and ``ovs-vswitchd`` instance. @@ -85,7 +98,7 @@ class IVSwitchOvs(IVSwitch, tasks.Process): tasks.Process.start(self) self.relinquish() except (pexpect.EOF, pexpect.TIMEOUT) as exc: - logging.error("Exception during VSwitch start.") + self._logger.error("Exception during VSwitch start.") self._kill_ovsdb() raise exc @@ -107,7 +120,7 @@ class IVSwitchOvs(IVSwitch, tasks.Process): tasks.Process.start(self) self.relinquish() except (pexpect.EOF, pexpect.TIMEOUT) as exc: - logging.error("Exception during VSwitch start.") + self._logger.error("Exception during VSwitch start.") self._kill_ovsdb() raise exc self._logger.info("Vswitchd...Started.") @@ -128,26 +141,57 @@ class IVSwitchOvs(IVSwitch, tasks.Process): def stop(self): """See IVswitch for general description """ + for switch_name in list(self._switches): + self.del_switch(switch_name) self._logger.info("Terminating vswitchd...") self.kill() - self._bridges = {} + self._switches = {} self._logger.info("Vswitchd...Terminated.") def add_switch(self, switch_name, params=None): """See IVswitch for general description """ + # create and configure new ovs bridge and delete all default flows bridge = OFBridge(switch_name) bridge.create(params) + bridge.del_flow({}) bridge.set_db_attribute('Open_vSwitch', '.', 'other_config:max-idle', settings.getValue('VSWITCH_FLOW_TIMEOUT')) - self._bridges[switch_name] = bridge + self._switches[switch_name] = bridge + if settings.getValue('OVS_ROUTING_TABLES'): + # table#0 - flows designed to force 5 & 13 tuple matches go here + flow = {'table':'0', 'priority':'1', 'actions': ['goto_table:1']} + bridge.add_flow(flow) + + # table#1 - flows to route packets between ports goes here. The + # chosen port is communicated to subsequent tables by setting the + # metadata value to the egress port number + # + # A placeholder - flows are added into this table by deployments + # or by TestSteps via add_connection() method + + # table#2 - frame modification table. Frame modification flow rules are + # isolated in this table so that they can be turned on or off + # without affecting the routing or tuple-matching flow rules. + flow = {'table':'2', 'priority':'1', 'actions': ['goto_table:3']} + bridge.add_flow(flow) + + # table#3 - egress table + # (NOTE) Billy O'Mahony - the drop action here actually required in + # order to egress the packet. This is the subject of a thread on + # ovs-discuss 2015-06-30. + flow = {'table':'3', 'priority':'1', 'actions': ['drop']} + bridge.add_flow(flow) def del_switch(self, switch_name): """See IVswitch for general description """ - bridge = self._bridges[switch_name] - self._bridges.pop(switch_name) + bridge = self._switches[switch_name] + bridge.del_flow({}) + for port in list(bridge.get_ports()): + bridge.del_port(port) + self._switches.pop(switch_name) bridge.destroy() def add_phy_port(self, switch_name): @@ -166,10 +210,10 @@ class IVSwitchOvs(IVSwitch, tasks.Process): """ if switch_name is None or remote_switch_name is None: - return + return None - bridge = self._bridges[switch_name] - remote_bridge = self._bridges[remote_switch_name] + bridge = self._switches[switch_name] + remote_bridge = self._switches[remote_switch_name] pcount = str(self._get_port_count('type=patch')) # NOTE ::: What if interface name longer than allowed width?? local_port_name = switch_name + '-' + remote_switch_name + '-' + pcount @@ -195,7 +239,7 @@ class IVSwitchOvs(IVSwitch, tasks.Process): def add_tunnel_port(self, switch_name, remote_ip, tunnel_type='vxlan', params=None): """Creates tunneling port """ - bridge = self._bridges[switch_name] + bridge = self._switches[switch_name] pcount = str(self._get_port_count('type=' + tunnel_type)) port_name = tunnel_type + pcount local_params = ['--', 'set', 'Interface', port_name, @@ -211,53 +255,123 @@ class IVSwitchOvs(IVSwitch, tasks.Process): def get_ports(self, switch_name): """See IVswitch for general description """ - bridge = self._bridges[switch_name] + bridge = self._switches[switch_name] ports = list(bridge.get_ports().items()) return [(name, of_port) for (name, (of_port, _)) in ports] def del_port(self, switch_name, port_name): """See IVswitch for general description """ - bridge = self._bridges[switch_name] + bridge = self._switches[switch_name] bridge.del_port(port_name) def add_flow(self, switch_name, flow, cache='off'): """See IVswitch for general description """ - bridge = self._bridges[switch_name] + bridge = self._switches[switch_name] bridge.add_flow(flow, cache=cache) def del_flow(self, switch_name, flow=None): """See IVswitch for general description """ flow = flow or {} - bridge = self._bridges[switch_name] + bridge = self._switches[switch_name] bridge.del_flow(flow) def dump_flows(self, switch_name): """See IVswitch for general description """ - bridge = self._bridges[switch_name] + bridge = self._switches[switch_name] bridge.dump_flows() + def _prepare_flows(self, operation, switch_name, port1, port2, traffic=None): + """Prepare flows for add_connection, del_connection and validate methods + It returns a list of flows based on given parameters. + """ + flows = [] + if operation == 'add': + bridge = self._switches[switch_name] + flow = self._flow_template.copy() + actions = [action.format(bridge.get_ports()[port2][0]) for action in self._flow_actions] + flow.update({'in_port': bridge.get_ports()[port1][0], 'actions': actions}) + # check if stream specific connection(s) should be crated for multistream feature + if traffic and traffic['pre_installed_flows'].lower() == 'yes': + for stream in range(traffic['multistream']): + tmp_flow = flow.copy() + # update flow based on trafficgen settings + if traffic['stream_type'] == 'L2': + dst_mac_value = netaddr.EUI(traffic['l2']['dstmac']).value + tmp_mac = netaddr.EUI(dst_mac_value + stream) + tmp_mac.dialect = netaddr.mac_unix_expanded + tmp_flow.update({'dl_dst':tmp_mac}) + elif traffic['stream_type'] == 'L3': + dst_ip_value = netaddr.IPAddress(traffic['l3']['dstip']).value + tmp_ip = netaddr.IPAddress(dst_ip_value + stream) + tmp_flow.update({'dl_type':'0x0800', 'nw_dst':tmp_ip}) + elif traffic['stream_type'] == 'L4': + tmp_flow.update({'dl_type':'0x0800', + 'nw_proto':socket.getprotobyname(traffic['l3']['proto'].lower()), + 'tp_dst':(traffic['l4']['dstport'] + stream) % 65536}) + flows.append(tmp_flow) + elif traffic and traffic['flow_type'].lower() == 'ip': + flow.update({'dl_type':'0x0800', 'nw_src':traffic['l3']['srcip'], + 'nw_dst':traffic['l3']['dstip']}) + flows.append(flow) + else: + flows.append(flow) + elif operation == 'del' and port1: + bridge = self._switches[switch_name] + flows.append({'in_port': bridge.get_ports()[port1][0]}) + else: + flows.append({}) + + return flows + + def add_connection(self, switch_name, port1, port2, traffic=None): + """See IVswitch for general description + """ + flows = self._prepare_flows('add', switch_name, port1, port2, traffic) + + # enable flows caching for large number of flows + cache = 'on' if len(flows) > _CACHE_FLOWS_LIMIT else 'off' + + for flow in flows: + self.add_flow(switch_name, flow, cache) + + if cache == 'on': + self.add_flow(switch_name, [], cache='flush') + + def del_connection(self, switch_name, port1=None, port2=None): + """See IVswitch for general description + """ + flows = self._prepare_flows('del', switch_name, port1, port2) + + for flow in flows: + self.del_flow(switch_name, flow) + + def dump_connections(self, switch_name): + """See IVswitch for general description + """ + self.dump_flows(switch_name) + def add_route(self, switch_name, network, destination): """See IVswitch for general description """ - bridge = self._bridges[switch_name] + bridge = self._switches[switch_name] bridge.add_route(network, destination) def set_tunnel_arp(self, ip_addr, mac_addr, switch_name): """See IVswitch for general description """ - bridge = self._bridges[switch_name] + bridge = self._switches[switch_name] bridge.set_tunnel_arp(ip_addr, mac_addr, switch_name) def _get_port_count(self, param): """Returns the number of ports having a certain parameter """ cnt = 0 - for k in self._bridges: - pparams = [c for (_, (_, c)) in list(self._bridges[k].get_ports().items())] + for k in self._switches: + pparams = [c for (_, (_, c)) in list(self._switches[k].get_ports().items())] phits = [i for i in pparams if param in i] cnt += len(phits) @@ -271,7 +385,7 @@ class IVSwitchOvs(IVSwitch, tasks.Process): :param switch_name: bridge to disable stp :return: None """ - bridge = self._bridges[switch_name] + bridge = self._switches[switch_name] bridge.set_stp(False) self._logger.info('Sleeping for 50 secs to allow stp to stop.') time.sleep(50) # needs time to disable @@ -282,7 +396,7 @@ class IVSwitchOvs(IVSwitch, tasks.Process): :param switch_name: bridge to enable stp :return: None """ - bridge = self._bridges[switch_name] + bridge = self._switches[switch_name] bridge.set_stp(True) self._logger.info('Sleeping for 50 secs to allow stp to start.') time.sleep(50) # needs time to enable @@ -293,7 +407,7 @@ class IVSwitchOvs(IVSwitch, tasks.Process): :param switch_name: bridge to disable rstp :return: None """ - bridge = self._bridges[switch_name] + bridge = self._switches[switch_name] bridge.set_rstp(False) self._logger.info('Sleeping for 15 secs to allow rstp to stop.') time.sleep(15) # needs time to disable @@ -304,7 +418,7 @@ class IVSwitchOvs(IVSwitch, tasks.Process): :param switch_name: bridge to enable rstp :return: None """ - bridge = self._bridges[switch_name] + bridge = self._switches[switch_name] bridge.set_rstp(True) self._logger.info('Sleeping for 15 secs to allow rstp to start.') time.sleep(15) # needs time to enable @@ -382,7 +496,7 @@ class IVSwitchOvs(IVSwitch, tasks.Process): with open(self._ovsdb_pidfile_path, "r") as pidfile: ovsdb_pid = pidfile.read().strip() - self._logger.info("Killing ovsdb with pid: " + ovsdb_pid) + self._logger.info("Killing ovsdb with pid: %s", ovsdb_pid) if ovsdb_pid: tasks.terminate_task(ovsdb_pid, logger=self._logger) @@ -409,10 +523,10 @@ class IVSwitchOvs(IVSwitch, tasks.Process): # # validate methods required for integration testcases # - def validate_add_switch(self, dummy_result, switch_name, dummy_params=None): + def validate_add_switch(self, _dummy_result, switch_name, _dummy_params=None): """Validate - Create a new logical switch with no ports """ - bridge = self._bridges[switch_name] + bridge = self._switches[switch_name] output = bridge.run_vsctl(['show'], check_error=True) assert not output[1] # there shouldn't be any stderr, but in case assert re.search('Bridge ["\']?%s["\']?' % switch_name, output[0]) is not None @@ -420,7 +534,7 @@ class IVSwitchOvs(IVSwitch, tasks.Process): # Method could be a function # pylint: disable=no-self-use - def validate_del_switch(self, dummy_result, switch_name): + def validate_del_switch(self, _dummy_result, switch_name): """Validate removal of switch """ bridge = OFBridge('tmp') @@ -432,7 +546,7 @@ class IVSwitchOvs(IVSwitch, tasks.Process): def validate_add_phy_port(self, result, switch_name): """ Validate that physical port was added to bridge. """ - bridge = self._bridges[switch_name] + bridge = self._switches[switch_name] output = bridge.run_vsctl(['show'], check_error=True) assert not output[1] # there shouldn't be any stderr, but in case assert re.search('Port ["\']?%s["\']?' % result[0], output[0]) is not None @@ -444,16 +558,39 @@ class IVSwitchOvs(IVSwitch, tasks.Process): """ return self.validate_add_phy_port(result, switch_name) - def validate_del_port(self, dummy_result, switch_name, port_name): + def validate_del_port(self, _dummy_result, switch_name, port_name): """ Validate that port_name was removed from bridge. """ - bridge = self._bridges[switch_name] + bridge = self._switches[switch_name] output = bridge.run_vsctl(['show'], check_error=True) assert not output[1] # there shouldn't be any stderr, but in case assert 'Port "%s"' % port_name not in output[0] return True - def validate_add_flow(self, dummy_result, switch_name, flow, dummy_cache='off'): + def validate_add_connection(self, result, switch_name, port1, port2, traffic=None): + """ Validate that connection was added + """ + for flow in self._prepare_flows('add', switch_name, port1, port2, traffic): + if not self.validate_add_flow(result, switch_name, flow): + return False + + return True + + def validate_del_connection(self, result, switch_name, port1, port2): + """ Validate that connection was deleted + """ + for flow in self._prepare_flows('del', switch_name, port1, port2): + if not self.validate_del_flow(result, switch_name, flow): + return False + + return True + + def validate_dump_connections(self, _dummy_result, _dummy_switch_name): + """ Validate dump connections call + """ + return True + + def validate_add_flow(self, _dummy_result, switch_name, flow, _dummy_cache='off'): """ Validate insertion of the flow into the switch """ @@ -466,7 +603,7 @@ class IVSwitchOvs(IVSwitch, tasks.Process): # get dump of flows and compare them one by one flow_src = flow_key(flow) - bridge = self._bridges[switch_name] + bridge = self._switches[switch_name] output = bridge.run_ofctl(['dump-flows', switch_name], check_error=True) for flow_dump in output[0].split('\n'): if flow_match(flow_dump, flow_src): @@ -474,44 +611,44 @@ class IVSwitchOvs(IVSwitch, tasks.Process): return True return False - def validate_del_flow(self, dummy_result, switch_name, flow=None): + def validate_del_flow(self, _dummy_result, switch_name, flow=None): """ Validate removal of the flow """ if not flow: # what else we can do? return True - return not self.validate_add_flow(dummy_result, switch_name, flow) + return not self.validate_add_flow(_dummy_result, switch_name, flow) - def validate_dump_flows(self, dummy_result, dummy_switch_name): + def validate_dump_flows(self, _dummy_result, _dummy_switch_name): """ Validate call of flow dump """ return True - def validate_disable_rstp(self, dummy_result, switch_name): + def validate_disable_rstp(self, _dummy_result, switch_name): """ Validate rstp disable """ - bridge = self._bridges[switch_name] + bridge = self._switches[switch_name] return 'rstp_enable : false' in ''.join(bridge.bridge_info()) - def validate_enable_rstp(self, dummy_result, switch_name): + def validate_enable_rstp(self, _dummy_result, switch_name): """ Validate rstp enable """ - bridge = self._bridges[switch_name] + bridge = self._switches[switch_name] return 'rstp_enable : true' in ''.join(bridge.bridge_info()) - def validate_disable_stp(self, dummy_result, switch_name): + def validate_disable_stp(self, _dummy_result, switch_name): """ Validate stp disable """ - bridge = self._bridges[switch_name] + bridge = self._switches[switch_name] return 'stp_enable : false' in ''.join(bridge.bridge_info()) - def validate_enable_stp(self, dummy_result, switch_name): + def validate_enable_stp(self, _dummy_result, switch_name): """ Validate stp enable """ - bridge = self._bridges[switch_name] + bridge = self._switches[switch_name] return 'stp_enable : true' in ''.join(bridge.bridge_info()) - def validate_restart(self, dummy_result): + def validate_restart(self, _dummy_result): """ Validate restart """ return True diff --git a/vswitches/ovs_dpdk_vhost.py b/vswitches/ovs_dpdk_vhost.py index 6deb0c25..8da043c6 100644 --- a/vswitches/ovs_dpdk_vhost.py +++ b/vswitches/ovs_dpdk_vhost.py @@ -1,4 +1,4 @@ -# Copyright 2015-2017 Intel Corporation. +# Copyright 2015-2018 Intel Corporation., Tieto # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,7 +15,6 @@ """VSPERF VSwitch implementation using DPDK and vhost ports """ -import logging import subprocess from src.ovs import OFBridge @@ -36,9 +35,7 @@ class OvsDpdkVhost(IVSwitchOvs): """ def __init__(self): - super(OvsDpdkVhost, self).__init__() - self._logger = logging.getLogger(__name__) - + super().__init__() vswitchd_args = [] # legacy DPDK configuration through --dpdk option of vswitchd @@ -104,9 +101,9 @@ class OvsDpdkVhost(IVSwitchOvs): if S.getValue('VSWITCH_AFFINITIZATION_ON') == 1: # Sets the PMD core mask to VSWITCH_PMD_CPU_MASK # for CPU core affinitization - self._bridges[switch_name].set_db_attribute('Open_vSwitch', '.', - 'other_config:pmd-cpu-mask', - S.getValue('VSWITCH_PMD_CPU_MASK')) + self._switches[switch_name].set_db_attribute('Open_vSwitch', '.', + 'other_config:pmd-cpu-mask', + S.getValue('VSWITCH_PMD_CPU_MASK')) def add_phy_port(self, switch_name): """See IVswitch for general description @@ -115,7 +112,7 @@ class OvsDpdkVhost(IVSwitchOvs): The new port is named dpdk<n> where n is an integer starting from 0. """ _nics = S.getValue('NICS') - bridge = self._bridges[switch_name] + bridge = self._switches[switch_name] dpdk_count = self._get_port_count('type=dpdk') if dpdk_count == len(_nics): raise RuntimeError("Can't add phy port! There are only {} ports defined " @@ -144,7 +141,7 @@ class OvsDpdkVhost(IVSwitchOvs): The new port is named dpdkvhost<n> where n is an integer starting from 0 """ - bridge = self._bridges[switch_name] + bridge = self._switches[switch_name] if S.getValue('VSWITCH_VHOSTUSER_SERVER_MODE'): nic_type = 'dpdkvhostuser' @@ -177,18 +174,3 @@ class OvsDpdkVhost(IVSwitchOvs): return True except subprocess.CalledProcessError: return False - - def add_connection(self, switch_name, port1, port2, bidir=False): - """See IVswitch for general description - """ - raise NotImplementedError() - - def del_connection(self, switch_name, port1, port2, bidir=False): - """See IVswitch for general description - """ - raise NotImplementedError() - - def dump_connections(self, switch_name): - """See IVswitch for general description - """ - raise NotImplementedError() diff --git a/vswitches/ovs_vanilla.py b/vswitches/ovs_vanilla.py index 83c52050..d23a0c61 100644 --- a/vswitches/ovs_vanilla.py +++ b/vswitches/ovs_vanilla.py @@ -1,4 +1,4 @@ -# Copyright 2015-2017 Intel Corporation. +# Copyright 2015-2018 Intel Corporation., Tieto # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,7 +15,6 @@ """VSPERF Vanilla OVS implementation """ -import logging import time from conf import settings from vswitches.ovs import IVSwitchOvs @@ -36,9 +35,8 @@ class OvsVanilla(IVSwitchOvs): _vport_id = 0 def __init__(self): - super(OvsVanilla, self).__init__() + super().__init__() self._ports = list(nic['device'] for nic in settings.getValue('NICS')) - self._logger = logging.getLogger(__name__) self._vswitchd_args += ["unix:%s" % self.get_db_sock_path()] self._vswitchd_args += settings.getValue('VSWITCHD_VANILLA_ARGS') @@ -81,7 +79,7 @@ class OvsVanilla(IVSwitchOvs): self._logger.error("Can't detect device name for NIC %s", self._current_id) raise ValueError("Invalid device name for %s" % self._current_id) - bridge = self._bridges[switch_name] + bridge = self._switches[switch_name] port_name = self._ports[self._current_id] params = [] @@ -129,21 +127,6 @@ class OvsVanilla(IVSwitchOvs): tasks.run_task(['sudo', 'ip', 'link', 'set', 'dev', tap_name, 'up'], self._logger, 'Bring up ' + tap_name, False) - bridge = self._bridges[switch_name] + bridge = self._switches[switch_name] of_port = bridge.add_port(tap_name, []) return (tap_name, of_port) - - def add_connection(self, switch_name, port1, port2, bidir=False): - """See IVswitch for general description - """ - raise NotImplementedError() - - def del_connection(self, switch_name, port1, port2, bidir=False): - """See IVswitch for general description - """ - raise NotImplementedError() - - def dump_connections(self, switch_name): - """See IVswitch for general description - """ - raise NotImplementedError() diff --git a/vswitches/vpp_dpdk_vhost.py b/vswitches/vpp_dpdk_vhost.py index 58d6bf51..5d676a01 100644 --- a/vswitches/vpp_dpdk_vhost.py +++ b/vswitches/vpp_dpdk_vhost.py @@ -1,4 +1,4 @@ -# Copyright 2017 Intel Corporation. +# Copyright 2017-2018 Intel Corporation., Tieto # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,7 +15,6 @@ """VSPERF VPP implementation using DPDK and vhostuser vports """ -import logging import os import copy import re @@ -37,19 +36,13 @@ class VppDpdkVhost(IVSwitch, tasks.Process): def __init__(self): """See IVswitch for general description """ + super().__init__() self._logfile = os.path.join(S.getValue('LOG_DIR'), S.getValue('LOG_FILE_VPP')) - self._logger = logging.getLogger(__name__) self._expect = r'vpp#' - self._timeout = 30 - self._vswitch_args = [] - self._cmd = [] self._cmd_template = ['sudo', '-E', S.getValue('TOOLS')['vpp']] - self._stamp = None - self._logger = logging.getLogger(__name__) self._phy_ports = [] self._virt_ports = [] - self._switches = {} self._vpp_ctl = ['sudo', S.getValue('TOOLS')['vppctl']] # configure DPDK NICs @@ -151,7 +144,7 @@ class VppDpdkVhost(IVSwitch, tasks.Process): tasks.Process.start(self) self.relinquish() except (pexpect.EOF, pexpect.TIMEOUT) as exc: - logging.error("Exception during VPP start.") + self._logger.error("Exception during VPP start.") raise exc self._logger.info("VPP...Started.") @@ -205,6 +198,7 @@ class VppDpdkVhost(IVSwitch, tasks.Process): def add_switch(self, switch_name, dummy_params=None): """See IVswitch for general description """ + # pylint: disable=unused-argument if switch_name in self._switches: self._logger.warning("switch %s already exists...", switch_name) else: @@ -221,6 +215,7 @@ class VppDpdkVhost(IVSwitch, tasks.Process): """See IVswitch for general description :raises: RuntimeError """ + # pylint: disable=unused-argument # get list of physical interfaces with PCI addresses vpp_nics = self._get_nic_info(key='Pci') # check if there are any NICs left @@ -239,6 +234,7 @@ class VppDpdkVhost(IVSwitch, tasks.Process): def add_vport(self, dummy_switch_name): """See IVswitch for general description """ + # pylint: disable=unused-argument socket_name = S.getValue('TOOLS')['ovs_var_tmp'] + 'dpdkvhostuser' + str(len(self._virt_ports)) if S.getValue('VSWITCH_VHOSTUSER_SERVER_MODE'): mode = ['server'] @@ -266,21 +262,17 @@ class VppDpdkVhost(IVSwitch, tasks.Process): else: self._logger.warning("Port %s is not configured.", port_name) - def add_l2patch(self, port1, port2, bidir=False): + def add_l2patch(self, port1, port2): """Create l2patch connection between given ports """ self.run_vppctl(['test', 'l2patch', 'rx', port1, 'tx', port2]) - if bidir: - self.run_vppctl(['test', 'l2patch', 'rx', port2, 'tx', port1]) - def add_xconnect(self, port1, port2, bidir=False): + def add_xconnect(self, port1, port2): """Create l2patch connection between given ports """ self.run_vppctl(['set', 'interface', 'l2', 'xconnect', port1, port2]) - if bidir: - self.run_vppctl(['set', 'interface', 'l2', 'xconnect', port2, port1]) - def add_bridge(self, switch_name, port1, port2, dummy_bidir=False): + def add_bridge(self, switch_name, port1, port2): """Add given ports to bridge ``switch_name`` """ self.run_vppctl(['set', 'interface', 'l2', 'bridge', port1, @@ -288,56 +280,59 @@ class VppDpdkVhost(IVSwitch, tasks.Process): self.run_vppctl(['set', 'interface', 'l2', 'bridge', port2, str(self._switches[switch_name])]) - def add_connection(self, switch_name, port1, port2, bidir=False): + def add_connection(self, switch_name, port1, port2, traffic=None): """See IVswitch for general description :raises: RuntimeError """ + if traffic: + self._logger.warning("VPP add_connection() does not support 'traffic' options.") + mode = S.getValue('VSWITCH_VPP_L2_CONNECT_MODE') if mode == 'l2patch': - self.add_l2patch(port1, port2, bidir) + self.add_l2patch(port1, port2) elif mode == 'xconnect': - self.add_xconnect(port1, port2, bidir) + self.add_xconnect(port1, port2) elif mode == 'bridge': self.add_bridge(switch_name, port1, port2) else: - raise RuntimeError('VPP: Unsupported l2 connection mode detected %s', mode) + raise RuntimeError('VPP: Unsupported l2 connection mode detected %s' % mode) - def del_l2patch(self, port1, port2, bidir=False): + def del_l2patch(self, port1, port2): """Remove l2patch connection between given ports :param port1: port to be used in connection :param port2: port to be used in connection - :param bidir: switch between uni and bidirectional traffic """ self.run_vppctl(['test', 'l2patch', 'rx', port1, 'tx', port2, 'del']) - if bidir: - self.run_vppctl(['test', 'l2patch', 'rx', port2, 'tx', port1, 'del']) - def del_xconnect(self, dummy_port1, dummy_port2, dummy_bidir=False): + def del_xconnect(self, port1, port2): """Remove xconnect connection between given ports """ - self._logger.warning('VPP: Removal of l2 xconnect is not implemented.') + self.run_vppctl(['set', 'interface', 'l3', port1]) + self.run_vppctl(['set', 'interface', 'l3', port2]) - def del_bridge(self, dummy_switch_name, dummy_port1, dummy_port2): + def del_bridge(self, _dummy_switch_name, port1, port2): """Remove given ports from the bridge """ - self._logger.warning('VPP: Removal of interfaces from bridge is not implemented.') + self.run_vppctl(['set', 'interface', 'l3', port1]) + self.run_vppctl(['set', 'interface', 'l3', port2]) - def del_connection(self, switch_name, port1, port2, bidir=False): + def del_connection(self, switch_name, port1=None, port2=None): """See IVswitch for general description :raises: RuntimeError """ - mode = S.getValue('VSWITCH_VPP_L2_CONNECT_MODE') - if mode == 'l2patch': - self.del_l2patch(port1, port2, bidir) - elif mode == 'xconnect': - self.del_xconnect(port1, port2, bidir) - elif mode == 'bridge': - self.del_bridge(switch_name, port1, port2) - else: - raise RuntimeError('VPP: Unsupported l2 connection mode detected %s', mode) + if port1 and port2: + mode = S.getValue('VSWITCH_VPP_L2_CONNECT_MODE') + if mode == 'l2patch': + self.del_l2patch(port1, port2) + elif mode == 'xconnect': + self.del_xconnect(port1, port2) + elif mode == 'bridge': + self.del_bridge(switch_name, port1, port2) + else: + raise RuntimeError('VPP: Unsupported l2 connection mode detected %s' % mode) def dump_l2patch(self): """Dump l2patch connections @@ -347,7 +342,7 @@ class VppDpdkVhost(IVSwitch, tasks.Process): def dump_xconnect(self): """Dump l2 xconnect connections """ - self._logger.warning("VPP: Dump of l2 xconnections is not supported.") + self.run_vppctl(['show', 'mode'] + self._phy_ports + self._virt_ports) def dump_bridge(self, switch_name): """Show bridge details @@ -369,7 +364,7 @@ class VppDpdkVhost(IVSwitch, tasks.Process): elif mode == 'bridge': self.dump_bridge(switch_name) else: - raise RuntimeError('VPP: Unsupported l2 connection mode detected %s', mode) + raise RuntimeError('VPP: Unsupported l2 connection mode detected %s' % mode) def run_vppctl(self, args, check_error=False): """Run ``vppctl`` with supplied arguments. @@ -385,50 +380,50 @@ class VppDpdkVhost(IVSwitch, tasks.Process): # # Validate methods # - def validate_add_switch(self, dummy_result, switch_name, dummy_params=None): + def validate_add_switch(self, _dummy_result, switch_name, _dummy_params=None): """Validate - Create a new logical switch with no ports """ return switch_name in self._switches - def validate_del_switch(self, dummy_result, switch_name): + def validate_del_switch(self, _dummy_result, switch_name): """Validate removal of switch """ - return not self.validate_add_switch(dummy_result, switch_name) + return not self.validate_add_switch(_dummy_result, switch_name) - def validate_add_phy_port(self, result, dummy_switch_name): + def validate_add_phy_port(self, result, _dummy_switch_name): """ Validate that physical port was added to bridge. """ return result[0] in self._phy_ports - def validate_add_vport(self, result, dummy_switch_name): + def validate_add_vport(self, result, _dummy_switch_name): """ Validate that virtual port was added to bridge. """ return result[0] in self._virt_ports - def validate_del_port(self, dummy_result, dummy_switch_name, port_name): + def validate_del_port(self, _dummy_result, _dummy_switch_name, port_name): """ Validate that port_name was removed from bridge. """ return not (port_name in self._phy_ports or port_name in self._virt_ports) # pylint: disable=no-self-use - def validate_add_connection(self, dummy_result, dummy_switch_name, dummy_port1, - dummy_port2, dummy_bidir=False): + def validate_add_connection(self, _dummy_result, _dummy_switch_name, _dummy_port1, + _dummy_port2, _dummy_traffic=None): """ Validate that connection was added """ return True - def validate_del_connection(self, dummy_result, dummy_switch_name, dummy_port1, - dummy_port2, dummy_bidir=False): + def validate_del_connection(self, _dummy_result, _dummy_switch_name, _dummy_port1, + _dummy_port2): """ Validate that connection was deleted """ return True - def validate_dump_connections(self, dummy_result, dummy_switch_name): + def validate_dump_connections(self, _dummy_result, _dummy_switch_name): """ Validate dump connections call """ return True - def validate_run_vppctl(self, result, dummy_args, dummy_check_error=False): + def validate_run_vppctl(self, result, _dummy_args, _dummy_check_error=False): """validate execution of ``vppctl`` with supplied arguments. """ # there shouldn't be any stderr @@ -437,21 +432,6 @@ class VppDpdkVhost(IVSwitch, tasks.Process): # # Non implemented methods # - def add_flow(self, switch_name, flow, cache='off'): - """See IVswitch for general description - """ - raise NotImplementedError() - - def del_flow(self, switch_name, flow=None): - """See IVswitch for general description - """ - raise NotImplementedError() - - def dump_flows(self, switch_name): - """See IVswitch for general description - """ - raise NotImplementedError() - def add_route(self, switch_name, network, destination): """See IVswitch for general description """ diff --git a/vswitches/vswitch.py b/vswitches/vswitch.py index efa3a349..a3d4e974 100644 --- a/vswitches/vswitch.py +++ b/vswitches/vswitch.py @@ -1,4 +1,4 @@ -# Copyright 2015-2016 Intel Corporation. +# Copyright 2015-2018 Intel Corporation., Tieto # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -14,12 +14,23 @@ """Generic interface VSPERF uses for controlling a vSwitch """ +import logging class IVSwitch(object): """Interface class that is implemented by vSwitch-specific classes Other methods are called only between start() and stop() """ + def __init__(self): + """Initialization of vswitch class + """ + self._timeout = 30 + self._switches = {} + self._logger = logging.getLogger(__name__) + self._cmd = [] + self._vswitch_args = [] + self._stamp = None + def get_version(self): """Return version of vSwitch and DPDK (if used by vSwitch) This method should be implemented in case, that version @@ -112,58 +123,23 @@ class IVSwitch(object): """ raise NotImplementedError() - def add_flow(self, switch_name, flow, cache='off'): - """Add a flow rule to the logical switch - - :param switch_name: The switch on which to operate - :param flow: Flow description as a dictionary - :param cache: Optional. Specifies if flow should be inserted - to the switch or cached to increase performance during manipulation - with large number of flows. - Values: - 'off' - cache is off and flow is inserted directly to the switch - 'on' - cache is on and flow is inserted into the cache - 'flush' - cache content will be inserted into the switch - - Example flow dictionary: - flow = { - 'in_port': '1', - 'idle_timeout': '0', - 'actions': ['output:3'] - } - """ - raise NotImplementedError() - - def del_flow(self, switch_name, flow=None): - """Delete the flow rule from the logical switch - - :param switch_name: The switch on which to operate - :param flow: Flow description as a dictionary - - For flow dictionary description, see add_flow - For flow==None, all flows are deleted - """ - raise NotImplementedError() - - def add_connection(self, switch_name, port1, port2, bidir=False): + def add_connection(self, switch_name, port1, port2, traffic=None): """Creates connection between given ports. :param switch_name: switch on which to operate :param port1: port to be used in connection :param port2: port to be used in connection - :param bidir: switch between uni and bidirectional traffic :raises: RuntimeError """ raise NotImplementedError() - def del_connection(self, switch_name, port1, port2, bidir=False): + def del_connection(self, switch_name, port1=None, port2=None): """Remove connection between two interfaces. :param switch_name: switch on which to operate :param port1: port to be used in connection :param port2: port to be used in connection - :param bidir: switch between uni and bidirectional traffic :raises: RuntimeError """ @@ -178,13 +154,6 @@ class IVSwitch(object): """ raise NotImplementedError() - def dump_flows(self, switch_name): - """Dump flows from the logical switch - - :param switch_name: The switch on which to operate - """ - raise NotImplementedError() - def add_route(self, switch_name, network, destination): """Add a route for tunneling routing table |