# Copyright 2015-2016 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.
"""VSPERF Open vSwitch base class
"""
import logging
import re
from conf import settings
from vswitches.vswitch import IVSwitch
from src.ovs import OFBridge, flow_key, flow_match
_VSWITCHD_CONST_ARGS = ['--', '--pidfile', '--log-file']
class IVSwitchOvs(IVSwitch):
"""Open vSwitch base class implementation
The method docstrings document only considerations specific to this
implementation. For generic information of the nature of the methods,
see the interface.
"""
def __init__(self):
"""See IVswitch for general description
"""
self._vswitchd = None
self._logger = logging.getLogger(__name__)
self._bridges = {}
self._vswitchd_args = _VSWITCHD_CONST_ARGS
def start(self):
"""See IVswitch for general description
"""
self._logger.info("Starting vswitchd...")
self._vswitchd.start()
self._logger.info("Vswitchd...Started.")
def stop(self):
"""See IVswitch for general description
"""
self._logger.info("Terminating vswitchd...")
self._vswitchd.kill()
self._logger.info("Vswitchd...Terminated.")
def add_switch(self, switch_name, params=None):
"""See IVswitch for general description
"""
bridge = OFBridge(switch_name)
bridge.create(params)
bridge.set_db_attribute('Open_vSwitch', '.',
'other_config:max-idle',
settings.getValue('VSWITCH_FLOW_TIMEOUT'))
self._bridges[switch_name] = bridge
def del_switch(self, switch_name):
"""See IVswitch for general description
"""
bridge = self._bridges[switch_name]
self._bridges.pop(switch_name)
bridge.destroy()
def add_phy_port(self, switch_name):
"""See IVswitch for general description
"""
raise NotImplementedError
def add_vport(self, switch_name):
"""See IVswitch for general description
"""
raise NotImplementedError
def add_tunnel_port(self, switch_name, remote_ip, tunnel_type='vxlan', params=None):
"""Creates tunneling port
"""
bridge = self._bridges[switch_name]
pcount = str(self._get_port_count('type=' + tunnel_type))
port_name = tunnel_type + pcount
local_params = ['--', 'set', 'Interface', port_name,
'type=' + tunnel_type,
'options:remote_ip=' + remote_ip]
if params is not None:
local_params = local_params + params
of_port = bridge.add_port(port_name, local_params)
return (port_name, of_port)
def get_ports(self, switch_name):
"""See IVswitch for general description
"""
bridge = self._bridges[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.del_port(port_name)
def add_flow(self, switch_name, flow, cache='off'):
"""See IVswitch for general description
"""
bridge = self._bridges[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.del_flow(flow)
def dump_flows(self, switch_name):
"""See IVswitch for general description
"""
bridge = self._bridges[switch_name]
bridge.dump_flows()
def add_route(self, switch_name, network, destination):
"""See IVswitch for general description
"""
bridge = self._bridges[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.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())]
phits = [i for i in pparams if param in i]
cnt += len(phits)
if cnt is None:
cnt = 0
return cnt
def validate_add_switch(self, result, switch_name, params=None):
"""Validate - Create a new logical switch with no ports
"""
bridge = self._bridges[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
return True
def validate_del_switch(self, result, switch_name):
"""Validate removal of switch
"""
bridge = OFBridge('tmp')
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 None
return True
def validate_add_phy_port(self, result, switch_name):
""" Validate that physical port was added to bridge.
"""
bridge = self._bridges[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
assert re.search('Interface ["\']?%s["\']?' % result[0], output[0]) is not None
return True
def validate_add_vport(self, result, switch_name):
""" Validate that virtual port was added to bridge.
"""
return self.validate_add_phy_port(result, switch_name)
def validate_del_port(self, result, switch_name, port_name):
""" Validate that port_name was removed from bridge.
"""
bridge = self._bridges[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, result, switch_name, flow, cache='off'):
""" Validate insertion of the flow into the switch
"""
if 'idle_timeout' in flow:
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
# as add-flow, so we have to compare it the hard way
# get dump of flows and compare them one by one
flow_src = flow_key(flow)
bridge = self._bridges[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):
# flow was added correctly
return True
return False
def validate_del_flow(self, 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)
def validate_dump_flows(self, result, switch_name):
""" Validate call of flow dump
"""
return True