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:
parent
ecd0b354ca
commit
7bfb01bcb1
@ -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
|
||||
|
@ -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.
|
@ -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
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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):
|
||||
|
0
vif_plug_ovs/ovsdb/__init__.py
Normal file
0
vif_plug_ovs/ovsdb/__init__.py
Normal file
27
vif_plug_ovs/ovsdb/api.py
Normal file
27
vif_plug_ovs/ovsdb/api.py
Normal 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)
|
378
vif_plug_ovs/ovsdb/impl_vsctl.py
Normal file
378
vif_plug_ovs/ovsdb/impl_vsctl.py
Normal 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
|
90
vif_plug_ovs/ovsdb/ovsdb_lib.py
Normal file
90
vif_plug_ovs/ovsdb/ovsdb_lib.py
Normal 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)
|
0
vif_plug_ovs/tests/unit/ovsdb/__init__.py
Normal file
0
vif_plug_ovs/tests/unit/ovsdb/__init__.py
Normal file
138
vif_plug_ovs/tests/unit/ovsdb/test_ovsdb_lib.py
Normal file
138
vif_plug_ovs/tests/unit/ovsdb/test_ovsdb_lib.py
Normal 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)
|
@ -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):
|
||||
|
@ -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'
|
||||
|
Loading…
Reference in New Issue
Block a user