Add abstract OVSDB API

Abstract all existing 'ovs-vsctl' calls to an abstract OVSDB API.
This will allow the future addition of a native OVSDB protocol
implementation of the API without breaking backwards compatibility.

Partial-Bug: #1666917
Change-Id: I9ec36be390d713a928a673191008612f3fddad8f
This commit is contained in:
Rodolfo Alonso Hernandez 2018-09-20 16:22:00 +01:00 committed by Sean Mooney
parent ecd0b354ca
commit 7bfb01bcb1
13 changed files with 706 additions and 439 deletions

View File

@ -49,6 +49,7 @@ oslo.service==1.30.0
oslo.utils==3.36.0
oslo.versionedobjects==1.28.0
oslotest==1.10.0
ovsdbapp==0.12.1
Paste==2.0.3
PasteDeploy==1.5.2
pbr==2.0.0

View File

@ -0,0 +1,9 @@
---
features:
- |
Added an abstract OVSDB API in ``vif_plug_ovs``. All calls to OVS database
will de done using this unique API.
Command line implementation using ``ovs-vsctl`` was refactored as a
backend for this abstract API.
A new configuration variable, ``ovsdb_interface``, is added to select
the interface for interacting with the OVS database.

View File

@ -10,6 +10,7 @@ oslo.log>=3.30.0 # Apache-2.0
oslo.i18n>=3.15.3 # Apache-2.0
oslo.privsep>=1.23.0 # Apache-2.0
oslo.versionedobjects>=1.28.0 # Apache-2.0
ovsdbapp>=0.12.1 # Apache-2.0
pyroute2>=0.4.21;sys_platform!='win32' # Apache-2.0 (+ dual licensed GPL2)
six>=1.10.0 # MIT
stevedore>=1.20.0 # Apache-2.0

View File

@ -49,68 +49,15 @@ PF_FUNC_RE = re.compile("\.(\d+)", 0)
_SRIOV_TOTALVFS = "sriov_totalvfs"
def _ovs_vsctl(args, timeout=None, ovsdb_connection=None):
full_args = ['ovs-vsctl']
if timeout is not None:
full_args += ['--timeout=%s' % timeout]
if ovsdb_connection is not None:
full_args += ['--db=%s' % ovsdb_connection]
full_args += args
try:
return processutils.execute(*full_args)
except Exception as e:
LOG.error("Unable to execute %(cmd)s. Exception: %(exception)s",
{'cmd': full_args, 'exception': e})
raise exception.AgentError(method=full_args)
def _create_ovs_vif_cmd(bridge, dev, iface_id, mac,
instance_id, interface_type=None,
vhost_server_path=None):
cmd = ['--', '--may-exist', 'add-port', bridge, dev,
'--', 'set', 'Interface', dev,
'external-ids:iface-id=%s' % iface_id,
'external-ids:iface-status=active',
'external-ids:attached-mac=%s' % mac,
'external-ids:vm-uuid=%s' % instance_id]
if interface_type:
cmd += ['type=%s' % interface_type]
if vhost_server_path:
cmd += ['options:vhost-server-path=%s' % vhost_server_path]
return cmd
def _create_ovs_bridge_cmd(bridge, datapath_type):
return ['--', '--may-exist', 'add-br', bridge,
'--', 'set', 'Bridge', bridge, 'datapath_type=%s' % datapath_type]
@privsep.vif_plug.entrypoint
def create_ovs_vif_port(bridge, dev, iface_id, mac, instance_id,
mtu=None, interface_type=None, timeout=None,
vhost_server_path=None, ovsdb_connection=None):
_ovs_vsctl(_create_ovs_vif_cmd(bridge, dev, iface_id,
mac, instance_id, interface_type,
vhost_server_path), timeout=timeout,
ovsdb_connection=ovsdb_connection)
_update_device_mtu(dev, mtu, interface_type, timeout=timeout,
ovsdb_connection=ovsdb_connection)
@privsep.vif_plug.entrypoint
def update_ovs_vif_port(dev, mtu=None, interface_type=None, timeout=None,
ovsdb_connection=None):
_update_device_mtu(dev, mtu, interface_type, timeout=timeout,
ovsdb_connection=ovsdb_connection)
@privsep.vif_plug.entrypoint
def delete_ovs_vif_port(bridge, dev, timeout=None,
ovsdb_connection=None, delete_netdev=True):
_ovs_vsctl(['--', '--if-exists', 'del-port', bridge, dev],
timeout=timeout, ovsdb_connection=ovsdb_connection)
if delete_netdev:
_delete_net_dev(dev)
def _update_device_mtu(dev, mtu):
if not mtu:
return
if sys.platform != constants.PLATFORM_WIN32:
# Hyper-V with OVS does not support external programming of
# virtual interface MTUs via netsh or other Windows tools.
# When plugging an interface on Windows, we therefore skip
# programming the MTU and fallback to DHCP advertisement.
set_device_mtu(dev, mtu)
def interface_in_bridge(bridge, device):
@ -119,7 +66,7 @@ def interface_in_bridge(bridge, device):
{'bridge': bridge, 'device': device})
def _delete_net_dev(dev):
def delete_net_dev(dev):
"""Delete a network device only if it exists."""
if ip_lib.exists(dev):
try:
@ -136,7 +83,7 @@ def create_veth_pair(dev1_name, dev2_name, mtu):
deleting any previous devices with those names.
"""
for dev in [dev1_name, dev2_name]:
_delete_net_dev(dev)
delete_net_dev(dev)
ip_lib.add(dev1_name, 'veth', peer=dev2_name)
for dev in [dev1_name, dev2_name]:
@ -152,13 +99,6 @@ def update_veth_pair(dev1_name, dev2_name, mtu):
_update_device_mtu(dev, mtu)
@privsep.vif_plug.entrypoint
def ensure_ovs_bridge(bridge, datapath_type, timeout=None,
ovsdb_connection=None):
_ovs_vsctl(_create_ovs_bridge_cmd(bridge, datapath_type), timeout=timeout,
ovsdb_connection=ovsdb_connection)
@privsep.vif_plug.entrypoint
def ensure_bridge(bridge):
if not ip_lib.exists(bridge):
@ -196,32 +136,8 @@ def add_bridge_port(bridge, dev):
processutils.execute('brctl', 'addif', bridge, dev)
def _update_device_mtu(dev, mtu, interface_type=None, timeout=120,
ovsdb_connection=None):
if not mtu:
return
if interface_type not in [
constants.OVS_VHOSTUSER_INTERFACE_TYPE,
constants.OVS_VHOSTUSER_CLIENT_INTERFACE_TYPE]:
if sys.platform != constants.PLATFORM_WIN32:
# Hyper-V with OVS does not support external programming of virtual
# interface MTUs via netsh or other Windows tools.
# When plugging an interface on Windows, we therefore skip
# programming the MTU and fallback to DHCP advertisement.
_set_device_mtu(dev, mtu)
elif _ovs_supports_mtu_requests(timeout=timeout,
ovsdb_connection=ovsdb_connection):
_set_mtu_request(dev, mtu, timeout=timeout,
ovsdb_connection=ovsdb_connection)
else:
LOG.debug("MTU not set on %(interface_name)s interface "
"of type %(interface_type)s.",
{'interface_name': dev,
'interface_type': interface_type})
@privsep.vif_plug.entrypoint
def _set_device_mtu(dev, mtu):
def set_device_mtu(dev, mtu):
"""Set the device MTU."""
ip_lib.set(dev, mtu=mtu, check_exit_code=[0, 2, 254])
@ -231,24 +147,6 @@ def set_interface_state(interface_name, port_state):
ip_lib.set(interface_name, state=port_state, check_exit_code=[0, 2, 254])
@privsep.vif_plug.entrypoint
def _set_mtu_request(dev, mtu, timeout=None, ovsdb_connection=None):
args = ['--', 'set', 'interface', dev,
'mtu_request=%s' % mtu]
_ovs_vsctl(args, timeout=timeout, ovsdb_connection=ovsdb_connection)
@privsep.vif_plug.entrypoint
def _ovs_supports_mtu_requests(timeout=None, ovsdb_connection=None):
args = ['--columns=mtu_request', 'list', 'interface']
_, error = _ovs_vsctl(args, timeout=timeout,
ovsdb_connection=ovsdb_connection)
if (error == 'ovs-vsctl: Interface does not contain' +
' a column whose name matches "mtu_request"'):
return False
return True
def _parse_vf_number(phys_port_name):
"""Parses phys_port_name and returns VF number or None.

View File

@ -27,6 +27,8 @@ from oslo_config import cfg
from vif_plug_ovs import constants
from vif_plug_ovs import exception
from vif_plug_ovs import linux_net
from vif_plug_ovs.ovsdb import api as ovsdb_api
from vif_plug_ovs.ovsdb import ovsdb_lib
class OvsPlugin(plugin.PluginBase):
@ -62,8 +64,16 @@ class OvsPlugin(plugin.PluginBase):
'tcp:127.0.0.1:6640, ovs-vsctl commands will use the '
'tcp:IP:PORT parameter for communicating with OVSDB over '
'a TCP socket.'),
cfg.StrOpt('ovsdb_interface',
choices=list(ovsdb_api.interface_map),
default='vsctl',
help='The interface for interacting with the OVSDB'),
)
def __init__(self, config):
super(OvsPlugin, self).__init__(config)
self.ovsdb = ovsdb_lib.BaseOVS(self.config)
@staticmethod
def gen_port_name(prefix, id):
return ("%s%s" % (prefix, id))[:OvsPlugin.NIC_NAME_LEN]
@ -118,21 +128,17 @@ class OvsPlugin(plugin.PluginBase):
def _create_vif_port(self, vif, vif_name, instance_info, **kwargs):
mtu = self._get_mtu(vif)
linux_net.create_ovs_vif_port(
self.ovsdb.create_ovs_vif_port(
vif.network.bridge,
vif_name,
vif.port_profile.interface_id,
vif.address, instance_info.uuid,
mtu,
timeout=self.config.ovs_vsctl_timeout,
ovsdb_connection=self.config.ovsdb_connection,
**kwargs)
def _update_vif_port(self, vif, vif_name):
mtu = self._get_mtu(vif)
linux_net.update_ovs_vif_port(vif_name, mtu,
timeout=self.config.ovs_vsctl_timeout,
ovsdb_connection=self.config.ovsdb_connection)
self.ovsdb.update_ovs_vif_port(vif_name, mtu)
@staticmethod
def _get_vif_datapath_type(vif, datapath=constants.OVS_DATAPATH_SYSTEM):
@ -142,7 +148,7 @@ class OvsPlugin(plugin.PluginBase):
return profile.datapath_type
def _plug_vhostuser(self, vif, instance_info):
linux_net.ensure_ovs_bridge(
self.ovsdb.ensure_ovs_bridge(
vif.network.bridge, self._get_vif_datapath_type(
vif, datapath=constants.OVS_DATAPATH_NETDEV))
vif_name = OvsPlugin.gen_port_name(
@ -176,10 +182,8 @@ class OvsPlugin(plugin.PluginBase):
if not ip_lib.exists(v2_name):
linux_net.create_veth_pair(v1_name, v2_name, mtu)
linux_net.add_bridge_port(vif.bridge_name, v1_name)
linux_net.ensure_ovs_bridge(vif.network.bridge,
self._get_vif_datapath_type(vif),
timeout=self.config.ovs_vsctl_timeout,
ovsdb_connection=self.config.ovsdb_connection)
self.ovsdb.ensure_ovs_bridge(vif.network.bridge,
self._get_vif_datapath_type(vif))
self._create_vif_port(vif, v2_name, instance_info)
else:
linux_net.update_veth_pair(v1_name, v2_name, mtu)
@ -189,15 +193,13 @@ class OvsPlugin(plugin.PluginBase):
"""Create a per-VIF OVS port."""
if not ip_lib.exists(vif.id):
linux_net.ensure_ovs_bridge(vif.network.bridge,
self._get_vif_datapath_type(vif))
self.ovsdb.ensure_ovs_bridge(vif.network.bridge,
self._get_vif_datapath_type(vif))
self._create_vif_port(vif, vif.id, instance_info)
def _plug_vf_passthrough(self, vif, instance_info):
linux_net.ensure_ovs_bridge(
vif.network.bridge, constants.OVS_DATAPATH_SYSTEM,
timeout=self.config.ovs_vsctl_timeout,
ovsdb_connection=self.config.ovsdb_connection)
self.ovsdb.ensure_ovs_bridge(
vif.network.bridge, constants.OVS_DATAPATH_SYSTEM)
pci_slot = vif.dev_address
pf_ifname = linux_net.get_ifname_by_pci_address(
pci_slot, pf_interface=True, switchdev=True)
@ -216,10 +218,8 @@ class OvsPlugin(plugin.PluginBase):
if isinstance(vif, objects.vif.VIFOpenVSwitch):
if sys.platform != constants.PLATFORM_WIN32:
linux_net.ensure_ovs_bridge(vif.network.bridge,
self._get_vif_datapath_type(vif),
timeout=self.config.ovs_vsctl_timeout,
ovsdb_connection=self.config.ovsdb_connection)
self.ovsdb.ensure_ovs_bridge(vif.network.bridge,
self._get_vif_datapath_type(vif))
else:
self._plug_vif_windows(vif, instance_info)
elif isinstance(vif, objects.vif.VIFBridge):
@ -233,12 +233,10 @@ class OvsPlugin(plugin.PluginBase):
self._plug_vf_passthrough(vif, instance_info)
def _unplug_vhostuser(self, vif, instance_info):
linux_net.delete_ovs_vif_port(vif.network.bridge,
self.ovsdb.delete_ovs_vif_port(vif.network.bridge,
OvsPlugin.gen_port_name(
constants.OVS_VHOSTUSER_PREFIX,
vif.id),
timeout=self.config.ovs_vsctl_timeout,
ovsdb_connection=self.config.ovsdb_connection)
vif.id))
def _unplug_bridge(self, vif, instance_info):
"""UnPlug using hybrid strategy
@ -251,16 +249,11 @@ class OvsPlugin(plugin.PluginBase):
linux_net.delete_bridge(vif.bridge_name, v1_name)
linux_net.delete_ovs_vif_port(vif.network.bridge, v2_name,
timeout=self.config.ovs_vsctl_timeout,
ovsdb_connection=self.config.ovsdb_connection)
self.ovsdb.delete_ovs_vif_port(vif.network.bridge, v2_name)
def _unplug_vif_windows(self, vif, instance_info):
"""Remove port from OVS."""
linux_net.delete_ovs_vif_port(vif.network.bridge, vif.id,
timeout=self.config.ovs_vsctl_timeout,
ovsdb_connection=self.config.ovsdb_connection)
self.ovsdb.delete_ovs_vif_port(vif.network.bridge, vif.id)
def _unplug_vf_passthrough(self, vif, instance_info):
"""Remove port from OVS."""
@ -272,11 +265,8 @@ class OvsPlugin(plugin.PluginBase):
# The representor interface can't be deleted because it bind the
# SR-IOV VF, therefore we just need to remove it from the ovs bridge
# and set the status to down
linux_net.delete_ovs_vif_port(
vif.network.bridge, representor,
timeout=self.config.ovs_vsctl_timeout,
ovsdb_connection=self.config.ovsdb_connection,
delete_netdev=False)
self.ovsdb.delete_ovs_vif_port(
vif.network.bridge, representor, delete_netdev=False)
linux_net.set_interface_state(representor, 'down')
def unplug(self, vif, instance_info):

View File

27
vif_plug_ovs/ovsdb/api.py Normal file
View File

@ -0,0 +1,27 @@
# 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.
from oslo_utils import importutils
interface_map = {
'vsctl': 'vif_plug_ovs.ovsdb.impl_vsctl',
# NOTE(ralonsoh): to be implemented in following patches.
# 'native': 'vif_plug_ovs.ovsdb.impl_idl',
}
def get_instance(context, iface_name=None):
"""Return the configured OVSDB API implementation"""
iface = importutils.import_module(
interface_map[iface_name or context.interface])
return iface.api_factory(context)

View File

@ -0,0 +1,378 @@
# Derived from neutron/agent/ovsdb/impl_vsctl.py
#
# 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 collections
import itertools
import uuid
from oslo_concurrency import processutils
from oslo_log import log as logging
from oslo_serialization import jsonutils
from oslo_utils import excutils
from oslo_utils import uuidutils
from ovsdbapp import api as ovsdb_api
import six
from vif_plug_ovs import privsep
LOG = logging.getLogger(__name__)
def _val_to_py(val):
"""Convert a json ovsdb return value to native python object"""
if isinstance(val, collections.Sequence) and len(val) == 2:
if val[0] == "uuid":
return uuid.UUID(val[1])
elif val[0] == "set":
return [_val_to_py(x) for x in val[1]]
elif val[0] == "map":
return {_val_to_py(x): _val_to_py(y) for x, y in val[1]}
return val
def _py_to_val(pyval):
"""Convert python value to ovs-vsctl value argument"""
if isinstance(pyval, bool):
return 'true' if pyval is True else 'false'
elif pyval == '':
return '""'
else:
# NOTE(twilson) If a Command object, return its record_id as a value
return getattr(pyval, "record_id", pyval)
def api_factory(context):
return OvsdbVsctl(context)
@privsep.vif_plug.entrypoint
def _run_vsctl(full_args):
# NOTE(ralonsoh): this function is defined outside the class Transaction
# to allow oslo_privsep.PrivContext.entrypoint to wrap
# the function correctly.
return processutils.execute(*full_args)[0].rstrip()
class Transaction(ovsdb_api.Transaction):
def __init__(self, context, check_error=False, log_errors=True, opts=None):
self.context = context
self.check_error = check_error
self.log_errors = log_errors
self.opts = ['--timeout=%d' % self.context.timeout,
'--oneline', '--format=json']
if self.context.connection:
self.opts += ['--db=%s' % self.context.connection]
if opts:
self.opts += opts
self.commands = []
def add(self, command):
self.commands.append(command)
return command
def commit(self):
args = []
for cmd in self.commands:
cmd.result = None
args += cmd.vsctl_args()
res = self.run_vsctl(args)
if res is None:
return
res = res.replace(r'\\', '\\').splitlines()
for i, record in enumerate(res):
self.commands[i].result = record
return [cmd.result for cmd in self.commands]
def run_vsctl(self, args):
full_args = ["ovs-vsctl"] + self.opts + args
try:
# We log our own errors, so never have utils.execute do it
return _run_vsctl(full_args)
except Exception as e:
with excutils.save_and_reraise_exception() as ctxt:
if self.log_errors:
LOG.error("Unable to execute %(cmd)s. Exception: "
"%(exception)s",
{'cmd': full_args, 'exception': e})
if not self.check_error:
ctxt.reraise = False
class BaseCommand(ovsdb_api.Command):
def __init__(self, context, cmd, opts=None, args=None):
self.context = context
self.cmd = cmd
self.opts = [] if opts is None else opts
self.args = [] if args is None else args
def execute(self, check_error=False, log_errors=True):
with Transaction(self.context, check_error=check_error,
log_errors=log_errors) as txn:
txn.add(self)
return self.result
def vsctl_args(self):
return itertools.chain(('--',), self.opts, (self.cmd,), self.args)
class MultiLineCommand(BaseCommand):
"""Command for ovs-vsctl commands that return multiple lines"""
@property
def result(self):
return self._result
@result.setter
def result(self, raw_result):
self._result = raw_result.split(r'\n') if raw_result else []
class DbCommand(BaseCommand):
def __init__(self, context, cmd, opts=None, args=None, columns=None):
if opts is None:
opts = []
if columns:
opts += ['--columns=%s' % ",".join(columns)]
super(DbCommand, self).__init__(context, cmd, opts, args)
@property
def result(self):
return self._result
@result.setter
def result(self, raw_result):
# If check_error=False, run_vsctl can return None
if not raw_result:
self._result = None
return
try:
json = jsonutils.loads(raw_result)
except (ValueError, TypeError) as e:
# This shouldn't happen, but if it does and we check_errors
# log and raise.
with excutils.save_and_reraise_exception():
LOG.error("Could not parse: %(raw_result)s. Exception: "
"%(exception)s",
{'raw_result': raw_result, 'exception': e})
headings = json['headings']
data = json['data']
results = []
for record in data:
obj = {}
for pos, heading in enumerate(headings):
obj[heading] = _val_to_py(record[pos])
results.append(obj)
self._result = results
class DbGetCommand(DbCommand):
@DbCommand.result.setter
def result(self, val):
# super()'s never worked for setters http://bugs.python.org/issue14965
DbCommand.result.fset(self, val)
# DbCommand will return [{'column': value}] and we just want value.
if self._result:
self._result = list(self._result[0].values())[0]
class DbCreateCommand(BaseCommand):
def __init__(self, context, opts=None, args=None):
super(DbCreateCommand, self).__init__(context, "create", opts, args)
# NOTE(twilson) pre-commit result used for intra-transaction reference
self.record_id = "@%s" % uuidutils.generate_uuid()
self.opts.append("--id=%s" % self.record_id)
@property
def result(self):
return self._result
@result.setter
def result(self, val):
self._result = uuid.UUID(val) if val else val
class BrExistsCommand(DbCommand):
@DbCommand.result.setter
def result(self, val):
self._result = val is not None
def execute(self):
return super(BrExistsCommand, self).execute(check_error=False,
log_errors=False)
class OvsdbVsctl(ovsdb_api.API):
def __init__(self, context):
super(OvsdbVsctl, self).__init__()
self.context = context
def create_transaction(self, check_error=False, log_errors=True, **kwargs):
return Transaction(self.context, check_error, log_errors, **kwargs)
def add_manager(self, connection_uri):
# This will add a new manager without overriding existing ones.
conn_uri = 'target="%s"' % connection_uri
args = ['create', 'Manager', conn_uri, '--', 'add', 'Open_vSwitch',
'.', 'manager_options', '@manager']
return BaseCommand(self.context, '--id=@manager', args=args)
def get_manager(self):
return MultiLineCommand(self.context, 'get-manager')
def remove_manager(self, connection_uri):
args = ['get', 'Manager', connection_uri, '--', 'remove',
'Open_vSwitch', '.', 'manager_options', '@manager']
return BaseCommand(self.context, '--id=@manager', args=args)
def add_br(self, name, may_exist=True, datapath_type=None):
opts = ['--may-exist'] if may_exist else None
params = [name]
if datapath_type:
params += ['--', 'set', 'Bridge', name,
'datapath_type=%s' % datapath_type]
return BaseCommand(self.context, 'add-br', opts, params)
def del_br(self, name, if_exists=True):
opts = ['--if-exists'] if if_exists else None
return BaseCommand(self.context, 'del-br', opts, [name])
def br_exists(self, name):
return BrExistsCommand(self.context, 'list', args=['Bridge', name])
def port_to_br(self, name):
return BaseCommand(self.context, 'port-to-br', args=[name])
def iface_to_br(self, name):
return BaseCommand(self.context, 'iface-to-br', args=[name])
def list_br(self):
return MultiLineCommand(self.context, 'list-br')
def br_get_external_id(self, name, field):
return BaseCommand(self.context, 'br-get-external-id',
args=[name, field])
def db_create(self, table, **col_values):
args = [table]
args += _set_colval_args(*col_values.items())
return DbCreateCommand(self.context, args=args)
def db_destroy(self, table, record):
args = [table, record]
return BaseCommand(self.context, 'destroy', args=args)
def db_set(self, table, record, *col_values):
args = [table, record]
args += _set_colval_args(*col_values)
return BaseCommand(self.context, 'set', args=args)
def db_add(self, table, record, column, *values):
args = [table, record, column]
for value in values:
if isinstance(value, collections.Mapping):
args += ["{}={}".format(_py_to_val(k), _py_to_val(v))
for k, v in value.items()]
else:
args.append(_py_to_val(value))
return BaseCommand(self.context, 'add', args=args)
def db_clear(self, table, record, column):
return BaseCommand(self.context, 'clear', args=[table, record,
column])
def db_get(self, table, record, column):
# Use the 'list' command as it can return json and 'get' cannot so that
# we can get real return types instead of treating everything as string
# NOTE: openvswitch can return a single atomic value for fields that
# are sets, but only have one value. This makes directly iterating over
# the result of a db_get() call unsafe.
return DbGetCommand(self.context, 'list', args=[table, record],
columns=[column])
def db_list(self, table, records=None, columns=None, if_exists=False):
opts = ['--if-exists'] if if_exists else None
args = [table]
if records:
args += records
return DbCommand(self.context, 'list', opts=opts, args=args,
columns=columns)
def db_find(self, table, *conditions, **kwargs):
columns = kwargs.pop('columns', None)
args = itertools.chain([table],
*[_set_colval_args(c) for c in conditions])
return DbCommand(self.context, 'find', args=args, columns=columns)
def set_controller(self, bridge, controllers):
return BaseCommand(self.context, 'set-controller',
args=[bridge] + list(controllers))
def del_controller(self, bridge):
return BaseCommand(self.context, 'del-controller', args=[bridge])
def get_controller(self, bridge):
return MultiLineCommand(self.context, 'get-controller', args=[bridge])
def set_fail_mode(self, bridge, mode):
return BaseCommand(self.context, 'set-fail-mode', args=[bridge, mode])
def add_port(self, bridge, port, may_exist=True):
opts = ['--may-exist'] if may_exist else None
return BaseCommand(self.context, 'add-port', opts, [bridge, port])
def del_port(self, port, bridge=None, if_exists=True):
opts = ['--if-exists'] if if_exists else None
args = filter(None, [bridge, port])
return BaseCommand(self.context, 'del-port', opts, args)
def list_ports(self, bridge):
return MultiLineCommand(self.context, 'list-ports', args=[bridge])
def list_ifaces(self, bridge):
return MultiLineCommand(self.context, 'list-ifaces', args=[bridge])
def db_list_rows(self, table, record=None, if_exists=False):
raise NotImplementedError()
def db_find_rows(self, table, *conditions, **kwargs):
raise NotImplementedError()
def db_remove(self, table, record, column, *values, **keyvalues):
raise NotImplementedError()
def _set_colval_args(*col_values):
args = []
# TODO(twilson) This is ugly, but set/find args are very similar except for
# op. Will try to find a better way to default this op to '='
for entry in col_values:
if len(entry) == 2:
col, op, val = entry[0], '=', entry[1]
else:
col, op, val = entry
if isinstance(val, collections.Mapping):
args += ["%s:%s%s%s" % (
col, k, op, _py_to_val(v)) for k, v in val.items()]
elif (isinstance(val, collections.Sequence)
and not isinstance(val, six.string_types)):
if len(val) == 0:
args.append("%s%s%s" % (col, op, "[]"))
else:
args.append(
"%s%s%s" % (col, op, ",".join(map(_py_to_val, val))))
else:
args.append("%s%s%s" % (col, op, _py_to_val(val)))
return args

View File

@ -0,0 +1,90 @@
# 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 sys
from oslo_log import log as logging
from vif_plug_ovs import constants
from vif_plug_ovs import linux_net
from vif_plug_ovs.ovsdb import api as ovsdb_api
LOG = logging.getLogger(__name__)
class BaseOVS(object):
def __init__(self, config):
self.timeout = config.ovs_vsctl_timeout
self.connection = config.ovsdb_connection
self.interface = config.ovsdb_interface
self.ovsdb = ovsdb_api.get_instance(self)
def _ovs_supports_mtu_requests(self):
return bool(self.ovsdb.db_list(
'Interface', columns=['mtu_request']).execute(check_error=True,
log_errors=True))
def _set_mtu_request(self, dev, mtu):
self.ovsdb.db_set('Interface', dev, ('mtu_request', mtu)).execute()
def update_device_mtu(self, dev, mtu, interface_type=None):
if not mtu:
return
if interface_type not in [
constants.OVS_VHOSTUSER_INTERFACE_TYPE,
constants.OVS_VHOSTUSER_CLIENT_INTERFACE_TYPE]:
if sys.platform != constants.PLATFORM_WIN32:
# Hyper-V with OVS does not support external programming of
# virtual interface MTUs via netsh or other Windows tools.
# When plugging an interface on Windows, we therefore skip
# programming the MTU and fallback to DHCP advertisement.
linux_net.set_device_mtu(dev, mtu)
elif self._ovs_supports_mtu_requests():
self._set_mtu_request(dev, mtu)
else:
LOG.debug("MTU not set on %(interface_name)s interface "
"of type %(interface_type)s.",
{'interface_name': dev,
'interface_type': interface_type})
def ensure_ovs_bridge(self, bridge, datapath_type):
return self.ovsdb.add_br(bridge, may_exist=True,
datapath_type=datapath_type).execute()
def create_ovs_vif_port(self, bridge, dev, iface_id, mac, instance_id,
mtu=None, interface_type=None,
vhost_server_path=None):
external_ids = {'iface-id': iface_id,
'iface-status': 'active',
'attached-mac': mac,
'vm-uuid': instance_id}
col_values = [('external_ids', external_ids)]
if interface_type:
col_values.append(('type', interface_type))
if vhost_server_path:
col_values.append(('options',
{'vhost-server-path': vhost_server_path}))
with self.ovsdb.transaction() as txn:
txn.add(self.ovsdb.add_port(bridge, dev))
txn.add(self.ovsdb.db_set('Interface', dev, *col_values))
self.update_device_mtu(dev, mtu, interface_type=interface_type)
def update_ovs_vif_port(self, dev, mtu=None, interface_type=None):
self.update_device_mtu(dev, mtu, interface_type=interface_type)
def delete_ovs_vif_port(self, bridge, dev, delete_netdev=True):
self.ovsdb.del_port(dev, bridge=bridge, if_exists=True).execute()
if delete_netdev:
linux_net.delete_net_dev(dev)

View File

@ -0,0 +1,138 @@
# 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 mock
import sys
import testtools
from oslo_config import cfg
from oslo_utils import uuidutils
from vif_plug_ovs import constants
from vif_plug_ovs import linux_net
from vif_plug_ovs.ovsdb import ovsdb_lib
CONF = cfg.CONF
class BaseOVSTest(testtools.TestCase):
def setUp(self):
super(BaseOVSTest, self).setUp()
test_vif_plug_ovs_group = cfg.OptGroup('test_vif_plug_ovs')
CONF.register_group(test_vif_plug_ovs_group)
CONF.register_opt(cfg.IntOpt('ovs_vsctl_timeout', default=1500),
test_vif_plug_ovs_group)
CONF.register_opt(cfg.StrOpt('ovsdb_interface', default='vsctl'),
test_vif_plug_ovs_group)
CONF.register_opt(cfg.StrOpt('ovsdb_connection', default=None),
test_vif_plug_ovs_group)
self.br = ovsdb_lib.BaseOVS(cfg.CONF.test_vif_plug_ovs)
self.mock_db_set = mock.patch.object(self.br.ovsdb, 'db_set').start()
self.mock_del_port = mock.patch.object(self.br.ovsdb,
'del_port').start()
self.mock_add_port = mock.patch.object(self.br.ovsdb,
'add_port').start()
self.mock_add_br = mock.patch.object(self.br.ovsdb, 'add_br').start()
self.mock_transaction = mock.patch.object(self.br.ovsdb,
'transaction').start()
def test__set_mtu_request(self):
self.br._set_mtu_request('device', 1500)
calls = [mock.call('Interface', 'device', ('mtu_request', 1500))]
self.mock_db_set.assert_has_calls(calls)
@mock.patch.object(sys, 'platform', return_value='linux')
@mock.patch.object(linux_net, 'set_device_mtu')
def test__update_device_mtu_interface_not_vhostuser_linux(self,
mock_set_device_mtu, mock_platform):
self.br.update_device_mtu('device', 1500, 'not_vhost')
mock_set_device_mtu.assert_has_calls([mock.call('device', 1500)])
@mock.patch.object(linux_net, 'set_device_mtu')
def test__update_device_mtu_interface_not_vhostuser_windows(self,
mock_set_device_mtu):
sys.platform = constants.PLATFORM_WIN32
self.br.update_device_mtu('device', 1500, 'not_vhost')
mock_set_device_mtu.assert_not_called()
def test__update_device_mtu_interface_vhostuser_supports_mtu_req(self):
with mock.patch.object(self.br, '_ovs_supports_mtu_requests',
return_value=True), \
mock.patch.object(self.br, '_set_mtu_request') as \
mock_set_mtu_request:
self.br.update_device_mtu('device', 1500,
constants.OVS_VHOSTUSER_INTERFACE_TYPE)
mock_set_mtu_request.assert_has_calls([mock.call('device', 1500)])
def test__update_device_mtu_interface_vhostuser_not_supports_mtu_req(self):
with mock.patch.object(self.br, '_ovs_supports_mtu_requests',
return_value=False), \
mock.patch.object(self.br, '_set_mtu_request') as \
mock_set_mtu_request:
self.br.update_device_mtu('device', 1500,
constants.OVS_VHOSTUSER_INTERFACE_TYPE)
mock_set_mtu_request.assert_not_called()
def test_create_ovs_vif_port(self):
iface_id = 'iface_id'
mac = 'ca:fe:ca:fe:ca:fe'
instance_id = uuidutils.generate_uuid()
interface_type = constants.OVS_VHOSTUSER_INTERFACE_TYPE
vhost_server_path = '/fake/path'
device = 'device'
bridge = 'bridge'
mtu = 1500
external_ids = {'iface-id': iface_id,
'iface-status': 'active',
'attached-mac': mac,
'vm-uuid': instance_id}
values = [('external_ids', external_ids),
('type', interface_type),
('options', {'vhost-server-path': vhost_server_path})
]
with mock.patch.object(self.br, 'update_device_mtu',
return_value=True) as mock_update_device_mtu, \
mock.patch.object(self.br, '_ovs_supports_mtu_requests',
return_value=True):
self.br.create_ovs_vif_port(bridge, device, iface_id, mac,
instance_id, mtu=mtu,
interface_type=interface_type,
vhost_server_path=vhost_server_path)
self.mock_add_port.assert_has_calls([mock.call(bridge, device)])
self.mock_db_set.assert_has_calls(
[mock.call('Interface', device, *values)])
mock_update_device_mtu.assert_has_calls(
[mock.call(device, mtu, interface_type=interface_type)])
def test_update_ovs_vif_port(self):
with mock.patch.object(self.br, 'update_device_mtu') as \
mock_update_device_mtu:
self.br.update_ovs_vif_port('device', mtu=1500,
interface_type=constants.OVS_VHOSTUSER_INTERFACE_TYPE)
mock_update_device_mtu.assert_has_calls(
[mock.call(
'device', 1500,
interface_type=constants.OVS_VHOSTUSER_INTERFACE_TYPE)])
@mock.patch.object(linux_net, 'delete_net_dev')
def test_delete_ovs_vif_port(self, mock_delete_net_dev):
self.br.delete_ovs_vif_port('bridge', 'device')
self.mock_del_port.assert_has_calls(
[mock.call('device', bridge='bridge', if_exists=True)])
mock_delete_net_dev.assert_has_calls([mock.call('device')])
def test_ensure_ovs_bridge(self):
self.br.ensure_ovs_bridge('bridge', constants.OVS_DATAPATH_SYSTEM)
self.mock_add_br('bridge', may_exist=True,
datapath_type=constants.OVS_DATAPATH_SYSTEM)

View File

@ -18,7 +18,6 @@ import testtools
from os_vif.internal.command import ip as ip_lib
from oslo_concurrency import processutils
from vif_plug_ovs import constants
from vif_plug_ovs import exception
from vif_plug_ovs import linux_net
from vif_plug_ovs import privsep
@ -131,257 +130,6 @@ class LinuxNetTest(testtools.TestCase):
mock_execute.assert_has_calls([
mock.call('brctl', 'addif', 'br0', 'vnet1')])
def test_ovs_vif_port_cmd(self):
expected = ['--', '--may-exist', 'add-port',
'fake-bridge', 'fake-dev',
'--', 'set', 'Interface', 'fake-dev',
'external-ids:iface-id=fake-iface-id',
'external-ids:iface-status=active',
'external-ids:attached-mac=fake-mac',
'external-ids:vm-uuid=fake-instance-uuid']
cmd = linux_net._create_ovs_vif_cmd('fake-bridge', 'fake-dev',
'fake-iface-id', 'fake-mac',
'fake-instance-uuid')
self.assertEqual(expected, cmd)
expected += ['type=fake-type']
cmd = linux_net._create_ovs_vif_cmd('fake-bridge', 'fake-dev',
'fake-iface-id', 'fake-mac',
'fake-instance-uuid',
'fake-type')
self.assertEqual(expected, cmd)
expected += ['options:vhost-server-path=/fake/path']
cmd = linux_net._create_ovs_vif_cmd('fake-bridge', 'fake-dev',
'fake-iface-id', 'fake-mac',
'fake-instance-uuid',
'fake-type',
vhost_server_path='/fake/path')
self.assertEqual(expected, cmd)
@mock.patch.object(linux_net, '_create_ovs_bridge_cmd')
@mock.patch.object(linux_net, '_ovs_vsctl')
def test_ensure_ovs_bridge(self, mock_vsctl, mock_create_ovs_bridge):
bridge = 'fake-bridge'
dp_type = 'fake-type'
linux_net.ensure_ovs_bridge(bridge, dp_type)
mock_create_ovs_bridge.assert_called_once_with(bridge, dp_type)
self.assertTrue(mock_vsctl.called)
def test_create_ovs_bridge_cmd(self):
bridge = 'fake-bridge'
dp_type = 'fake-type'
expected = ['--', '--may-exist', 'add-br', bridge,
'--', 'set', 'Bridge', bridge,
'datapath_type=%s' % dp_type]
actual = linux_net._create_ovs_bridge_cmd(bridge, dp_type)
self.assertEqual(expected, actual)
@mock.patch.object(linux_net, '_ovs_supports_mtu_requests')
@mock.patch.object(linux_net, '_ovs_vsctl')
@mock.patch.object(linux_net, '_create_ovs_vif_cmd')
@mock.patch.object(linux_net, '_set_device_mtu')
def test_ovs_vif_port_with_type_vhostuser(self, mock_set_device_mtu,
mock_create_cmd, mock_vsctl,
mock_ovs_supports_mtu_requests):
mock_ovs_supports_mtu_requests.return_value = True
linux_net.create_ovs_vif_port(
'fake-bridge',
'fake-dev', 'fake-iface-id', 'fake-mac',
"fake-instance-uuid", mtu=1500,
interface_type=constants.OVS_VHOSTUSER_INTERFACE_TYPE)
mock_create_cmd.assert_called_once_with('fake-bridge',
'fake-dev', 'fake-iface-id', 'fake-mac',
"fake-instance-uuid", constants.OVS_VHOSTUSER_INTERFACE_TYPE,
None)
self.assertFalse(mock_set_device_mtu.called)
self.assertTrue(mock_vsctl.called)
@mock.patch.object(linux_net, '_ovs_supports_mtu_requests')
@mock.patch.object(linux_net, '_ovs_vsctl')
@mock.patch.object(linux_net, '_create_ovs_vif_cmd')
@mock.patch.object(linux_net, '_set_device_mtu')
def test_ovs_vif_port_with_type_vhostuserclient(self,
mock_set_device_mtu, mock_create_cmd,
mock_vsctl, mock_ovs_supports_mtu_requests):
mock_ovs_supports_mtu_requests.return_value = True
linux_net.create_ovs_vif_port(
'fake-bridge',
'fake-dev', 'fake-iface-id', 'fake-mac',
"fake-instance-uuid", mtu=1500,
interface_type=constants.OVS_VHOSTUSER_CLIENT_INTERFACE_TYPE,
vhost_server_path="/fake/path")
mock_create_cmd.assert_called_once_with('fake-bridge',
'fake-dev', 'fake-iface-id', 'fake-mac',
"fake-instance-uuid",
constants.OVS_VHOSTUSER_CLIENT_INTERFACE_TYPE,
"/fake/path")
self.assertFalse(mock_set_device_mtu.called)
self.assertTrue(mock_vsctl.called)
@mock.patch.object(linux_net, '_ovs_supports_mtu_requests')
@mock.patch.object(linux_net, '_ovs_vsctl')
@mock.patch.object(linux_net, '_create_ovs_vif_cmd')
@mock.patch.object(linux_net, '_set_device_mtu')
def test_ovs_vif_port_with_no_mtu(self, mock_set_device_mtu,
mock_create_cmd, mock_vsctl,
mock_ovs_supports_mtu_requests):
mock_ovs_supports_mtu_requests.return_value = True
linux_net.create_ovs_vif_port(
'fake-bridge',
'fake-dev', 'fake-iface-id', 'fake-mac',
"fake-instance-uuid")
mock_create_cmd.assert_called_once_with('fake-bridge',
'fake-dev', 'fake-iface-id', 'fake-mac',
"fake-instance-uuid", None, None)
self.assertFalse(mock_set_device_mtu.called)
self.assertTrue(mock_vsctl.called)
@mock.patch.object(linux_net, '_ovs_supports_mtu_requests')
@mock.patch.object(linux_net, '_set_mtu_request')
@mock.patch.object(linux_net, '_ovs_vsctl')
@mock.patch.object(linux_net, '_create_ovs_vif_cmd',
return_value='ovs_command')
@mock.patch.object(linux_net, '_set_device_mtu')
def test_ovs_vif_port_with_timeout(self, mock_set_device_mtu,
mock_create_cmd, mock_vsctl,
mock_set_mtu_request,
mock_ovs_supports_mtu_requests):
mock_ovs_supports_mtu_requests.return_value = True
linux_net.create_ovs_vif_port(
'fake-bridge',
'fake-dev', 'fake-iface-id', 'fake-mac',
"fake-instance-uuid", ovsdb_connection=None,
timeout=42)
self.assertTrue(mock_create_cmd.called)
self.assertFalse(mock_set_device_mtu.called)
mock_vsctl.assert_called_with('ovs_command', ovsdb_connection=None,
timeout=42)
@mock.patch.object(linux_net, '_ovs_supports_mtu_requests')
@mock.patch.object(linux_net, '_set_mtu_request')
@mock.patch.object(linux_net, '_ovs_vsctl')
@mock.patch.object(linux_net, '_create_ovs_vif_cmd',
return_value='ovs_command')
@mock.patch.object(linux_net, '_set_device_mtu')
def test_ovs_vif_port_with_no_timeout(self, mock_set_device_mtu,
mock_create_cmd, mock_vsctl,
mock_set_mtu_request,
mock_ovs_supports_mtu_requests):
mock_ovs_supports_mtu_requests.return_value = True
linux_net.create_ovs_vif_port(
'fake-bridge',
'fake-dev', 'fake-iface-id', 'fake-mac',
"fake-instance-uuid")
self.assertTrue(mock_create_cmd.called)
self.assertFalse(mock_set_device_mtu.called)
mock_vsctl.assert_called_with('ovs_command', ovsdb_connection=None,
timeout=None)
@mock.patch.object(linux_net, '_ovs_supports_mtu_requests')
@mock.patch.object(linux_net, '_set_mtu_request')
@mock.patch.object(linux_net, '_ovs_vsctl')
@mock.patch.object(linux_net, '_create_ovs_vif_cmd',
return_value='ovs_command')
@mock.patch.object(linux_net, '_set_device_mtu')
def test_ovs_vif_port_with_ovsdb_connection(self, mock_set_device_mtu,
mock_create_cmd, mock_vsctl,
mock_set_mtu_request,
mock_ovs_supports_mtu_requests):
mock_ovs_supports_mtu_requests.return_value = True
linux_net.create_ovs_vif_port(
'fake-bridge',
'fake-dev', 'fake-iface-id', 'fake-mac',
"fake-instance-uuid", ovsdb_connection='tcp:127.0.0.1:6640',
timeout=42)
self.assertTrue(mock_create_cmd.called)
self.assertFalse(mock_set_device_mtu.called)
mock_vsctl.assert_called_with('ovs_command',
ovsdb_connection='tcp:127.0.0.1:6640',
timeout=42)
@mock.patch.object(processutils, "execute")
def test_ovs_vsctl(self, mock_execute):
args = ['fake-args', 42]
ovsdb_connection = 'tcp:127.0.0.1:6640'
timeout = 42
linux_net._ovs_vsctl(args)
linux_net._ovs_vsctl(args, ovsdb_connection=ovsdb_connection,
timeout=timeout)
mock_execute.assert_has_calls([
mock.call('ovs-vsctl', *args),
mock.call('ovs-vsctl', '--timeout=%s' % timeout,
'--db=%s' % ovsdb_connection, *args)])
@mock.patch.object(linux_net, '_ovs_vsctl')
def test_set_mtu_request(self, mock_vsctl):
dev = 'fake-dev'
mtu = 'fake-mtu'
ovsdb_connection = None
timeout = 120
linux_net._set_mtu_request(dev, mtu, ovsdb_connection=ovsdb_connection,
timeout=timeout)
args = ['--', 'set', 'interface', dev,
'mtu_request=%s' % mtu]
mock_vsctl.assert_called_with(args, ovsdb_connection=ovsdb_connection,
timeout=timeout)
@mock.patch.object(linux_net, '_delete_net_dev')
@mock.patch.object(linux_net, '_ovs_vsctl')
def test_delete_ovs_vif_port_delete_netdev(
self, mock_vsctl, mock_delete_net_dev):
bridge = 'fake-bridge'
dev = 'fake-dev'
ovsdb_connection = None
timeout = 120
linux_net.delete_ovs_vif_port(bridge, dev,
ovsdb_connection=ovsdb_connection,
timeout=timeout)
args = ['--', '--if-exists', 'del-port', bridge, dev]
mock_vsctl.assert_called_with(args, ovsdb_connection=ovsdb_connection,
timeout=timeout)
mock_delete_net_dev.assert_called()
@mock.patch.object(linux_net, '_delete_net_dev')
@mock.patch.object(linux_net, '_ovs_vsctl')
def test_delete_ovs_vif_port(self, mock_vsctl, mock_delete_net_dev):
bridge = 'fake-bridge'
dev = 'fake-dev'
ovsdb_connection = None
timeout = 120
linux_net.delete_ovs_vif_port(
bridge, dev, ovsdb_connection=ovsdb_connection, timeout=timeout,
delete_netdev=False)
args = ['--', '--if-exists', 'del-port', bridge, dev]
mock_vsctl.assert_called_with(args, ovsdb_connection=ovsdb_connection,
timeout=timeout)
mock_delete_net_dev.assert_not_called()
@mock.patch.object(linux_net, '_ovs_vsctl')
def test_ovs_supports_mtu_requests(self, mock_vsctl):
args = ['--columns=mtu_request', 'list', 'interface']
ovsdb_connection = None
timeout = 120
msg = 'ovs-vsctl: Interface does not contain' + \
' a column whose name matches "mtu_request"'
mock_vsctl.return_value = (None, msg)
result = linux_net._ovs_supports_mtu_requests(
ovsdb_connection=ovsdb_connection,
timeout=timeout)
mock_vsctl.assert_called_with(args, ovsdb_connection=ovsdb_connection,
timeout=timeout)
self.assertFalse(result)
mock_vsctl.return_value = (None, None)
result = linux_net._ovs_supports_mtu_requests(
ovsdb_connection=ovsdb_connection,
timeout=timeout)
mock_vsctl.assert_called_with(args, ovsdb_connection=ovsdb_connection,
timeout=timeout)
self.assertTrue(result)
@mock.patch('six.moves.builtins.open')
@mock.patch.object(os.path, 'isfile')
def test_is_switchdev_ioerror(self, mock_isfile, mock_open):

View File

@ -20,6 +20,7 @@ from os_vif.objects import fields
from vif_plug_ovs import constants
from vif_plug_ovs import linux_net
from vif_plug_ovs import ovs
from vif_plug_ovs.ovsdb import ovsdb_lib
class PluginTest(testtools.TestCase):
@ -118,7 +119,7 @@ class PluginTest(testtools.TestCase):
self.vif_ovs_hybrid, datapath=constants.OVS_DATAPATH_SYSTEM)
self.assertEqual(constants.OVS_DATAPATH_SYSTEM, dp_type)
@mock.patch.object(linux_net, 'create_ovs_vif_port')
@mock.patch.object(ovsdb_lib.BaseOVS, 'create_ovs_vif_port')
def test_create_vif_port(self, mock_create_ovs_vif_port):
plugin = ovs.OvsPlugin.load(constants.PLUGIN_NAME)
plugin._create_vif_port(
@ -129,11 +130,9 @@ class PluginTest(testtools.TestCase):
self.vif_ovs.port_profile.interface_id,
self.vif_ovs.address, self.instance.uuid,
plugin.config.network_device_mtu,
ovsdb_connection=plugin.config.ovsdb_connection,
timeout=plugin.config.ovs_vsctl_timeout,
interface_type=constants.OVS_VHOSTUSER_INTERFACE_TYPE)
@mock.patch.object(linux_net, 'create_ovs_vif_port')
@mock.patch.object(ovsdb_lib.BaseOVS, 'create_ovs_vif_port')
def test_create_vif_port_mtu_in_model(self, mock_create_ovs_vif_port):
self.vif_ovs.network = self.network_ovs_mtu
plugin = ovs.OvsPlugin.load(constants.PLUGIN_NAME)
@ -145,12 +144,10 @@ class PluginTest(testtools.TestCase):
self.vif_ovs.port_profile.interface_id,
self.vif_ovs.address, self.instance.uuid,
self.network_ovs_mtu.mtu,
ovsdb_connection=plugin.config.ovsdb_connection,
timeout=plugin.config.ovs_vsctl_timeout,
interface_type=constants.OVS_VHOSTUSER_INTERFACE_TYPE)
@mock.patch.object(ovs, 'sys')
@mock.patch.object(linux_net, 'ensure_ovs_bridge')
@mock.patch.object(ovsdb_lib.BaseOVS, 'ensure_ovs_bridge')
def test_plug_ovs(self, ensure_ovs_bridge, mock_sys):
mock_sys.platform = 'linux'
plug_bridge_mock = mock.Mock()
@ -159,10 +156,10 @@ class PluginTest(testtools.TestCase):
plugin.plug(self.vif_ovs, self.instance)
dp_type = ovs.OvsPlugin._get_vif_datapath_type(self.vif_ovs)
ensure_ovs_bridge.assert_called_once_with(self.vif_ovs.network.bridge,
dp_type, ovsdb_connection=None, timeout=120)
dp_type)
@mock.patch.object(linux_net, 'set_interface_state')
@mock.patch.object(linux_net, 'ensure_ovs_bridge')
@mock.patch.object(ovsdb_lib.BaseOVS, 'ensure_ovs_bridge')
@mock.patch.object(ovs.OvsPlugin, '_update_vif_port')
@mock.patch.object(ovs.OvsPlugin, '_create_vif_port')
@mock.patch.object(linux_net, 'add_bridge_port')
@ -195,9 +192,7 @@ class PluginTest(testtools.TestCase):
'_create_vif_port': [mock.call(self.vif_ovs_hybrid,
'qvob679325f-ca',
self.instance)],
'ensure_ovs_bridge': [mock.call('br0', dp_type,
ovsdb_connection=None,
timeout=120)]
'ensure_ovs_bridge': [mock.call('br0', dp_type)]
}
# plugging new devices should result in devices being created
@ -229,7 +224,7 @@ class PluginTest(testtools.TestCase):
update_veth_pair.assert_has_calls(calls['update_veth_pair'])
_update_vif_port.assert_has_calls(calls['_update_vif_port'])
@mock.patch.object(linux_net, 'ensure_ovs_bridge')
@mock.patch.object(ovsdb_lib.BaseOVS, 'ensure_ovs_bridge')
@mock.patch.object(ovs.OvsPlugin, '_create_vif_port')
@mock.patch.object(ip_lib, 'exists', return_value=False)
@mock.patch.object(ovs, 'sys')
@ -262,15 +257,14 @@ class PluginTest(testtools.TestCase):
plugin.unplug(self.vif_ovs, self.instance)
unplug_bridge_mock.assert_not_called()
@mock.patch.object(linux_net, 'delete_ovs_vif_port')
@mock.patch.object(ovsdb_lib.BaseOVS, 'delete_ovs_vif_port')
@mock.patch.object(linux_net, 'delete_bridge')
@mock.patch.object(ovs, 'sys')
def test_unplug_ovs_bridge(self, mock_sys, delete_bridge,
delete_ovs_vif_port):
calls = {
'delete_bridge': [mock.call('qbrvif-xxx-yyy', 'qvbb679325f-ca')],
'delete_ovs_vif_port': [mock.call('br0', 'qvob679325f-ca',
ovsdb_connection=None, timeout=120)]
'delete_ovs_vif_port': [mock.call('br0', 'qvob679325f-ca')]
}
mock_sys.platform = 'linux'
plugin = ovs.OvsPlugin.load(constants.PLUGIN_NAME)
@ -278,14 +272,13 @@ class PluginTest(testtools.TestCase):
delete_bridge.assert_has_calls(calls['delete_bridge'])
delete_ovs_vif_port.assert_has_calls(calls['delete_ovs_vif_port'])
@mock.patch.object(linux_net, 'delete_ovs_vif_port')
@mock.patch.object(ovsdb_lib.BaseOVS, 'delete_ovs_vif_port')
@mock.patch.object(ovs, 'sys')
def _check_unplug_ovs_windows(self, vif, mock_sys, delete_ovs_vif_port):
mock_sys.platform = constants.PLATFORM_WIN32
plugin = ovs.OvsPlugin.load(constants.PLUGIN_NAME)
plugin.unplug(vif, self.instance)
delete_ovs_vif_port.assert_called_once_with('br0', vif.id,
ovsdb_connection=None, timeout=120)
delete_ovs_vif_port.assert_called_once_with('br0', vif.id)
def test_unplug_ovs_windows(self):
self._check_unplug_ovs_windows(self.vif_ovs)
@ -293,7 +286,7 @@ class PluginTest(testtools.TestCase):
def test_unplug_ovs_bridge_windows(self):
self._check_unplug_ovs_windows(self.vif_ovs_hybrid)
@mock.patch.object(linux_net, 'ensure_ovs_bridge')
@mock.patch.object(ovsdb_lib.BaseOVS, 'ensure_ovs_bridge')
@mock.patch.object(ovs.OvsPlugin, '_create_vif_port')
def test_plug_ovs_vhostuser(self, _create_vif_port, ensure_ovs_bridge):
dp_type = ovs.OvsPlugin._get_vif_datapath_type(self.vif_vhostuser)
@ -311,8 +304,8 @@ class PluginTest(testtools.TestCase):
_create_vif_port.assert_has_calls(calls['_create_vif_port'])
ensure_ovs_bridge.assert_has_calls(calls['ensure_ovs_bridge'])
@mock.patch.object(linux_net, 'ensure_ovs_bridge')
@mock.patch.object(linux_net, 'create_ovs_vif_port')
@mock.patch.object(ovsdb_lib.BaseOVS, 'ensure_ovs_bridge')
@mock.patch.object(ovsdb_lib.BaseOVS, 'create_ovs_vif_port')
def test_plug_ovs_vhostuser_client(self, create_ovs_vif_port,
ensure_ovs_bridge):
dp_type = ovs.OvsPlugin._get_vif_datapath_type(
@ -325,9 +318,7 @@ class PluginTest(testtools.TestCase):
'ca:fe:de:ad:be:ef',
'f0000000-0000-0000-0000-000000000001',
1500, interface_type='dpdkvhostuserclient',
vhost_server_path='/var/run/openvswitch/vhub679325f-ca',
ovsdb_connection=None,
timeout=120)],
vhost_server_path='/var/run/openvswitch/vhub679325f-ca')],
'ensure_ovs_bridge': [mock.call('br0', dp_type)]
}
@ -336,17 +327,16 @@ class PluginTest(testtools.TestCase):
create_ovs_vif_port.assert_has_calls(calls['create_ovs_vif_port'])
ensure_ovs_bridge.assert_has_calls(calls['ensure_ovs_bridge'])
@mock.patch.object(linux_net, 'delete_ovs_vif_port')
@mock.patch.object(ovsdb_lib.BaseOVS, 'delete_ovs_vif_port')
def test_unplug_ovs_vhostuser(self, delete_ovs_vif_port):
calls = {
'delete_ovs_vif_port': [mock.call('br0', 'vhub679325f-ca',
ovsdb_connection=None, timeout=120)]
'delete_ovs_vif_port': [mock.call('br0', 'vhub679325f-ca')]
}
plugin = ovs.OvsPlugin.load(constants.PLUGIN_NAME)
plugin.unplug(self.vif_vhostuser, self.instance)
delete_ovs_vif_port.assert_has_calls(calls['delete_ovs_vif_port'])
@mock.patch.object(linux_net, 'ensure_ovs_bridge')
@mock.patch.object(ovsdb_lib.BaseOVS, 'ensure_ovs_bridge')
@mock.patch.object(linux_net, 'get_ifname_by_pci_address')
@mock.patch.object(linux_net, 'get_vf_num_by_pci_address')
@mock.patch.object(linux_net, 'get_representor_port')
@ -365,9 +355,7 @@ class PluginTest(testtools.TestCase):
calls = {
'ensure_ovs_bridge': [mock.call('br0',
constants.OVS_DATAPATH_SYSTEM,
ovsdb_connection=None,
timeout=120)],
constants.OVS_DATAPATH_SYSTEM)],
'get_ifname_by_pci_address': [mock.call('0002:24:12.3',
pf_interface=True,
switchdev=True)],
@ -395,7 +383,7 @@ class PluginTest(testtools.TestCase):
@mock.patch.object(linux_net, 'get_vf_num_by_pci_address')
@mock.patch.object(linux_net, 'get_representor_port')
@mock.patch.object(linux_net, 'set_interface_state')
@mock.patch.object(linux_net, 'delete_ovs_vif_port')
@mock.patch.object(ovsdb_lib.BaseOVS, 'delete_ovs_vif_port')
def test_unplug_ovs_vf_passthrough(self, delete_ovs_vif_port,
set_interface_state,
get_representor_port,
@ -409,9 +397,8 @@ class PluginTest(testtools.TestCase):
'get_vf_num_by_pci_address': [mock.call('0002:24:12.3')],
'get_representor_port': [mock.call('eth0', '2')],
'set_interface_state': [mock.call('eth0_2', 'down')],
'delete_ovs_vif_port': [mock.call('br0', 'eth0_2', timeout=120,
ovsdb_connection=None,
delete_netdev=False)]
'delete_ovs_vif_port': [mock.call('br0', 'eth0_2',
delete_netdev=False)]
}
get_ifname_by_pci_address.return_value = 'eth0'