X-Git-Url: https://gerrit.opnfv.org/gerrit/gitweb?a=blobdiff_plain;f=vswitches%2Fovs.py;h=be7b77dfa8fa19551e84610dd19d47bb9a5a6606;hb=7820695ec10db06f7f486902ef3ef04b2bc40da3;hp=115ab19bd8eea2bc57cbaf0c8fb240c6a58c04fd;hpb=7cded121ab8291d742ea9d5324f93cae34dc10e5;p=vswitchperf.git diff --git a/vswitches/ovs.py b/vswitches/ovs.py index 115ab19b..be7b77df 100644 --- a/vswitches/ovs.py +++ b/vswitches/ovs.py @@ -1,4 +1,4 @@ -# Copyright 2015-2016 Intel Corporation. +# Copyright 2015-2018 Intel Corporation, Tieto and Others. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -17,19 +17,18 @@ import logging import os -import pexpect import re import time +import datetime +import random +import pexpect from conf import settings from src.ovs import OFBridge, flow_key, flow_match -from tools import tasks from vswitches.vswitch import IVSwitch - -_OVS_VAR_DIR = settings.getValue('OVS_VAR_DIR') -_OVS_ETC_DIR = settings.getValue('OVS_ETC_DIR') - - +from tools import tasks +from tools.module_manager import ModuleManager +# pylint: disable=too-many-public-methods class IVSwitchOvs(IVSwitch, tasks.Process): """Open vSwitch base class implementation @@ -37,23 +36,28 @@ class IVSwitchOvs(IVSwitch, tasks.Process): implementation. For generic information of the nature of the methods, see the interface. """ - _logfile = os.path.join(settings.getValue('LOG_DIR'), settings.getValue('LOG_FILE_VSWITCHD')) - _ovsdb_pidfile_path = os.path.join(settings.getValue('LOG_DIR'), "ovsdb_pidfile.pid") - _vswitchd_pidfile_path = os.path.join(settings.getValue('LOG_DIR'), "vswitchd_pidfile.pid") _proc_name = 'ovs-vswitchd' def __init__(self): """See IVswitch for general description """ + 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__) - self._expect = None + # 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', os.path.join(settings.getValue('OVS_DIR'), - 'vswitchd', 'ovs-vswitchd')] + self._cmd_template = ['sudo', '-E', settings.getValue('TOOLS')['ovs-vswitchd']] + self._stamp = None + self._module_manager = ModuleManager() def start(self): """ Start ``ovsdb-server`` and ``ovs-vswitchd`` instance. @@ -62,6 +66,10 @@ class IVSwitchOvs(IVSwitch, tasks.Process): """ self._logger.info("Starting vswitchd...") + # insert kernel modules if required + if 'vswitch_modules' in settings.getValue('TOOLS'): + self._module_manager.insert_modules(settings.getValue('TOOLS')['vswitch_modules']) + self._cmd = self._cmd_template + self._vswitchd_args # DB must be started before vswitchd @@ -83,16 +91,48 @@ class IVSwitchOvs(IVSwitch, tasks.Process): self._logger.info("Vswitchd...Started.") + def restart(self): + """ Restart ``ovs-vswitchd`` instance. ``ovsdb-server`` is not restarted. + + :raises: pexpect.EOF, pexpect.TIMEOUT + """ + self._logger.info("Restarting vswitchd...") + if os.path.isfile(self._vswitchd_pidfile_path): + self._logger.info('Killing ovs-vswitchd...') + with open(self._vswitchd_pidfile_path, "r") as pidfile: + vswitchd_pid = pidfile.read().strip() + tasks.terminate_task(vswitchd_pid, logger=self._logger) + + try: + tasks.Process.start(self) + self.relinquish() + except (pexpect.EOF, pexpect.TIMEOUT) as exc: + logging.error("Exception during VSwitch start.") + self._kill_ovsdb() + raise exc + self._logger.info("Vswitchd...Started.") + def configure(self): """ Configure vswitchd through ovsdb if needed """ pass + # Method could be a function + # pylint: disable=no-self-use + def get_version(self): + """See IVswitch for general description + """ + # OVS version can be read offline + return [] + def stop(self): """See IVswitch for general description """ + for switch_name in list(self._bridges): + self.del_switch(switch_name) self._logger.info("Terminating vswitchd...") self.kill() + self._bridges = {} self._logger.info("Vswitchd...Terminated.") def add_switch(self, switch_name, params=None): @@ -109,6 +149,9 @@ class IVSwitchOvs(IVSwitch, tasks.Process): """See IVswitch for general description """ bridge = self._bridges[switch_name] + bridge.del_flow({}) + for port in list(bridge.get_ports()): + bridge.del_port(port) self._bridges.pop(switch_name) bridge.destroy() @@ -122,6 +165,38 @@ class IVSwitchOvs(IVSwitch, tasks.Process): """ raise NotImplementedError + def add_veth_pair_port(self, switch_name=None, remote_switch_name=None, + local_opts=None, remote_opts=None): + """Creates veth-pair port between 'switch_name' and 'remote_switch_name' + + """ + if switch_name is None or remote_switch_name is None: + return None + + bridge = self._bridges[switch_name] + remote_bridge = self._bridges[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 + remote_port_name = remote_switch_name + '-' + switch_name + '-' + pcount + local_params = ['--', 'set', 'Interface', local_port_name, + 'type=patch', + 'options:peer=' + remote_port_name] + remote_params = ['--', 'set', 'Interface', remote_port_name, + 'type=patch', + 'options:peer=' + local_port_name] + + if local_opts is not None: + local_params = local_params + local_opts + + if remote_opts is not None: + remote_params = remote_params + remote_opts + + local_of_port = bridge.add_port(local_port_name, local_params) + remote_of_port = remote_bridge.add_port(remote_port_name, remote_params) + return [(local_port_name, local_of_port), + (remote_port_name, remote_of_port)] + def add_tunnel_port(self, switch_name, remote_ip, tunnel_type='vxlan', params=None): """Creates tunneling port """ @@ -264,14 +339,20 @@ class IVSwitchOvs(IVSwitch, tasks.Process): """ self._logger.info('Resetting system after last run...') - tasks.run_task(['sudo', 'rm', '-rf', _OVS_VAR_DIR], self._logger) - tasks.run_task(['sudo', 'mkdir', '-p', _OVS_VAR_DIR], self._logger) - tasks.run_task(['sudo', 'rm', '-rf', _OVS_ETC_DIR], self._logger) - tasks.run_task(['sudo', 'mkdir', '-p', _OVS_ETC_DIR], self._logger) - - tasks.run_task(['sudo', 'rm', '-f', - os.path.join(_OVS_ETC_DIR, 'conf.db')], - self._logger) + # create a backup of ovs_var_tmp and ovs_etc_tmp; It is + # essential for OVS installed from binary packages. + self._stamp = '{:%Y%m%d_%H%M%S}_{}'.format(datetime.datetime.now(), + random.randrange(1000, 9999)) + for tmp_dir in ['ovs_var_tmp', 'ovs_etc_tmp']: + if os.path.exists(settings.getValue('TOOLS')[tmp_dir]): + orig_dir = os.path.normpath(settings.getValue('TOOLS')[tmp_dir]) + self._logger.info('Creating backup of %s directory...', tmp_dir) + tasks.run_task(['sudo', 'mv', orig_dir, '{}.{}'.format(orig_dir, self._stamp)], + self._logger) + + # create fresh tmp dirs + tasks.run_task(['sudo', 'mkdir', '-p', settings.getValue('TOOLS')['ovs_var_tmp']], self._logger) + tasks.run_task(['sudo', 'mkdir', '-p', settings.getValue('TOOLS')['ovs_etc_tmp']], self._logger) self._logger.info('System reset after last run.') @@ -280,21 +361,18 @@ class IVSwitchOvs(IVSwitch, tasks.Process): :returns: None """ - ovsdb_tool_bin = os.path.join( - settings.getValue('OVS_DIR'), 'ovsdb', 'ovsdb-tool') + ovsdb_tool_bin = settings.getValue('TOOLS')['ovsdb-tool'] tasks.run_task(['sudo', ovsdb_tool_bin, 'create', - os.path.join(_OVS_ETC_DIR, 'conf.db'), - os.path.join(settings.getValue('OVS_DIR'), 'vswitchd', - 'vswitch.ovsschema')], + os.path.join(settings.getValue('TOOLS')['ovs_etc_tmp'], 'conf.db'), + settings.getValue('TOOLS')['ovsschema']], self._logger, 'Creating ovsdb configuration database...') - ovsdb_server_bin = os.path.join( - settings.getValue('OVS_DIR'), 'ovsdb', 'ovsdb-server') + ovsdb_server_bin = settings.getValue('TOOLS')['ovsdb-server'] tasks.run_background_task( ['sudo', ovsdb_server_bin, - '--remote=punix:%s' % os.path.join(_OVS_VAR_DIR, 'db.sock'), + '--remote=punix:%s' % os.path.join(settings.getValue('TOOLS')['ovs_var_tmp'], 'db.sock'), '--remote=db:Open_vSwitch,Open_vSwitch,manager_options', '--pidfile=' + self._ovsdb_pidfile_path, '--overwrite-pidfile'], self._logger, @@ -309,24 +387,34 @@ 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) + # restore original content of ovs_var_tmp and ovs_etc_tmp; It is + # essential for OVS installed from binary packages. + if self._stamp: + for tmp_dir in ['ovs_var_tmp', 'ovs_etc_tmp']: + orig_dir = os.path.normpath(settings.getValue('TOOLS')[tmp_dir]) + if os.path.exists('{}.{}'.format(orig_dir, self._stamp)): + self._logger.info('Restoring backup of %s directory...', tmp_dir) + tasks.run_task(['sudo', 'rm', '-rf', orig_dir], self._logger) + tasks.run_task(['sudo', 'mv', '{}.{}'.format(orig_dir, self._stamp), orig_dir], + self._logger) + @staticmethod def get_db_sock_path(): """Method returns location of db.sock file :returns: path to db.sock file. """ - return os.path.join(_OVS_VAR_DIR, 'db.sock') + return os.path.join(settings.getValue('TOOLS')['ovs_var_tmp'], 'db.sock') # # validate methods required for integration testcases # - - def validate_add_switch(self, result, switch_name, 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] @@ -335,7 +423,9 @@ class IVSwitchOvs(IVSwitch, tasks.Process): assert re.search('Bridge ["\']?%s["\']?' % switch_name, output[0]) is not None return True - def validate_del_switch(self, result, switch_name): + # Method could be a function + # pylint: disable=no-self-use + def validate_del_switch(self, _dummy_result, switch_name): """Validate removal of switch """ bridge = OFBridge('tmp') @@ -359,7 +449,7 @@ class IVSwitchOvs(IVSwitch, tasks.Process): """ return self.validate_add_phy_port(result, switch_name) - def validate_del_port(self, 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] @@ -368,11 +458,12 @@ class IVSwitchOvs(IVSwitch, tasks.Process): assert 'Port "%s"' % port_name not in output[0] return True - def validate_add_flow(self, result, switch_name, flow, cache='off'): + def validate_add_flow(self, _dummy_result, switch_name, flow, _dummy_cache='off'): """ Validate insertion of the flow into the switch """ + if 'idle_timeout' in flow: - del(flow['idle_timeout']) + del flow['idle_timeout'] # Note: it should be possible to call `ovs-ofctl dump-flows switch flow` # to verify flow insertion, but it doesn't accept the same flow syntax @@ -388,39 +479,44 @@ class IVSwitchOvs(IVSwitch, tasks.Process): return True return False - def validate_del_flow(self, 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(result, switch_name, flow) + return not self.validate_add_flow(_dummy_result, switch_name, flow) - def validate_dump_flows(self, result, switch_name): + def validate_dump_flows(self, _dummy_result, _dummy_switch_name): """ Validate call of flow dump """ return True - def validate_disable_rstp(self, result, switch_name): + def validate_disable_rstp(self, _dummy_result, switch_name): """ Validate rstp disable """ bridge = self._bridges[switch_name] return 'rstp_enable : false' in ''.join(bridge.bridge_info()) - def validate_enable_rstp(self, result, switch_name): + def validate_enable_rstp(self, _dummy_result, switch_name): """ Validate rstp enable """ bridge = self._bridges[switch_name] return 'rstp_enable : true' in ''.join(bridge.bridge_info()) - def validate_disable_stp(self, result, switch_name): + def validate_disable_stp(self, _dummy_result, switch_name): """ Validate stp disable """ bridge = self._bridges[switch_name] return 'stp_enable : false' in ''.join(bridge.bridge_info()) - def validate_enable_stp(self, result, switch_name): + def validate_enable_stp(self, _dummy_result, switch_name): """ Validate stp enable """ bridge = self._bridges[switch_name] return 'stp_enable : true' in ''.join(bridge.bridge_info()) + + def validate_restart(self, _dummy_result): + """ Validate restart + """ + return True