Remove deprecated vsctl ovsdb_interface api
This was deprecated in https://review.openstack.org/#/c/503070/ so remove all the vsctl-related code, leaving just the native ovsdb api. Also removed renamed ovs_vsctl_timeout value, which was changed to ovsdb_timeout in https://review.openstack.org/#/c/518391/ Change-Id: I50dfcea3deb41df1bd01fd06b76522453a6ba50b
This commit is contained in:
parent
7b6754e9d4
commit
cf37563c83
@ -30,7 +30,7 @@ import tenacity
|
|||||||
from neutron._i18n import _
|
from neutron._i18n import _
|
||||||
from neutron.agent.common import ip_lib
|
from neutron.agent.common import ip_lib
|
||||||
from neutron.agent.common import utils
|
from neutron.agent.common import utils
|
||||||
from neutron.agent.ovsdb import api as ovsdb_api
|
from neutron.agent.ovsdb import impl_idl
|
||||||
from neutron.common import constants as common_constants
|
from neutron.common import constants as common_constants
|
||||||
from neutron.common import utils as common_utils
|
from neutron.common import utils as common_utils
|
||||||
from neutron.conf.agent import ovs_conf
|
from neutron.conf.agent import ovs_conf
|
||||||
@ -70,15 +70,15 @@ CTRL_BURST_LIMIT_MIN = 25
|
|||||||
|
|
||||||
|
|
||||||
def _ovsdb_result_pending(result):
|
def _ovsdb_result_pending(result):
|
||||||
"""Return True if ovs-vsctl indicates the result is still pending."""
|
"""Return True if ovsdb indicates the result is still pending."""
|
||||||
# ovs-vsctl can return '[]' for an ofport that has not yet been assigned
|
# ovsdb can return '[]' for an ofport that has not yet been assigned
|
||||||
return result == []
|
return result == []
|
||||||
|
|
||||||
|
|
||||||
def _ovsdb_retry(fn):
|
def _ovsdb_retry(fn):
|
||||||
"""Decorator for retrying when OVS has yet to assign an ofport.
|
"""Decorator for retrying when OVS has yet to assign an ofport.
|
||||||
|
|
||||||
The instance's vsctl_timeout is used as the max waiting time. This relies
|
The instance's ovsdb_timeout is used as the max waiting time. This relies
|
||||||
on the fact that instance methods receive self as the first argument.
|
on the fact that instance methods receive self as the first argument.
|
||||||
"""
|
"""
|
||||||
@six.wraps(fn)
|
@six.wraps(fn)
|
||||||
@ -89,7 +89,7 @@ def _ovsdb_retry(fn):
|
|||||||
retry=tenacity.retry_if_result(_ovsdb_result_pending),
|
retry=tenacity.retry_if_result(_ovsdb_result_pending),
|
||||||
wait=tenacity.wait_exponential(multiplier=0.01, max=1),
|
wait=tenacity.wait_exponential(multiplier=0.01, max=1),
|
||||||
stop=tenacity.stop_after_delay(
|
stop=tenacity.stop_after_delay(
|
||||||
self.vsctl_timeout))(fn)
|
self.ovsdb_timeout))(fn)
|
||||||
return new_fn(*args, **kwargs)
|
return new_fn(*args, **kwargs)
|
||||||
return wrapped
|
return wrapped
|
||||||
|
|
||||||
@ -113,8 +113,8 @@ class VifPort(object):
|
|||||||
class BaseOVS(object):
|
class BaseOVS(object):
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.vsctl_timeout = cfg.CONF.OVS.ovsdb_timeout
|
self.ovsdb_timeout = cfg.CONF.OVS.ovsdb_timeout
|
||||||
self.ovsdb = ovsdb_api.from_config(self)
|
self.ovsdb = impl_idl.api_factory()
|
||||||
|
|
||||||
def add_manager(self, connection_uri, timeout=_SENTINEL):
|
def add_manager(self, connection_uri, timeout=_SENTINEL):
|
||||||
"""Have ovsdb-server listen for manager connections
|
"""Have ovsdb-server listen for manager connections
|
||||||
|
@ -15,21 +15,6 @@
|
|||||||
import collections
|
import collections
|
||||||
import uuid
|
import uuid
|
||||||
|
|
||||||
from oslo_config import cfg
|
|
||||||
from oslo_utils import importutils
|
|
||||||
|
|
||||||
from neutron.conf.agent import ovsdb_api
|
|
||||||
|
|
||||||
|
|
||||||
ovsdb_api.register_ovsdb_api_opts()
|
|
||||||
|
|
||||||
|
|
||||||
def from_config(context, iface_name=None):
|
|
||||||
"""Return the configured OVSDB API implementation"""
|
|
||||||
iface = importutils.import_module(
|
|
||||||
ovsdb_api.interface_map[iface_name or cfg.CONF.OVS.ovsdb_interface])
|
|
||||||
return iface.api_factory(context)
|
|
||||||
|
|
||||||
|
|
||||||
def val_to_py(val):
|
def val_to_py(val):
|
||||||
"""Convert a json ovsdb return value to native python object"""
|
"""Convert a json ovsdb return value to native python object"""
|
||||||
@ -41,14 +26,3 @@ def val_to_py(val):
|
|||||||
elif val[0] == "map":
|
elif val[0] == "map":
|
||||||
return {val_to_py(x): val_to_py(y) for x, y in val[1]}
|
return {val_to_py(x): val_to_py(y) for x, y in val[1]}
|
||||||
return val
|
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)
|
|
||||||
|
@ -42,7 +42,7 @@ ovs_conf.register_ovs_agent_opts()
|
|||||||
_connection = None
|
_connection = None
|
||||||
|
|
||||||
|
|
||||||
def api_factory(context):
|
def api_factory():
|
||||||
global _connection
|
global _connection
|
||||||
if _connection is None:
|
if _connection is None:
|
||||||
_connection = connection.Connection(
|
_connection = connection.Connection(
|
||||||
|
@ -1,345 +0,0 @@
|
|||||||
# Copyright (c) 2014 OpenStack Foundation
|
|
||||||
#
|
|
||||||
# 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_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 neutron.agent.common import utils
|
|
||||||
from neutron.agent.ovsdb import api as ovsdb
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
def api_factory(context):
|
|
||||||
return OvsdbVsctl(context)
|
|
||||||
|
|
||||||
|
|
||||||
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.vsctl_timeout,
|
|
||||||
'--oneline', '--format=json']
|
|
||||||
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 utils.execute(full_args, run_as_root=True,
|
|
||||||
log_fail_as_error=False).rstrip()
|
|
||||||
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] = ovsdb.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(ovsdb.py_to_val(k), ovsdb.py_to_val(v))
|
|
||||||
for k, v in value.items()]
|
|
||||||
else:
|
|
||||||
args.append(ovsdb.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_remove(self, table, record, column, *values, **keyvalues):
|
|
||||||
raise NotImplementedError()
|
|
||||||
|
|
||||||
def db_find_rows(self, table, *conditions, **kwargs):
|
|
||||||
raise NotImplementedError()
|
|
||||||
|
|
||||||
def db_list_rows(self, table, record=None, if_exists=False):
|
|
||||||
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, ovsdb.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(ovsdb.py_to_val, val))))
|
|
||||||
else:
|
|
||||||
args.append("%s%s%s" % (col, op, ovsdb.py_to_val(val)))
|
|
||||||
return args
|
|
@ -24,12 +24,16 @@ import tenacity
|
|||||||
|
|
||||||
from neutron.agent.ovsdb.native import exceptions as ovsdb_exc
|
from neutron.agent.ovsdb.native import exceptions as ovsdb_exc
|
||||||
from neutron.agent.ovsdb.native import helpers
|
from neutron.agent.ovsdb.native import helpers
|
||||||
|
from neutron.conf.agent import ovsdb_api
|
||||||
|
|
||||||
TransactionQueue = moves.moved_class(_connection.TransactionQueue,
|
TransactionQueue = moves.moved_class(_connection.TransactionQueue,
|
||||||
'TransactionQueue', __name__)
|
'TransactionQueue', __name__)
|
||||||
Connection = moves.moved_class(_connection.Connection, 'Connection', __name__)
|
Connection = moves.moved_class(_connection.Connection, 'Connection', __name__)
|
||||||
|
|
||||||
|
|
||||||
|
ovsdb_api.register_ovsdb_api_opts()
|
||||||
|
|
||||||
|
|
||||||
def configure_ssl_conn():
|
def configure_ssl_conn():
|
||||||
"""Configures required settings for an SSL based OVSDB client connection
|
"""Configures required settings for an SSL based OVSDB client connection
|
||||||
|
|
||||||
|
@ -17,12 +17,10 @@ from oslo_config import cfg
|
|||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
|
|
||||||
from neutron.agent.common import ovs_lib
|
from neutron.agent.common import ovs_lib
|
||||||
from neutron.agent.linux import ip_lib
|
|
||||||
from neutron.common import config
|
from neutron.common import config
|
||||||
from neutron.conf.agent import cmd
|
from neutron.conf.agent import cmd
|
||||||
from neutron.conf.agent import common as agent_config
|
from neutron.conf.agent import common as agent_config
|
||||||
from neutron.conf.agent.l3 import config as l3_config
|
from neutron.conf.agent.l3 import config as l3_config
|
||||||
from neutron.plugins.ml2.drivers.openvswitch.agent.common import constants
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -47,38 +45,6 @@ def setup_conf():
|
|||||||
return conf
|
return conf
|
||||||
|
|
||||||
|
|
||||||
def get_bridge_deletable_ports(br):
|
|
||||||
"""Get bridge deletable ports
|
|
||||||
|
|
||||||
Return a list of OVS Bridge ports, excluding the ports who should not be
|
|
||||||
cleaned. such ports are tagged with the 'skip_cleanup' key in external_ids.
|
|
||||||
"""
|
|
||||||
return [port.port_name for port in br.get_vif_ports()
|
|
||||||
if constants.SKIP_CLEANUP not in
|
|
||||||
br.get_port_external_ids(port.port_name)]
|
|
||||||
|
|
||||||
|
|
||||||
def collect_neutron_ports(bridges):
|
|
||||||
"""Collect ports created by Neutron from OVS."""
|
|
||||||
ports = []
|
|
||||||
for bridge in bridges:
|
|
||||||
ovs = ovs_lib.OVSBridge(bridge)
|
|
||||||
ports += get_bridge_deletable_ports(ovs)
|
|
||||||
return ports
|
|
||||||
|
|
||||||
|
|
||||||
def delete_neutron_ports(ports):
|
|
||||||
"""Delete non-internal ports created by Neutron
|
|
||||||
|
|
||||||
Non-internal OVS ports need to be removed manually.
|
|
||||||
"""
|
|
||||||
for port in ports:
|
|
||||||
device = ip_lib.IPDevice(port)
|
|
||||||
if device.exists():
|
|
||||||
device.link.delete()
|
|
||||||
LOG.info("Deleting port: %s", port)
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
"""Main method for cleaning up OVS bridges.
|
"""Main method for cleaning up OVS bridges.
|
||||||
|
|
||||||
@ -103,28 +69,9 @@ def do_main(conf):
|
|||||||
else:
|
else:
|
||||||
bridges = available_configuration_bridges
|
bridges = available_configuration_bridges
|
||||||
|
|
||||||
try:
|
|
||||||
# The ovs_cleanup method not added to the deprecated vsctl backend
|
|
||||||
for bridge in bridges:
|
for bridge in bridges:
|
||||||
LOG.info("Cleaning bridge: %s", bridge)
|
LOG.info("Cleaning bridge: %s", bridge)
|
||||||
ovs.ovsdb.ovs_cleanup(bridge,
|
ovs.ovsdb.ovs_cleanup(bridge,
|
||||||
conf.ovs_all_ports).execute(check_error=True)
|
conf.ovs_all_ports).execute(check_error=True)
|
||||||
except AttributeError:
|
|
||||||
# Collect existing ports created by Neutron on configuration bridges.
|
|
||||||
# After deleting ports from OVS bridges, we cannot determine which
|
|
||||||
# ports were created by Neutron, so port information is collected now.
|
|
||||||
ports = collect_neutron_ports(available_configuration_bridges)
|
|
||||||
|
|
||||||
for bridge in bridges:
|
|
||||||
LOG.info("Cleaning bridge: %s", bridge)
|
|
||||||
ovs = ovs_lib.OVSBridge(bridge)
|
|
||||||
if conf.ovs_all_ports:
|
|
||||||
port_names = ovs.get_port_name_list()
|
|
||||||
else:
|
|
||||||
port_names = get_bridge_deletable_ports(ovs)
|
|
||||||
for port_name in port_names:
|
|
||||||
ovs.delete_port(port_name)
|
|
||||||
# Remove remaining ports created by Neutron (usually veth pair)
|
|
||||||
delete_neutron_ports(ports)
|
|
||||||
|
|
||||||
LOG.info("OVS cleanup completed successfully")
|
LOG.info("OVS cleanup completed successfully")
|
||||||
|
@ -193,7 +193,6 @@ def check_vf_extended_management():
|
|||||||
|
|
||||||
|
|
||||||
def check_ovsdb_native():
|
def check_ovsdb_native():
|
||||||
cfg.CONF.set_override('ovsdb_interface', 'native', group='OVS')
|
|
||||||
result = checks.ovsdb_native_supported()
|
result = checks.ovsdb_native_supported()
|
||||||
if not result:
|
if not result:
|
||||||
LOG.error('Check for native OVSDB support failed.')
|
LOG.error('Check for native OVSDB support failed.')
|
||||||
@ -365,8 +364,6 @@ def enable_tests_from_config():
|
|||||||
cfg.CONF.set_default('arp_responder', True)
|
cfg.CONF.set_default('arp_responder', True)
|
||||||
if not cfg.CONF.AGENT.use_helper_for_ns_read:
|
if not cfg.CONF.AGENT.use_helper_for_ns_read:
|
||||||
cfg.CONF.set_default('read_netns', True)
|
cfg.CONF.set_default('read_netns', True)
|
||||||
if cfg.CONF.OVS.ovsdb_interface == 'native':
|
|
||||||
cfg.CONF.set_default('ovsdb_native', True)
|
|
||||||
if cfg.CONF.l3_ha:
|
if cfg.CONF.l3_ha:
|
||||||
cfg.CONF.set_default('keepalived_ipv6_support', True)
|
cfg.CONF.set_default('keepalived_ipv6_support', True)
|
||||||
cfg.CONF.set_default('ip_nonlocal_bind', True)
|
cfg.CONF.set_default('ip_nonlocal_bind', True)
|
||||||
|
@ -23,7 +23,6 @@ DEFAULT_OVSDB_TIMEOUT = 10
|
|||||||
OPTS = [
|
OPTS = [
|
||||||
cfg.IntOpt('ovsdb_timeout',
|
cfg.IntOpt('ovsdb_timeout',
|
||||||
default=DEFAULT_OVSDB_TIMEOUT,
|
default=DEFAULT_OVSDB_TIMEOUT,
|
||||||
deprecated_name='ovs_vsctl_timeout', deprecated_group='DEFAULT',
|
|
||||||
help=_('Timeout in seconds for ovsdb commands. '
|
help=_('Timeout in seconds for ovsdb commands. '
|
||||||
'If the timeout expires, ovsdb commands will fail with '
|
'If the timeout expires, ovsdb commands will fail with '
|
||||||
'ALARMCLOCK error.')),
|
'ALARMCLOCK error.')),
|
||||||
|
@ -17,18 +17,7 @@ from neutron._i18n import _
|
|||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
|
|
||||||
|
|
||||||
interface_map = {
|
|
||||||
'vsctl': 'neutron.agent.ovsdb.impl_vsctl',
|
|
||||||
'native': 'neutron.agent.ovsdb.impl_idl',
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
API_OPTS = [
|
API_OPTS = [
|
||||||
cfg.StrOpt('ovsdb_interface',
|
|
||||||
deprecated_for_removal=True,
|
|
||||||
choices=interface_map.keys(),
|
|
||||||
default='native',
|
|
||||||
help=_('The interface for interacting with the OVSDB')),
|
|
||||||
cfg.StrOpt('ovsdb_connection',
|
cfg.StrOpt('ovsdb_connection',
|
||||||
default='tcp:127.0.0.1:6640',
|
default='tcp:127.0.0.1:6640',
|
||||||
help=_('The connection string for the OVSDB backend. '
|
help=_('The connection string for the OVSDB backend. '
|
||||||
|
@ -1021,7 +1021,7 @@ class OVSNeutronAgent(l2population_rpc.L2populationRpcCallBackTunnelMixin,
|
|||||||
|
|
||||||
'''
|
'''
|
||||||
# Ensure the integration bridge is created.
|
# Ensure the integration bridge is created.
|
||||||
# ovs_lib.OVSBridge.create() will run
|
# ovs_lib.OVSBridge.create() will run the equivalent of
|
||||||
# ovs-vsctl -- --may-exist add-br BRIDGE_NAME
|
# ovs-vsctl -- --may-exist add-br BRIDGE_NAME
|
||||||
# which does nothing if bridge already exists.
|
# which does nothing if bridge already exists.
|
||||||
self.int_br.create()
|
self.int_br.create()
|
||||||
|
@ -27,7 +27,6 @@ from neutron.objects.qos import policy
|
|||||||
from neutron.objects.qos import rule
|
from neutron.objects.qos import rule
|
||||||
from neutron.tests.common.agents import l2_extensions
|
from neutron.tests.common.agents import l2_extensions
|
||||||
from neutron.tests.functional.agent.l2 import base
|
from neutron.tests.functional.agent.l2 import base
|
||||||
from neutron.tests.functional.agent.linux import base as linux_base
|
|
||||||
|
|
||||||
|
|
||||||
load_tests = testscenarios.load_tests_apply_scenarios
|
load_tests = testscenarios.load_tests_apply_scenarios
|
||||||
@ -179,16 +178,11 @@ class OVSAgentQoSExtensionTestFramework(base.OVSAgentTestFramework):
|
|||||||
|
|
||||||
class TestOVSAgentQosExtension(OVSAgentQoSExtensionTestFramework):
|
class TestOVSAgentQosExtension(OVSAgentQoSExtensionTestFramework):
|
||||||
|
|
||||||
interface_scenarios = linux_base.BaseOVSLinuxTestCase.scenarios
|
scenarios = [
|
||||||
|
|
||||||
direction_scenarios = [
|
|
||||||
('ingress', {'direction': constants.INGRESS_DIRECTION}),
|
('ingress', {'direction': constants.INGRESS_DIRECTION}),
|
||||||
('egress', {'direction': constants.EGRESS_DIRECTION})
|
('egress', {'direction': constants.EGRESS_DIRECTION})
|
||||||
]
|
]
|
||||||
|
|
||||||
scenarios = testscenarios.multiply_scenarios(
|
|
||||||
interface_scenarios, direction_scenarios)
|
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(TestOVSAgentQosExtension, self).setUp()
|
super(TestOVSAgentQosExtension, self).setUp()
|
||||||
self.test_bw_limit_rule_1.direction = self.direction
|
self.test_bw_limit_rule_1.direction = self.direction
|
||||||
|
@ -12,8 +12,6 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
import testscenarios
|
|
||||||
|
|
||||||
from neutron.tests.common.exclusive_resources import ip_address
|
from neutron.tests.common.exclusive_resources import ip_address
|
||||||
from neutron.tests.functional import base
|
from neutron.tests.functional import base
|
||||||
|
|
||||||
@ -26,18 +24,7 @@ MARKED_BLOCK_RULE = '-m mark --mark %s -j DROP' % MARK_VALUE
|
|||||||
ICMP_BLOCK_RULE = '-p icmp -j DROP'
|
ICMP_BLOCK_RULE = '-p icmp -j DROP'
|
||||||
|
|
||||||
|
|
||||||
# Regarding MRO, it goes BaseOVSLinuxTestCase, WithScenarios,
|
class BaseOVSLinuxTestCase(base.BaseSudoTestCase):
|
||||||
# BaseSudoTestCase, ..., UnitTest, object. setUp is not defined in
|
|
||||||
# WithScenarios, so it will correctly be found in BaseSudoTestCase.
|
|
||||||
class BaseOVSLinuxTestCase(testscenarios.WithScenarios, base.BaseSudoTestCase):
|
|
||||||
scenarios = [
|
|
||||||
('vsctl', dict(ovsdb_interface='vsctl')),
|
|
||||||
('native', dict(ovsdb_interface='native')),
|
|
||||||
]
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
super(BaseOVSLinuxTestCase, self).setUp()
|
|
||||||
self.config(group='OVS', ovsdb_interface=self.ovsdb_interface)
|
|
||||||
|
|
||||||
def get_test_net_address(self, block):
|
def get_test_net_address(self, block):
|
||||||
"""Return exclusive address based on RFC 5737.
|
"""Return exclusive address based on RFC 5737.
|
||||||
|
@ -36,7 +36,6 @@ from neutron.conf.agent import securitygroups_rpc as security_config
|
|||||||
from neutron.tests.common import conn_testers
|
from neutron.tests.common import conn_testers
|
||||||
from neutron.tests.common import helpers
|
from neutron.tests.common import helpers
|
||||||
from neutron.tests.functional.agent.linux import base as linux_base
|
from neutron.tests.functional.agent.linux import base as linux_base
|
||||||
from neutron.tests.functional import base
|
|
||||||
from neutron.tests.functional import constants as test_constants
|
from neutron.tests.functional import constants as test_constants
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
@ -76,7 +75,7 @@ def _add_rule(sg_rules, base, port_range_min=None, port_range_max=None):
|
|||||||
sg_rules.append(rule)
|
sg_rules.append(rule)
|
||||||
|
|
||||||
|
|
||||||
class BaseFirewallTestCase(base.BaseSudoTestCase):
|
class BaseFirewallTestCase(linux_base.BaseOVSLinuxTestCase):
|
||||||
FAKE_SECURITY_GROUP_ID = uuidutils.generate_uuid()
|
FAKE_SECURITY_GROUP_ID = uuidutils.generate_uuid()
|
||||||
MAC_SPOOFED = "fa:16:3e:9a:2f:48"
|
MAC_SPOOFED = "fa:16:3e:9a:2f:48"
|
||||||
scenarios_iptables = testscenarios.multiply_scenarios(
|
scenarios_iptables = testscenarios.multiply_scenarios(
|
||||||
@ -87,8 +86,7 @@ class BaseFirewallTestCase(base.BaseSudoTestCase):
|
|||||||
|
|
||||||
scenarios_ovs_fw_interfaces = testscenarios.multiply_scenarios(
|
scenarios_ovs_fw_interfaces = testscenarios.multiply_scenarios(
|
||||||
[('OVS Firewall Driver', {'initialize': 'initialize_ovs',
|
[('OVS Firewall Driver', {'initialize': 'initialize_ovs',
|
||||||
'firewall_name': 'openvswitch'})],
|
'firewall_name': 'openvswitch'})])
|
||||||
linux_base.BaseOVSLinuxTestCase.scenarios)
|
|
||||||
|
|
||||||
scenarios = scenarios_iptables + scenarios_ovs_fw_interfaces
|
scenarios = scenarios_iptables + scenarios_ovs_fw_interfaces
|
||||||
|
|
||||||
@ -126,7 +124,6 @@ class BaseFirewallTestCase(base.BaseSudoTestCase):
|
|||||||
return tester, firewall_drv
|
return tester, firewall_drv
|
||||||
|
|
||||||
def initialize_ovs(self):
|
def initialize_ovs(self):
|
||||||
self.config(group='OVS', ovsdb_interface=self.ovsdb_interface)
|
|
||||||
# Tests for ovs requires kernel >= 4.3 and OVS >= 2.5
|
# Tests for ovs requires kernel >= 4.3 and OVS >= 2.5
|
||||||
if not checks.ovs_conntrack_supported():
|
if not checks.ovs_conntrack_supported():
|
||||||
self.skipTest("Open vSwitch with conntrack is not installed "
|
self.skipTest("Open vSwitch with conntrack is not installed "
|
||||||
|
@ -36,9 +36,9 @@ from neutron.tests.common import base as common_base
|
|||||||
from neutron.tests.common import helpers
|
from neutron.tests.common import helpers
|
||||||
from neutron.tests.common import net_helpers
|
from neutron.tests.common import net_helpers
|
||||||
from neutron.tests.functional.agent import test_ovs_lib
|
from neutron.tests.functional.agent import test_ovs_lib
|
||||||
from neutron.tests.functional import base
|
|
||||||
from neutron.tests import tools
|
from neutron.tests import tools
|
||||||
|
|
||||||
|
load_tests = testscenarios.load_tests_apply_scenarios
|
||||||
|
|
||||||
OVS_TRACE_FINAL_FLOW = 'Final flow'
|
OVS_TRACE_FINAL_FLOW = 'Final flow'
|
||||||
OVS_TRACE_DATAPATH_ACTIONS = 'Datapath actions'
|
OVS_TRACE_DATAPATH_ACTIONS = 'Datapath actions'
|
||||||
@ -47,14 +47,12 @@ cfg.CONF.import_group('OVS', 'neutron.plugins.ml2.drivers.openvswitch.agent.'
|
|||||||
'common.config')
|
'common.config')
|
||||||
|
|
||||||
|
|
||||||
class OVSAgentTestBase(test_ovs_lib.OVSBridgeTestBase,
|
class OVSAgentTestBase(test_ovs_lib.OVSBridgeTestBase):
|
||||||
base.BaseSudoTestCase):
|
scenarios = [
|
||||||
scenarios = testscenarios.multiply_scenarios([
|
|
||||||
('ofctl', {'main_module': ('neutron.plugins.ml2.drivers.openvswitch.'
|
('ofctl', {'main_module': ('neutron.plugins.ml2.drivers.openvswitch.'
|
||||||
'agent.openflow.ovs_ofctl.main')}),
|
'agent.openflow.ovs_ofctl.main')}),
|
||||||
('native', {'main_module': ('neutron.plugins.ml2.drivers.openvswitch.'
|
('native', {'main_module': ('neutron.plugins.ml2.drivers.openvswitch.'
|
||||||
'agent.openflow.native.main')})],
|
'agent.openflow.native.main')})]
|
||||||
test_ovs_lib.OVSBridgeTestBase.scenarios)
|
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(OVSAgentTestBase, self).setUp()
|
super(OVSAgentTestBase, self).setUp()
|
||||||
|
@ -528,7 +528,6 @@ class OVSBridgeTestCase(OVSBridgeTestBase):
|
|||||||
if_exists=False))
|
if_exists=False))
|
||||||
txn.add(ovsdb.db_set('Interface', port_name,
|
txn.add(ovsdb.db_set('Interface', port_name,
|
||||||
('type', 'internal')))
|
('type', 'internal')))
|
||||||
# native gives a more specific exception than vsctl
|
|
||||||
self.assertRaises((RuntimeError, idlutils.RowNotFound),
|
self.assertRaises((RuntimeError, idlutils.RowNotFound),
|
||||||
del_port_mod_iface)
|
del_port_mod_iface)
|
||||||
|
|
||||||
@ -583,7 +582,7 @@ class OVSLibTestCase(base.BaseOVSLinuxTestCase):
|
|||||||
self.assertIn(conn_uri, self.ovs.get_manager())
|
self.assertIn(conn_uri, self.ovs.get_manager())
|
||||||
self.assertEqual(self.ovs.db_get_val('Manager', conn_uri,
|
self.assertEqual(self.ovs.db_get_val('Manager', conn_uri,
|
||||||
'inactivity_probe'),
|
'inactivity_probe'),
|
||||||
self.ovs.vsctl_timeout * 1000)
|
self.ovs.ovsdb_timeout * 1000)
|
||||||
self.ovs.remove_manager(conn_uri)
|
self.ovs.remove_manager(conn_uri)
|
||||||
self.assertNotIn(conn_uri, self.ovs.get_manager())
|
self.assertNotIn(conn_uri, self.ovs.get_manager())
|
||||||
|
|
||||||
|
@ -63,8 +63,6 @@ class LoggingExtensionTestFramework(test_firewall.BaseFirewallTestCase):
|
|||||||
|
|
||||||
def initialize_ovs_fw_log(self):
|
def initialize_ovs_fw_log(self):
|
||||||
mock.patch('ryu.base.app_manager.AppManager.get_instance').start()
|
mock.patch('ryu.base.app_manager.AppManager.get_instance').start()
|
||||||
mock.patch(
|
|
||||||
'neutron.agent.ovsdb.impl_vsctl.OvsdbVsctl.transaction').start()
|
|
||||||
agent_api = ovs_ext_api.OVSAgentExtensionAPI(
|
agent_api = ovs_ext_api.OVSAgentExtensionAPI(
|
||||||
ovs_bridge.OVSAgentBridge(self.tester.bridge.br_name),
|
ovs_bridge.OVSAgentBridge(self.tester.bridge.br_name),
|
||||||
ovs_bridge.OVSAgentBridge('br-tun'))
|
ovs_bridge.OVSAgentBridge('br-tun'))
|
||||||
|
@ -15,7 +15,6 @@
|
|||||||
import collections
|
import collections
|
||||||
|
|
||||||
import mock
|
import mock
|
||||||
from neutron_lib import constants
|
|
||||||
from neutron_lib import exceptions
|
from neutron_lib import exceptions
|
||||||
from oslo_serialization import jsonutils
|
from oslo_serialization import jsonutils
|
||||||
from oslo_utils import uuidutils
|
from oslo_utils import uuidutils
|
||||||
@ -24,23 +23,9 @@ import testtools
|
|||||||
|
|
||||||
from neutron.agent.common import ovs_lib
|
from neutron.agent.common import ovs_lib
|
||||||
from neutron.agent.common import utils
|
from neutron.agent.common import utils
|
||||||
from neutron.conf.agent import common as config
|
|
||||||
from neutron.plugins.ml2.drivers.openvswitch.agent.common \
|
from neutron.plugins.ml2.drivers.openvswitch.agent.common \
|
||||||
import constants as p_const
|
import constants as p_const
|
||||||
from neutron.tests import base
|
from neutron.tests import base
|
||||||
from neutron.tests import tools
|
|
||||||
|
|
||||||
|
|
||||||
OVS_LINUX_KERN_VERS_WITHOUT_VXLAN = "3.12.0"
|
|
||||||
|
|
||||||
# some test data for get_vif_port_to_ofport_map that exhibited bug 1444269
|
|
||||||
OVSLIST_WITH_UNSET_PORT = (
|
|
||||||
'{"data":[["patch-tun",["map",[]],1],["tap2ab72a72-44",["map",[["attached-'
|
|
||||||
'mac","fa:16:3e:b0:f8:38"],["iface-id","2ab72a72-4407-4ef3-806a-b2172f3e4d'
|
|
||||||
'c7"],["iface-status","active"]]],2],["tap6b108774-15",["map",[["attached-'
|
|
||||||
'mac","fa:16:3e:02:f5:91"],["iface-id","6b108774-1559-45e9-a7c3-b714f11722'
|
|
||||||
'cf"],["iface-status","active"]]],["set",[]]]],"headings":["name","externa'
|
|
||||||
'l_ids","ofport"]}')
|
|
||||||
|
|
||||||
|
|
||||||
class OFCTLParamListMatcher(object):
|
class OFCTLParamListMatcher(object):
|
||||||
@ -84,16 +69,6 @@ class StringSetMatcher(object):
|
|||||||
return '<comma-separated string for %s%s>' % (self.set, sep)
|
return '<comma-separated string for %s%s>' % (self.set, sep)
|
||||||
|
|
||||||
|
|
||||||
def vsctl_only(f):
|
|
||||||
# NOTE(ivasilevskaya) as long as some tests rely heavily on mocking
|
|
||||||
# direct vsctl commands, need to ensure that ovsdb_interface = 'vsctl'
|
|
||||||
# TODO(ivasilevskaya) introduce alternative tests for native interface?
|
|
||||||
def wrapper(*args, **kwargs):
|
|
||||||
config.cfg.CONF.set_override("ovsdb_interface", "vsctl", group="OVS")
|
|
||||||
return f(*args, **kwargs)
|
|
||||||
return wrapper
|
|
||||||
|
|
||||||
|
|
||||||
class OVS_Lib_Test(base.BaseTestCase):
|
class OVS_Lib_Test(base.BaseTestCase):
|
||||||
"""A test suite to exercise the OVS libraries shared by Neutron agents.
|
"""A test suite to exercise the OVS libraries shared by Neutron agents.
|
||||||
|
|
||||||
@ -101,33 +76,16 @@ class OVS_Lib_Test(base.BaseTestCase):
|
|||||||
can run on any system. That does, however, limit their scope.
|
can run on any system. That does, however, limit their scope.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@vsctl_only
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(OVS_Lib_Test, self).setUp()
|
super(OVS_Lib_Test, self).setUp()
|
||||||
self.BR_NAME = "br-int"
|
self.BR_NAME = "br-int"
|
||||||
|
|
||||||
|
# Don't attempt to connect to ovsdb
|
||||||
|
mock.patch('neutron.agent.ovsdb.impl_idl.api_factory').start()
|
||||||
self.br = ovs_lib.OVSBridge(self.BR_NAME)
|
self.br = ovs_lib.OVSBridge(self.BR_NAME)
|
||||||
self.execute = mock.patch.object(
|
self.execute = mock.patch.object(
|
||||||
utils, "execute", spec=utils.execute).start()
|
utils, "execute", spec=utils.execute).start()
|
||||||
|
|
||||||
@property
|
|
||||||
def TO(self):
|
|
||||||
return "--timeout=%s" % self.br.vsctl_timeout
|
|
||||||
|
|
||||||
def _vsctl_args(self, *args):
|
|
||||||
cmd = ['ovs-vsctl', self.TO, '--oneline', '--format=json', '--']
|
|
||||||
cmd += args
|
|
||||||
return cmd
|
|
||||||
|
|
||||||
def _vsctl_mock(self, *args):
|
|
||||||
cmd = self._vsctl_args(*args)
|
|
||||||
return mock.call(cmd, run_as_root=True, log_fail_as_error=False)
|
|
||||||
|
|
||||||
def _verify_vsctl_mock(self, *args):
|
|
||||||
cmd = self._vsctl_args(*args)
|
|
||||||
self.execute.assert_called_once_with(cmd, run_as_root=True,
|
|
||||||
log_fail_as_error=False)
|
|
||||||
|
|
||||||
def test_vifport(self):
|
def test_vifport(self):
|
||||||
"""Create and stringify vif port, confirm no exceptions."""
|
"""Create and stringify vif port, confirm no exceptions."""
|
||||||
|
|
||||||
@ -147,9 +105,6 @@ class OVS_Lib_Test(base.BaseTestCase):
|
|||||||
# test __str__
|
# test __str__
|
||||||
str(port)
|
str(port)
|
||||||
|
|
||||||
def _build_timeout_opt(self, exp_timeout):
|
|
||||||
return "--timeout=%d" % exp_timeout if exp_timeout else self.TO
|
|
||||||
|
|
||||||
def test_add_flow(self):
|
def test_add_flow(self):
|
||||||
ofport = "99"
|
ofport = "99"
|
||||||
vid = 4000
|
vid = 4000
|
||||||
@ -276,32 +231,6 @@ class OVS_Lib_Test(base.BaseTestCase):
|
|||||||
process_input="hard_timeout=0,idle_timeout=0,priority=1,"
|
process_input="hard_timeout=0,idle_timeout=0,priority=1,"
|
||||||
"cookie=1234,actions=normal")
|
"cookie=1234,actions=normal")
|
||||||
|
|
||||||
def _test_get_port_ofport(self, ofport, expected_result):
|
|
||||||
pname = "tap99"
|
|
||||||
self.br.vsctl_timeout = 0 # Don't waste precious time retrying
|
|
||||||
self.execute.return_value = self._encode_ovs_json(
|
|
||||||
['ofport'], [[ofport]])
|
|
||||||
self.assertEqual(self.br.get_port_ofport(pname), expected_result)
|
|
||||||
self._verify_vsctl_mock("--columns=ofport", "list", "Interface", pname)
|
|
||||||
|
|
||||||
def test_get_port_ofport_succeeds_for_valid_ofport(self):
|
|
||||||
self._test_get_port_ofport(6, 6)
|
|
||||||
|
|
||||||
def test_get_port_ofport_returns_invalid_ofport_for_non_int(self):
|
|
||||||
self._test_get_port_ofport([], ovs_lib.INVALID_OFPORT)
|
|
||||||
|
|
||||||
def test_get_port_ofport_returns_invalid_for_invalid(self):
|
|
||||||
self._test_get_port_ofport(ovs_lib.INVALID_OFPORT,
|
|
||||||
ovs_lib.INVALID_OFPORT)
|
|
||||||
|
|
||||||
def test_get_port_mac(self):
|
|
||||||
pname = "tap99"
|
|
||||||
self.br.vsctl_timeout = 0 # Don't waste precious time retrying
|
|
||||||
self.execute.return_value = self._encode_ovs_json(
|
|
||||||
['mac_in_use'], [['00:01:02:03:04:05']])
|
|
||||||
expected_result = '00:01:02:03:04:05'
|
|
||||||
self.assertEqual(self.br.get_port_mac(pname), expected_result)
|
|
||||||
|
|
||||||
def test_default_datapath(self):
|
def test_default_datapath(self):
|
||||||
# verify kernel datapath is default
|
# verify kernel datapath is default
|
||||||
expected = p_const.OVS_DATAPATH_SYSTEM
|
expected = p_const.OVS_DATAPATH_SYSTEM
|
||||||
@ -444,37 +373,6 @@ class OVS_Lib_Test(base.BaseTestCase):
|
|||||||
self.br.mod_flow,
|
self.br.mod_flow,
|
||||||
**params)
|
**params)
|
||||||
|
|
||||||
def test_ofctl_of_version_use_highest(self):
|
|
||||||
self.br.add_flow(in_port=1, actions="drop")
|
|
||||||
self.execute.assert_has_calls([
|
|
||||||
mock.call(['ovs-ofctl', 'add-flows', '-O', p_const.OPENFLOW10,
|
|
||||||
mock.ANY, '-'], process_input=mock.ANY,
|
|
||||||
run_as_root=mock.ANY)
|
|
||||||
])
|
|
||||||
self.br.use_at_least_protocol(p_const.OPENFLOW12)
|
|
||||||
self.execute.reset_mock()
|
|
||||||
self.br.add_flow(in_port=1, actions="drop")
|
|
||||||
self.execute.assert_has_calls([
|
|
||||||
mock.call(['ovs-ofctl', 'add-flows', '-O', p_const.OPENFLOW12,
|
|
||||||
mock.ANY, '-'], process_input=mock.ANY,
|
|
||||||
run_as_root=mock.ANY),
|
|
||||||
])
|
|
||||||
|
|
||||||
def test_ofctl_of_version_keep_highest(self):
|
|
||||||
self.br.use_at_least_protocol(p_const.OPENFLOW13)
|
|
||||||
self.br.use_at_least_protocol(p_const.OPENFLOW12)
|
|
||||||
self.execute.reset_mock()
|
|
||||||
self.br.add_flow(in_port=1, actions="drop")
|
|
||||||
self.execute.assert_has_calls([
|
|
||||||
mock.call(['ovs-ofctl', 'add-flows', '-O', p_const.OPENFLOW13,
|
|
||||||
mock.ANY, '-'], process_input=mock.ANY,
|
|
||||||
run_as_root=mock.ANY),
|
|
||||||
])
|
|
||||||
|
|
||||||
def test_ofctl_of_version_use_unknown(self):
|
|
||||||
with testtools.ExpectedException(Exception):
|
|
||||||
self.br.use_at_least_protocol("OpenFlow42")
|
|
||||||
|
|
||||||
def test_run_ofctl_retry_on_socket_error(self):
|
def test_run_ofctl_retry_on_socket_error(self):
|
||||||
err = RuntimeError('failed to connect to socket')
|
err = RuntimeError('failed to connect to socket')
|
||||||
self.execute.side_effect = [err] * 5
|
self.execute.side_effect = [err] * 5
|
||||||
@ -490,134 +388,6 @@ class OVS_Lib_Test(base.BaseTestCase):
|
|||||||
self.assertEqual(0, sleep.call_count)
|
self.assertEqual(0, sleep.call_count)
|
||||||
self.assertEqual(1, self.execute.call_count)
|
self.assertEqual(1, self.execute.call_count)
|
||||||
|
|
||||||
def test_add_tunnel_port(self):
|
|
||||||
pname = "tap99"
|
|
||||||
local_ip = "1.1.1.1"
|
|
||||||
remote_ip = "9.9.9.9"
|
|
||||||
ofport = 6
|
|
||||||
command = ["--may-exist", "add-port",
|
|
||||||
self.BR_NAME, pname]
|
|
||||||
command.extend(["--", "set", "Interface", pname])
|
|
||||||
command.extend(["type=gre", "options:df_default=true",
|
|
||||||
"options:remote_ip=" + remote_ip,
|
|
||||||
"options:local_ip=" + local_ip,
|
|
||||||
"options:in_key=flow",
|
|
||||||
"options:out_key=flow"])
|
|
||||||
# Each element is a tuple of (expected mock call, return_value)
|
|
||||||
expected_calls_and_values = [
|
|
||||||
(self._vsctl_mock(*command), None),
|
|
||||||
(self._vsctl_mock("--columns=ofport", "list", "Interface", pname),
|
|
||||||
self._encode_ovs_json(['ofport'], [[ofport]])),
|
|
||||||
]
|
|
||||||
tools.setup_mock_calls(self.execute, expected_calls_and_values)
|
|
||||||
|
|
||||||
self.assertEqual(
|
|
||||||
self.br.add_tunnel_port(pname, remote_ip, local_ip),
|
|
||||||
ofport)
|
|
||||||
|
|
||||||
tools.verify_mock_calls(self.execute, expected_calls_and_values)
|
|
||||||
|
|
||||||
def test_add_vxlan_fragmented_tunnel_port(self):
|
|
||||||
pname = "tap99"
|
|
||||||
local_ip = "1.1.1.1"
|
|
||||||
remote_ip = "9.9.9.9"
|
|
||||||
ofport = 6
|
|
||||||
vxlan_udp_port = "9999"
|
|
||||||
dont_fragment = False
|
|
||||||
command = ["--may-exist", "add-port", self.BR_NAME, pname]
|
|
||||||
command.extend(["--", "set", "Interface", pname])
|
|
||||||
command.extend(["type=" + constants.TYPE_VXLAN,
|
|
||||||
"options:dst_port=" + vxlan_udp_port,
|
|
||||||
"options:df_default=false",
|
|
||||||
"options:remote_ip=" + remote_ip,
|
|
||||||
"options:local_ip=" + local_ip,
|
|
||||||
"options:in_key=flow",
|
|
||||||
"options:out_key=flow"])
|
|
||||||
# Each element is a tuple of (expected mock call, return_value)
|
|
||||||
expected_calls_and_values = [
|
|
||||||
(self._vsctl_mock(*command), None),
|
|
||||||
(self._vsctl_mock("--columns=ofport", "list", "Interface", pname),
|
|
||||||
self._encode_ovs_json(['ofport'], [[ofport]])),
|
|
||||||
]
|
|
||||||
tools.setup_mock_calls(self.execute, expected_calls_and_values)
|
|
||||||
|
|
||||||
self.assertEqual(
|
|
||||||
self.br.add_tunnel_port(pname, remote_ip, local_ip,
|
|
||||||
constants.TYPE_VXLAN, vxlan_udp_port,
|
|
||||||
dont_fragment),
|
|
||||||
ofport)
|
|
||||||
|
|
||||||
tools.verify_mock_calls(self.execute, expected_calls_and_values)
|
|
||||||
|
|
||||||
def test_add_vxlan_csum_tunnel_port(self):
|
|
||||||
pname = "tap99"
|
|
||||||
local_ip = "1.1.1.1"
|
|
||||||
remote_ip = "9.9.9.9"
|
|
||||||
ofport = 6
|
|
||||||
vxlan_udp_port = "9999"
|
|
||||||
dont_fragment = True
|
|
||||||
tunnel_csum = True
|
|
||||||
command = ["--may-exist", "add-port", self.BR_NAME, pname]
|
|
||||||
command.extend(["--", "set", "Interface", pname])
|
|
||||||
command.extend(["type=" + constants.TYPE_VXLAN,
|
|
||||||
"options:dst_port=" + vxlan_udp_port,
|
|
||||||
"options:df_default=true",
|
|
||||||
"options:remote_ip=" + remote_ip,
|
|
||||||
"options:local_ip=" + local_ip,
|
|
||||||
"options:in_key=flow",
|
|
||||||
"options:out_key=flow",
|
|
||||||
"options:csum=true"])
|
|
||||||
# Each element is a tuple of (expected mock call, return_value)
|
|
||||||
expected_calls_and_values = [
|
|
||||||
(self._vsctl_mock(*command), None),
|
|
||||||
(self._vsctl_mock("--columns=ofport", "list", "Interface", pname),
|
|
||||||
self._encode_ovs_json(['ofport'], [[ofport]])),
|
|
||||||
]
|
|
||||||
tools.setup_mock_calls(self.execute, expected_calls_and_values)
|
|
||||||
|
|
||||||
self.assertEqual(
|
|
||||||
self.br.add_tunnel_port(pname, remote_ip, local_ip,
|
|
||||||
constants.TYPE_VXLAN, vxlan_udp_port,
|
|
||||||
dont_fragment, tunnel_csum),
|
|
||||||
ofport)
|
|
||||||
|
|
||||||
tools.verify_mock_calls(self.execute, expected_calls_and_values)
|
|
||||||
|
|
||||||
def test_add_vxlan_tos_tunnel_port(self):
|
|
||||||
pname = "tap99"
|
|
||||||
local_ip = "1.1.1.1"
|
|
||||||
remote_ip = "9.9.9.9"
|
|
||||||
ofport = 6
|
|
||||||
vxlan_udp_port = "9999"
|
|
||||||
dont_fragment = True
|
|
||||||
tunnel_csum = False
|
|
||||||
tos = 8
|
|
||||||
command = ["--may-exist", "add-port", self.BR_NAME, pname]
|
|
||||||
command.extend(["--", "set", "Interface", pname])
|
|
||||||
command.extend(["type=" + constants.TYPE_VXLAN,
|
|
||||||
"options:dst_port=" + vxlan_udp_port,
|
|
||||||
"options:df_default=true",
|
|
||||||
"options:remote_ip=" + remote_ip,
|
|
||||||
"options:local_ip=" + local_ip,
|
|
||||||
"options:in_key=flow",
|
|
||||||
"options:out_key=flow",
|
|
||||||
"options:tos=" + str(tos)])
|
|
||||||
# Each element is a tuple of (expected mock call, return_value)
|
|
||||||
expected_calls_and_values = [
|
|
||||||
(self._vsctl_mock(*command), None),
|
|
||||||
(self._vsctl_mock("--columns=ofport", "list", "Interface", pname),
|
|
||||||
self._encode_ovs_json(['ofport'], [[ofport]])),
|
|
||||||
]
|
|
||||||
tools.setup_mock_calls(self.execute, expected_calls_and_values)
|
|
||||||
|
|
||||||
self.assertEqual(
|
|
||||||
self.br.add_tunnel_port(pname, remote_ip, local_ip,
|
|
||||||
constants.TYPE_VXLAN, vxlan_udp_port,
|
|
||||||
dont_fragment, tunnel_csum, tos),
|
|
||||||
ofport)
|
|
||||||
|
|
||||||
tools.verify_mock_calls(self.execute, expected_calls_and_values)
|
|
||||||
|
|
||||||
def _encode_ovs_json(self, headings, data):
|
def _encode_ovs_json(self, headings, data):
|
||||||
# See man ovs-vsctl(8) for the encoding details.
|
# See man ovs-vsctl(8) for the encoding details.
|
||||||
r = {"data": [],
|
r = {"data": [],
|
||||||
@ -637,12 +407,6 @@ class OVS_Lib_Test(base.BaseTestCase):
|
|||||||
type(cell))
|
type(cell))
|
||||||
return jsonutils.dumps(r)
|
return jsonutils.dumps(r)
|
||||||
|
|
||||||
def test_get_vif_port_to_ofport_map(self):
|
|
||||||
self.execute.return_value = OVSLIST_WITH_UNSET_PORT
|
|
||||||
results = self.br.get_vif_port_to_ofport_map()
|
|
||||||
expected = {'2ab72a72-4407-4ef3-806a-b2172f3e4dc7': 2, 'patch-tun': 1}
|
|
||||||
self.assertEqual(expected, results)
|
|
||||||
|
|
||||||
def test_get_vif_ports(self):
|
def test_get_vif_ports(self):
|
||||||
pname = "tap99"
|
pname = "tap99"
|
||||||
ofport = 6
|
ofport = 6
|
||||||
@ -665,120 +429,6 @@ class OVS_Lib_Test(base.BaseTestCase):
|
|||||||
columns=['name', 'external_ids', 'ofport'],
|
columns=['name', 'external_ids', 'ofport'],
|
||||||
if_exists=True)
|
if_exists=True)
|
||||||
|
|
||||||
def test_get_vif_port_set(self):
|
|
||||||
id_key = 'iface-id'
|
|
||||||
headings = ['name', 'external_ids', 'ofport']
|
|
||||||
data = [
|
|
||||||
# A vif port on this bridge:
|
|
||||||
['tap99', {id_key: 'tap99id', 'attached-mac': 'tap99mac'}, 1],
|
|
||||||
# A vif port on this bridge not yet configured
|
|
||||||
['tap98', {id_key: 'tap98id', 'attached-mac': 'tap98mac'}, []],
|
|
||||||
# Another vif port on this bridge not yet configured
|
|
||||||
['tap97', {id_key: 'tap97id', 'attached-mac': 'tap97mac'},
|
|
||||||
['set', []]],
|
|
||||||
|
|
||||||
# Non-vif port on this bridge:
|
|
||||||
['bogus', {}, 2],
|
|
||||||
]
|
|
||||||
|
|
||||||
# Each element is a tuple of (expected mock call, return_value)
|
|
||||||
expected_calls_and_values = [
|
|
||||||
(self._vsctl_mock("list-ports", self.BR_NAME), 'tap99\\ntun22'),
|
|
||||||
(self._vsctl_mock("--if-exists",
|
|
||||||
"--columns=name,external_ids,ofport",
|
|
||||||
"list", "Interface", 'tap99', 'tun22'),
|
|
||||||
self._encode_ovs_json(headings, data)),
|
|
||||||
]
|
|
||||||
tools.setup_mock_calls(self.execute, expected_calls_and_values)
|
|
||||||
|
|
||||||
port_set = self.br.get_vif_port_set()
|
|
||||||
self.assertEqual(set(['tap99id']), port_set)
|
|
||||||
tools.verify_mock_calls(self.execute, expected_calls_and_values)
|
|
||||||
|
|
||||||
def test_get_vif_ports_list_ports_error(self):
|
|
||||||
expected_calls_and_values = [
|
|
||||||
(self._vsctl_mock("list-ports", self.BR_NAME), RuntimeError()),
|
|
||||||
]
|
|
||||||
tools.setup_mock_calls(self.execute, expected_calls_and_values)
|
|
||||||
self.assertRaises(RuntimeError, self.br.get_vif_ports)
|
|
||||||
tools.verify_mock_calls(self.execute, expected_calls_and_values)
|
|
||||||
|
|
||||||
def test_get_vif_port_set_list_ports_error(self):
|
|
||||||
expected_calls_and_values = [
|
|
||||||
(self._vsctl_mock("list-ports", self.BR_NAME), RuntimeError()),
|
|
||||||
]
|
|
||||||
tools.setup_mock_calls(self.execute, expected_calls_and_values)
|
|
||||||
self.assertRaises(RuntimeError, self.br.get_vif_port_set)
|
|
||||||
tools.verify_mock_calls(self.execute, expected_calls_and_values)
|
|
||||||
|
|
||||||
def test_get_vif_port_set_list_interface_error(self):
|
|
||||||
expected_calls_and_values = [
|
|
||||||
(self._vsctl_mock("list-ports", self.BR_NAME), 'tap99\n'),
|
|
||||||
(self._vsctl_mock("--if-exists",
|
|
||||||
"--columns=name,external_ids,ofport",
|
|
||||||
"list", "Interface", "tap99"), RuntimeError()),
|
|
||||||
]
|
|
||||||
tools.setup_mock_calls(self.execute, expected_calls_and_values)
|
|
||||||
self.assertRaises(RuntimeError, self.br.get_vif_port_set)
|
|
||||||
tools.verify_mock_calls(self.execute, expected_calls_and_values)
|
|
||||||
|
|
||||||
def test_get_port_tag_dict(self):
|
|
||||||
headings = ['name', 'tag']
|
|
||||||
data = [
|
|
||||||
['int-br-eth2', set()],
|
|
||||||
['patch-tun', set()],
|
|
||||||
['qr-76d9e6b6-21', 1],
|
|
||||||
['tapce5318ff-78', 1],
|
|
||||||
['tape1400310-e6', 1],
|
|
||||||
]
|
|
||||||
|
|
||||||
# Each element is a tuple of (expected mock call, return_value)
|
|
||||||
expected_calls_and_values = [
|
|
||||||
(self._vsctl_mock("list-ports", self.BR_NAME),
|
|
||||||
'\\n'.join((iface for iface, tag in data))),
|
|
||||||
(self._vsctl_mock("--columns=name,tag", "list", "Port"),
|
|
||||||
self._encode_ovs_json(headings, data)),
|
|
||||||
]
|
|
||||||
tools.setup_mock_calls(self.execute, expected_calls_and_values)
|
|
||||||
|
|
||||||
port_tags = self.br.get_port_tag_dict()
|
|
||||||
self.assertEqual(
|
|
||||||
port_tags,
|
|
||||||
{u'int-br-eth2': [],
|
|
||||||
u'patch-tun': [],
|
|
||||||
u'qr-76d9e6b6-21': 1,
|
|
||||||
u'tapce5318ff-78': 1,
|
|
||||||
u'tape1400310-e6': 1}
|
|
||||||
)
|
|
||||||
|
|
||||||
def test_clear_db_attribute(self):
|
|
||||||
pname = "tap77"
|
|
||||||
self.br.clear_db_attribute("Port", pname, "tag")
|
|
||||||
self._verify_vsctl_mock("clear", "Port", pname, "tag")
|
|
||||||
|
|
||||||
def _test_iface_to_br(self, exp_timeout=None):
|
|
||||||
iface = 'tap0'
|
|
||||||
br = 'br-int'
|
|
||||||
if exp_timeout:
|
|
||||||
self.br.vsctl_timeout = exp_timeout
|
|
||||||
self.execute.return_value = 'br-int'
|
|
||||||
self.assertEqual(self.br.get_bridge_for_iface(iface), br)
|
|
||||||
self._verify_vsctl_mock("iface-to-br", iface)
|
|
||||||
|
|
||||||
def test_iface_to_br(self):
|
|
||||||
self._test_iface_to_br()
|
|
||||||
|
|
||||||
def test_iface_to_br_non_default_timeout(self):
|
|
||||||
new_timeout = 5
|
|
||||||
self._test_iface_to_br(new_timeout)
|
|
||||||
|
|
||||||
def test_iface_to_br_handles_ovs_vsctl_exception(self):
|
|
||||||
iface = 'tap0'
|
|
||||||
self.execute.side_effect = Exception
|
|
||||||
|
|
||||||
self.assertIsNone(self.br.get_bridge_for_iface(iface))
|
|
||||||
self._verify_vsctl_mock("iface-to-br", iface)
|
|
||||||
|
|
||||||
def test_delete_all_ports(self):
|
def test_delete_all_ports(self):
|
||||||
with mock.patch.object(self.br, 'get_port_name_list',
|
with mock.patch.object(self.br, 'get_port_name_list',
|
||||||
return_value=['port1']) as get_port:
|
return_value=['port1']) as get_port:
|
||||||
@ -802,21 +452,6 @@ class OVS_Lib_Test(base.BaseTestCase):
|
|||||||
mock.call('tap5678')
|
mock.call('tap5678')
|
||||||
])
|
])
|
||||||
|
|
||||||
def test_delete_neutron_ports_list_error(self):
|
|
||||||
expected_calls_and_values = [
|
|
||||||
(self._vsctl_mock("list-ports", self.BR_NAME), RuntimeError()),
|
|
||||||
]
|
|
||||||
tools.setup_mock_calls(self.execute, expected_calls_and_values)
|
|
||||||
self.assertRaises(RuntimeError, self.br.delete_ports, all_ports=False)
|
|
||||||
tools.verify_mock_calls(self.execute, expected_calls_and_values)
|
|
||||||
|
|
||||||
def test_get_bridges_not_default_timeout(self):
|
|
||||||
bridges = ['br-int', 'br-ex']
|
|
||||||
self.br.vsctl_timeout = 5
|
|
||||||
self.execute.return_value = 'br-int\\nbr-ex\n'
|
|
||||||
self.assertEqual(self.br.get_bridges(), bridges)
|
|
||||||
self._verify_vsctl_mock("list-br")
|
|
||||||
|
|
||||||
def test_get_local_port_mac_succeeds(self):
|
def test_get_local_port_mac_succeeds(self):
|
||||||
with mock.patch('neutron.agent.linux.ip_lib.IpLinkCommand',
|
with mock.patch('neutron.agent.linux.ip_lib.IpLinkCommand',
|
||||||
return_value=mock.Mock(address='foo')):
|
return_value=mock.Mock(address='foo')):
|
||||||
@ -875,88 +510,6 @@ class OVS_Lib_Test(base.BaseTestCase):
|
|||||||
[mock.call('Interface', columns=['name', 'external_ids', 'ofport'],
|
[mock.call('Interface', columns=['name', 'external_ids', 'ofport'],
|
||||||
if_exists=True)])
|
if_exists=True)])
|
||||||
|
|
||||||
def _test_get_vif_port_by_id(self, iface_id, data, br_name=None,
|
|
||||||
extra_calls_and_values=None):
|
|
||||||
headings = ['external_ids', 'name', 'ofport']
|
|
||||||
|
|
||||||
# Each element is a tuple of (expected mock call, return_value)
|
|
||||||
expected_calls_and_values = [
|
|
||||||
(self._vsctl_mock("--columns=external_ids,name,ofport", "find",
|
|
||||||
"Interface",
|
|
||||||
'external_ids:iface-id=%s' % iface_id,
|
|
||||||
'external_ids:attached-mac!=""'),
|
|
||||||
self._encode_ovs_json(headings, data))]
|
|
||||||
if data:
|
|
||||||
if not br_name:
|
|
||||||
br_name = self.BR_NAME
|
|
||||||
|
|
||||||
# Only the last information list in 'data' is used, so if more
|
|
||||||
# than one vif is described in data, the rest must be declared
|
|
||||||
# in the argument 'expected_calls_and_values'.
|
|
||||||
if extra_calls_and_values:
|
|
||||||
expected_calls_and_values.extend(extra_calls_and_values)
|
|
||||||
|
|
||||||
expected_calls_and_values.append(
|
|
||||||
(self._vsctl_mock("iface-to-br",
|
|
||||||
data[-1][headings.index('name')]), br_name))
|
|
||||||
tools.setup_mock_calls(self.execute, expected_calls_and_values)
|
|
||||||
vif_port = self.br.get_vif_port_by_id(iface_id)
|
|
||||||
|
|
||||||
tools.verify_mock_calls(self.execute, expected_calls_and_values)
|
|
||||||
return vif_port
|
|
||||||
|
|
||||||
def _assert_vif_port(self, vif_port, ofport=None, mac=None):
|
|
||||||
if not ofport or ofport == -1 or not mac:
|
|
||||||
self.assertIsNone(vif_port, "Got %s" % vif_port)
|
|
||||||
return
|
|
||||||
self.assertEqual('tap99id', vif_port.vif_id)
|
|
||||||
self.assertEqual(mac, vif_port.vif_mac)
|
|
||||||
self.assertEqual('tap99', vif_port.port_name)
|
|
||||||
self.assertEqual(ofport, vif_port.ofport)
|
|
||||||
|
|
||||||
def _test_get_vif_port_by_id_with_data(self, ofport=None, mac=None):
|
|
||||||
external_ids = [["iface-id", "tap99id"],
|
|
||||||
["iface-status", "active"],
|
|
||||||
["attached-mac", mac]]
|
|
||||||
data = [[["map", external_ids], "tap99",
|
|
||||||
ofport if ofport else ["set", []]]]
|
|
||||||
vif_port = self._test_get_vif_port_by_id('tap99id', data)
|
|
||||||
self._assert_vif_port(vif_port, ofport, mac)
|
|
||||||
|
|
||||||
def test_get_vif_by_port_id_with_ofport(self):
|
|
||||||
self._test_get_vif_port_by_id_with_data(
|
|
||||||
ofport=1, mac="aa:bb:cc:dd:ee:ff")
|
|
||||||
|
|
||||||
def test_get_vif_by_port_id_without_ofport(self):
|
|
||||||
self._test_get_vif_port_by_id_with_data(mac="aa:bb:cc:dd:ee:ff")
|
|
||||||
|
|
||||||
def test_get_vif_by_port_id_with_invalid_ofport(self):
|
|
||||||
self._test_get_vif_port_by_id_with_data(
|
|
||||||
ofport=-1, mac="aa:bb:cc:dd:ee:ff")
|
|
||||||
|
|
||||||
def test_get_vif_by_port_id_with_no_data(self):
|
|
||||||
self.assertIsNone(self._test_get_vif_port_by_id('whatever', []))
|
|
||||||
|
|
||||||
def test_get_vif_by_port_id_different_bridge(self):
|
|
||||||
external_ids = [["iface-id", "tap99id"],
|
|
||||||
["iface-status", "active"]]
|
|
||||||
data = [[["map", external_ids], "tap99", 1]]
|
|
||||||
self.assertIsNone(self._test_get_vif_port_by_id('tap99id', data,
|
|
||||||
"br-ext"))
|
|
||||||
|
|
||||||
def test_get_vif_by_port_id_multiple_vifs(self):
|
|
||||||
external_ids = [["iface-id", "tap99id"],
|
|
||||||
["iface-status", "active"],
|
|
||||||
["attached-mac", "de:ad:be:ef:13:37"]]
|
|
||||||
data = [[["map", external_ids], "dummytap", 1],
|
|
||||||
[["map", external_ids], "tap99", 1337]]
|
|
||||||
extra_calls_and_values = [
|
|
||||||
(self._vsctl_mock("iface-to-br", "dummytap"), "br-ext")]
|
|
||||||
|
|
||||||
vif_port = self._test_get_vif_port_by_id(
|
|
||||||
'tap99id', data, extra_calls_and_values=extra_calls_and_values)
|
|
||||||
self._assert_vif_port(vif_port, ofport=1337, mac="de:ad:be:ef:13:37")
|
|
||||||
|
|
||||||
def test_get_port_ofport_retry(self):
|
def test_get_port_ofport_retry(self):
|
||||||
with mock.patch.object(
|
with mock.patch.object(
|
||||||
self.br, 'db_get_val',
|
self.br, 'db_get_val',
|
||||||
@ -965,7 +518,7 @@ class OVS_Lib_Test(base.BaseTestCase):
|
|||||||
|
|
||||||
def test_get_port_ofport_retry_fails(self):
|
def test_get_port_ofport_retry_fails(self):
|
||||||
# reduce timeout for faster execution
|
# reduce timeout for faster execution
|
||||||
self.br.vsctl_timeout = 1
|
self.br.ovsdb_timeout = 1
|
||||||
# after 7 calls the retry will timeout and raise
|
# after 7 calls the retry will timeout and raise
|
||||||
with mock.patch.object(
|
with mock.patch.object(
|
||||||
self.br, 'db_get_val',
|
self.br, 'db_get_val',
|
||||||
@ -985,7 +538,7 @@ class OVS_Lib_Test(base.BaseTestCase):
|
|||||||
|
|
||||||
def test_get_port_external_ids_retry_fails(self):
|
def test_get_port_external_ids_retry_fails(self):
|
||||||
# reduce timeout for faster execution
|
# reduce timeout for faster execution
|
||||||
self.br.vsctl_timeout = 1
|
self.br.ovsdb_timeout = 1
|
||||||
# after 7 calls the retry will timeout and raise
|
# after 7 calls the retry will timeout and raise
|
||||||
with mock.patch.object(
|
with mock.patch.object(
|
||||||
self.br, 'db_get_val',
|
self.br, 'db_get_val',
|
||||||
@ -1141,35 +694,3 @@ class TestDeferredOVSBridge(base.BaseTestCase):
|
|||||||
def test_getattr_unallowed_attr_failure(self):
|
def test_getattr_unallowed_attr_failure(self):
|
||||||
with ovs_lib.DeferredOVSBridge(self.br) as deferred_br:
|
with ovs_lib.DeferredOVSBridge(self.br) as deferred_br:
|
||||||
self.assertRaises(AttributeError, getattr, deferred_br, 'failure')
|
self.assertRaises(AttributeError, getattr, deferred_br, 'failure')
|
||||||
|
|
||||||
@vsctl_only
|
|
||||||
def test_default_cookie(self):
|
|
||||||
self.br = ovs_lib.OVSBridge("br-tun")
|
|
||||||
uuid_stamp1 = self.br.default_cookie
|
|
||||||
self.assertEqual(uuid_stamp1, self.br.default_cookie)
|
|
||||||
|
|
||||||
@vsctl_only
|
|
||||||
def test_cookie_passed_to_addmod(self):
|
|
||||||
self.br = ovs_lib.OVSBridge("br-tun")
|
|
||||||
stamp = str(self.br.default_cookie)
|
|
||||||
expected_calls = [
|
|
||||||
mock.call('add-flows', ['-'],
|
|
||||||
'hard_timeout=0,idle_timeout=0,priority=1,'
|
|
||||||
'cookie=' + stamp + ',actions=drop'),
|
|
||||||
mock.call('mod-flows', ['-'],
|
|
||||||
'cookie=' + stamp + ',actions=drop')
|
|
||||||
]
|
|
||||||
with mock.patch.object(self.br, 'run_ofctl') as f:
|
|
||||||
with ovs_lib.DeferredOVSBridge(self.br) as deferred_br:
|
|
||||||
deferred_br.add_flow(actions='drop')
|
|
||||||
deferred_br.mod_flow(actions='drop')
|
|
||||||
f.assert_has_calls(expected_calls)
|
|
||||||
|
|
||||||
@vsctl_only
|
|
||||||
def test_add_flow_with_bundle(self):
|
|
||||||
br = ovs_lib.OVSBridge("foo")
|
|
||||||
deferred = br.deferred(use_bundle=True)
|
|
||||||
with mock.patch.object(utils, "execute", spec=utils.execute) as mexec:
|
|
||||||
deferred.add_flow(in_port=1, actions='drop')
|
|
||||||
deferred.apply_flows()
|
|
||||||
self.assertIn('--bundle', mexec.call_args[0][0])
|
|
||||||
|
@ -734,7 +734,7 @@ class TestCookieContext(base.BaseTestCase):
|
|||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(TestCookieContext, self).setUp()
|
super(TestCookieContext, self).setUp()
|
||||||
# Don't attempt to connect to ovsdb
|
# Don't attempt to connect to ovsdb
|
||||||
mock.patch('neutron.agent.ovsdb.api.from_config').start()
|
mock.patch('neutron.agent.ovsdb.impl_idl.api_factory').start()
|
||||||
# Don't trigger iptables -> ovsfw migration
|
# Don't trigger iptables -> ovsfw migration
|
||||||
mock.patch(
|
mock.patch(
|
||||||
'neutron.agent.linux.openvswitch_firewall.iptables.Helper').start()
|
'neutron.agent.linux.openvswitch_firewall.iptables.Helper').start()
|
||||||
|
@ -13,47 +13,27 @@
|
|||||||
# License for the specific language governing permissions and limitations
|
# License for the specific language governing permissions and limitations
|
||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
import itertools
|
|
||||||
|
|
||||||
import mock
|
import mock
|
||||||
from oslo_utils import uuidutils
|
|
||||||
|
|
||||||
from neutron.agent.common import ovs_lib
|
|
||||||
from neutron.agent.linux import ip_lib
|
|
||||||
from neutron.cmd import ovs_cleanup as util
|
from neutron.cmd import ovs_cleanup as util
|
||||||
from neutron.tests import base
|
from neutron.tests import base
|
||||||
|
|
||||||
|
|
||||||
class TestOVSCleanup(base.BaseTestCase):
|
class TestOVSCleanup(base.BaseTestCase):
|
||||||
|
|
||||||
def test_collect_neutron_ports(self):
|
def test_clean_ovs_bridges(self):
|
||||||
port1 = ovs_lib.VifPort('tap1234', 1, uuidutils.generate_uuid(),
|
conf = mock.Mock()
|
||||||
'11:22:33:44:55:66', 'br')
|
conf.ovs_all_ports = True
|
||||||
port2 = ovs_lib.VifPort('tap5678', 2, uuidutils.generate_uuid(),
|
conf.ovs_integration_bridge = 'br-int'
|
||||||
'77:88:99:aa:bb:cc', 'br')
|
conf.external_network_bridge = 'br-ex'
|
||||||
port3 = ovs_lib.VifPort('tap90ab', 3, uuidutils.generate_uuid(),
|
bridges = [conf.ovs_integration_bridge, conf.external_network_bridge]
|
||||||
'99:00:aa:bb:cc:dd', 'br')
|
with mock.patch('neutron.agent.common.ovs_lib.BaseOVS') as ovs_cls:
|
||||||
ports = [[port1, port2], [port3]]
|
ovs_base = mock.Mock()
|
||||||
portnames = [p.port_name for p in itertools.chain(*ports)]
|
ovs_base.get_bridges.return_value = bridges
|
||||||
with mock.patch('neutron.agent.common.ovs_lib.OVSBridge') as ovs:
|
ovs_cls.return_value = ovs_base
|
||||||
ovs.return_value.get_vif_ports.side_effect = ports
|
|
||||||
bridges = ['br-int', 'br-ex']
|
|
||||||
ret = util.collect_neutron_ports(bridges)
|
|
||||||
self.assertEqual(ret, portnames)
|
|
||||||
|
|
||||||
@mock.patch.object(ip_lib, 'IPDevice')
|
util.do_main(conf)
|
||||||
def test_delete_neutron_ports(self, mock_ip):
|
ovs_base.ovsdb.ovs_cleanup.assert_has_calls(
|
||||||
ports = ['tap1234', 'tap5678', 'tap09ab']
|
[mock.call(conf.ovs_integration_bridge, True),
|
||||||
port_found = [True, False, True]
|
mock.call(conf.external_network_bridge, True)],
|
||||||
|
any_order=True)
|
||||||
mock_ip.return_value.exists.side_effect = port_found
|
|
||||||
util.delete_neutron_ports(ports)
|
|
||||||
mock_ip.assert_has_calls(
|
|
||||||
[mock.call('tap1234'),
|
|
||||||
mock.call().exists(),
|
|
||||||
mock.call().link.delete(),
|
|
||||||
mock.call('tap5678'),
|
|
||||||
mock.call().exists(),
|
|
||||||
mock.call('tap09ab'),
|
|
||||||
mock.call().exists(),
|
|
||||||
mock.call().link.delete()])
|
|
||||||
|
@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
upgrade:
|
||||||
|
- |
|
||||||
|
The deprecated ``ovsdb_interface`` configuration option has been removed,
|
||||||
|
the default ``native`` driver is now always used. In addition, the
|
||||||
|
deprecated ``ovs_vsctl_timeout`` option, which was renamed to
|
||||||
|
``ovsdb_timeout`` in Queens, has also been removed.
|
Loading…
Reference in New Issue
Block a user