Moves ovs_lib to agent/common
This patch moves ovs_lib from agent/linux to agent/common since it will be used by ovs_neutron_agent on both Linux and Windows platforms. To provide compatibility with out of tree code, a placeholder has been left in agent/linux. Unit tests are updated accordingly. Partially implements blueprint: hyper-v-ovs-agent Change-Id: I009f7f5e3b014633541ed5a45628aa1b2287e22b
This commit is contained in:
parent
9d5d545fd5
commit
1a089e6059
524
neutron/agent/common/ovs_lib.py
Normal file
524
neutron/agent/common/ovs_lib.py
Normal file
@ -0,0 +1,524 @@
|
|||||||
|
# Copyright 2011 VMware, Inc.
|
||||||
|
# All Rights Reserved.
|
||||||
|
#
|
||||||
|
# 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 operator
|
||||||
|
|
||||||
|
from oslo_config import cfg
|
||||||
|
from oslo_log import log as logging
|
||||||
|
from oslo_utils import excutils
|
||||||
|
import retrying
|
||||||
|
import six
|
||||||
|
|
||||||
|
from neutron.agent.linux import ip_lib
|
||||||
|
from neutron.agent.linux import utils
|
||||||
|
from neutron.agent.ovsdb import api as ovsdb
|
||||||
|
from neutron.common import exceptions
|
||||||
|
from neutron.i18n import _LE, _LI, _LW
|
||||||
|
from neutron.plugins.common import constants
|
||||||
|
|
||||||
|
# Default timeout for ovs-vsctl command
|
||||||
|
DEFAULT_OVS_VSCTL_TIMEOUT = 10
|
||||||
|
|
||||||
|
# Special return value for an invalid OVS ofport
|
||||||
|
INVALID_OFPORT = -1
|
||||||
|
UNASSIGNED_OFPORT = []
|
||||||
|
|
||||||
|
# OVS bridge fail modes
|
||||||
|
FAILMODE_SECURE = 'secure'
|
||||||
|
|
||||||
|
OPTS = [
|
||||||
|
cfg.IntOpt('ovs_vsctl_timeout',
|
||||||
|
default=DEFAULT_OVS_VSCTL_TIMEOUT,
|
||||||
|
help=_('Timeout in seconds for ovs-vsctl commands')),
|
||||||
|
]
|
||||||
|
cfg.CONF.register_opts(OPTS)
|
||||||
|
|
||||||
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def _ofport_result_pending(result):
|
||||||
|
"""Return True if ovs-vsctl indicates the result is still pending."""
|
||||||
|
# ovs-vsctl can return '[]' for an ofport that has not yet been assigned
|
||||||
|
try:
|
||||||
|
int(result)
|
||||||
|
return False
|
||||||
|
except (ValueError, TypeError):
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def _ofport_retry(fn):
|
||||||
|
"""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
|
||||||
|
on the fact that instance methods receive self as the first argument.
|
||||||
|
"""
|
||||||
|
@six.wraps(fn)
|
||||||
|
def wrapped(*args, **kwargs):
|
||||||
|
self = args[0]
|
||||||
|
new_fn = retrying.retry(
|
||||||
|
retry_on_result=_ofport_result_pending,
|
||||||
|
stop_max_delay=self.vsctl_timeout * 1000,
|
||||||
|
wait_exponential_multiplier=10,
|
||||||
|
wait_exponential_max=1000,
|
||||||
|
retry_on_exception=lambda _: False)(fn)
|
||||||
|
return new_fn(*args, **kwargs)
|
||||||
|
return wrapped
|
||||||
|
|
||||||
|
|
||||||
|
class VifPort(object):
|
||||||
|
def __init__(self, port_name, ofport, vif_id, vif_mac, switch):
|
||||||
|
self.port_name = port_name
|
||||||
|
self.ofport = ofport
|
||||||
|
self.vif_id = vif_id
|
||||||
|
self.vif_mac = vif_mac
|
||||||
|
self.switch = switch
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return ("iface-id=" + self.vif_id + ", vif_mac=" +
|
||||||
|
self.vif_mac + ", port_name=" + self.port_name +
|
||||||
|
", ofport=" + str(self.ofport) + ", bridge_name=" +
|
||||||
|
self.switch.br_name)
|
||||||
|
|
||||||
|
|
||||||
|
class BaseOVS(object):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.vsctl_timeout = cfg.CONF.ovs_vsctl_timeout
|
||||||
|
self.ovsdb = ovsdb.API.get(self)
|
||||||
|
|
||||||
|
def add_bridge(self, bridge_name):
|
||||||
|
self.ovsdb.add_br(bridge_name).execute()
|
||||||
|
br = OVSBridge(bridge_name)
|
||||||
|
# Don't return until vswitchd sets up the internal port
|
||||||
|
br.get_port_ofport(bridge_name)
|
||||||
|
return br
|
||||||
|
|
||||||
|
def delete_bridge(self, bridge_name):
|
||||||
|
self.ovsdb.del_br(bridge_name).execute()
|
||||||
|
|
||||||
|
def bridge_exists(self, bridge_name):
|
||||||
|
return self.ovsdb.br_exists(bridge_name).execute()
|
||||||
|
|
||||||
|
def port_exists(self, port_name):
|
||||||
|
cmd = self.ovsdb.db_get('Port', port_name, 'name')
|
||||||
|
return bool(cmd.execute(check_error=False, log_errors=False))
|
||||||
|
|
||||||
|
def get_bridge_for_iface(self, iface):
|
||||||
|
return self.ovsdb.iface_to_br(iface).execute()
|
||||||
|
|
||||||
|
def get_bridges(self):
|
||||||
|
return self.ovsdb.list_br().execute(check_error=True)
|
||||||
|
|
||||||
|
def get_bridge_external_bridge_id(self, bridge):
|
||||||
|
return self.ovsdb.br_get_external_id(bridge, 'bridge-id').execute()
|
||||||
|
|
||||||
|
def set_db_attribute(self, table_name, record, column, value,
|
||||||
|
check_error=False):
|
||||||
|
self.ovsdb.db_set(table_name, record, (column, value)).execute(
|
||||||
|
check_error=check_error)
|
||||||
|
|
||||||
|
def clear_db_attribute(self, table_name, record, column):
|
||||||
|
self.ovsdb.db_clear(table_name, record, column).execute()
|
||||||
|
|
||||||
|
def db_get_val(self, table, record, column, check_error=False):
|
||||||
|
return self.ovsdb.db_get(table, record, column).execute(
|
||||||
|
check_error=check_error)
|
||||||
|
|
||||||
|
|
||||||
|
class OVSBridge(BaseOVS):
|
||||||
|
def __init__(self, br_name):
|
||||||
|
super(OVSBridge, self).__init__()
|
||||||
|
self.br_name = br_name
|
||||||
|
|
||||||
|
def set_controller(self, controllers):
|
||||||
|
self.ovsdb.set_controller(self.br_name,
|
||||||
|
controllers).execute(check_error=True)
|
||||||
|
|
||||||
|
def del_controller(self):
|
||||||
|
self.ovsdb.del_controller(self.br_name).execute(check_error=True)
|
||||||
|
|
||||||
|
def get_controller(self):
|
||||||
|
return self.ovsdb.get_controller(self.br_name).execute(
|
||||||
|
check_error=True)
|
||||||
|
|
||||||
|
def set_secure_mode(self):
|
||||||
|
self.ovsdb.set_fail_mode(self.br_name, FAILMODE_SECURE).execute(
|
||||||
|
check_error=True)
|
||||||
|
|
||||||
|
def set_protocols(self, protocols):
|
||||||
|
self.set_db_attribute('Bridge', self.br_name, 'protocols', protocols,
|
||||||
|
check_error=True)
|
||||||
|
|
||||||
|
def create(self):
|
||||||
|
self.ovsdb.add_br(self.br_name).execute()
|
||||||
|
# Don't return until vswitchd sets up the internal port
|
||||||
|
self.get_port_ofport(self.br_name)
|
||||||
|
|
||||||
|
def destroy(self):
|
||||||
|
self.delete_bridge(self.br_name)
|
||||||
|
|
||||||
|
def reset_bridge(self, secure_mode=False):
|
||||||
|
with self.ovsdb.transaction() as txn:
|
||||||
|
txn.add(self.ovsdb.del_br(self.br_name))
|
||||||
|
txn.add(self.ovsdb.add_br(self.br_name))
|
||||||
|
if secure_mode:
|
||||||
|
txn.add(self.ovsdb.set_fail_mode(self.br_name,
|
||||||
|
FAILMODE_SECURE))
|
||||||
|
|
||||||
|
def add_port(self, port_name, *interface_attr_tuples):
|
||||||
|
with self.ovsdb.transaction() as txn:
|
||||||
|
txn.add(self.ovsdb.add_port(self.br_name, port_name))
|
||||||
|
if interface_attr_tuples:
|
||||||
|
txn.add(self.ovsdb.db_set('Interface', port_name,
|
||||||
|
*interface_attr_tuples))
|
||||||
|
return self.get_port_ofport(port_name)
|
||||||
|
|
||||||
|
def replace_port(self, port_name, *interface_attr_tuples):
|
||||||
|
"""Replace existing port or create it, and configure port interface."""
|
||||||
|
with self.ovsdb.transaction() as txn:
|
||||||
|
txn.add(self.ovsdb.del_port(port_name))
|
||||||
|
txn.add(self.ovsdb.add_port(self.br_name, port_name,
|
||||||
|
may_exist=False))
|
||||||
|
if interface_attr_tuples:
|
||||||
|
txn.add(self.ovsdb.db_set('Interface', port_name,
|
||||||
|
*interface_attr_tuples))
|
||||||
|
# Don't return until the port has been assigned by vswitchd
|
||||||
|
self.get_port_ofport(port_name)
|
||||||
|
|
||||||
|
def delete_port(self, port_name):
|
||||||
|
self.ovsdb.del_port(port_name, self.br_name).execute()
|
||||||
|
|
||||||
|
def run_ofctl(self, cmd, args, process_input=None):
|
||||||
|
full_args = ["ovs-ofctl", cmd, self.br_name] + args
|
||||||
|
try:
|
||||||
|
return utils.execute(full_args, run_as_root=True,
|
||||||
|
process_input=process_input)
|
||||||
|
except Exception as e:
|
||||||
|
LOG.error(_LE("Unable to execute %(cmd)s. Exception: "
|
||||||
|
"%(exception)s"),
|
||||||
|
{'cmd': full_args, 'exception': e})
|
||||||
|
|
||||||
|
def count_flows(self):
|
||||||
|
flow_list = self.run_ofctl("dump-flows", []).split("\n")[1:]
|
||||||
|
return len(flow_list) - 1
|
||||||
|
|
||||||
|
def remove_all_flows(self):
|
||||||
|
self.run_ofctl("del-flows", [])
|
||||||
|
|
||||||
|
@_ofport_retry
|
||||||
|
def _get_port_ofport(self, port_name):
|
||||||
|
return self.db_get_val("Interface", port_name, "ofport")
|
||||||
|
|
||||||
|
def get_port_ofport(self, port_name):
|
||||||
|
"""Get the port's assigned ofport, retrying if not yet assigned."""
|
||||||
|
ofport = INVALID_OFPORT
|
||||||
|
try:
|
||||||
|
ofport = self._get_port_ofport(port_name)
|
||||||
|
except retrying.RetryError as e:
|
||||||
|
LOG.exception(_LE("Timed out retrieving ofport on port %(pname)s. "
|
||||||
|
"Exception: %(exception)s"),
|
||||||
|
{'pname': port_name, 'exception': e})
|
||||||
|
return ofport
|
||||||
|
|
||||||
|
def get_datapath_id(self):
|
||||||
|
return self.db_get_val('Bridge',
|
||||||
|
self.br_name, 'datapath_id')
|
||||||
|
|
||||||
|
def do_action_flows(self, action, kwargs_list):
|
||||||
|
flow_strs = [_build_flow_expr_str(kw, action) for kw in kwargs_list]
|
||||||
|
self.run_ofctl('%s-flows' % action, ['-'], '\n'.join(flow_strs))
|
||||||
|
|
||||||
|
def add_flow(self, **kwargs):
|
||||||
|
self.do_action_flows('add', [kwargs])
|
||||||
|
|
||||||
|
def mod_flow(self, **kwargs):
|
||||||
|
self.do_action_flows('mod', [kwargs])
|
||||||
|
|
||||||
|
def delete_flows(self, **kwargs):
|
||||||
|
self.do_action_flows('del', [kwargs])
|
||||||
|
|
||||||
|
def dump_flows_for_table(self, table):
|
||||||
|
retval = None
|
||||||
|
flow_str = "table=%s" % table
|
||||||
|
flows = self.run_ofctl("dump-flows", [flow_str])
|
||||||
|
if flows:
|
||||||
|
retval = '\n'.join(item for item in flows.splitlines()
|
||||||
|
if 'NXST' not in item)
|
||||||
|
return retval
|
||||||
|
|
||||||
|
def deferred(self, **kwargs):
|
||||||
|
return DeferredOVSBridge(self, **kwargs)
|
||||||
|
|
||||||
|
def add_tunnel_port(self, port_name, remote_ip, local_ip,
|
||||||
|
tunnel_type=constants.TYPE_GRE,
|
||||||
|
vxlan_udp_port=constants.VXLAN_UDP_PORT,
|
||||||
|
dont_fragment=True):
|
||||||
|
attrs = [('type', tunnel_type)]
|
||||||
|
# TODO(twilson) This is an OrderedDict solely to make a test happy
|
||||||
|
options = collections.OrderedDict()
|
||||||
|
vxlan_uses_custom_udp_port = (
|
||||||
|
tunnel_type == constants.TYPE_VXLAN and
|
||||||
|
vxlan_udp_port != constants.VXLAN_UDP_PORT
|
||||||
|
)
|
||||||
|
if vxlan_uses_custom_udp_port:
|
||||||
|
options['dst_port'] = vxlan_udp_port
|
||||||
|
options['df_default'] = str(dont_fragment).lower()
|
||||||
|
options['remote_ip'] = remote_ip
|
||||||
|
options['local_ip'] = local_ip
|
||||||
|
options['in_key'] = 'flow'
|
||||||
|
options['out_key'] = 'flow'
|
||||||
|
attrs.append(('options', options))
|
||||||
|
|
||||||
|
return self.add_port(port_name, *attrs)
|
||||||
|
|
||||||
|
def add_patch_port(self, local_name, remote_name):
|
||||||
|
attrs = [('type', 'patch'),
|
||||||
|
('options', {'peer': remote_name})]
|
||||||
|
return self.add_port(local_name, *attrs)
|
||||||
|
|
||||||
|
def get_port_name_list(self):
|
||||||
|
return self.ovsdb.list_ports(self.br_name).execute(check_error=True)
|
||||||
|
|
||||||
|
def get_port_stats(self, port_name):
|
||||||
|
return self.db_get_val("Interface", port_name, "statistics")
|
||||||
|
|
||||||
|
def get_xapi_iface_id(self, xs_vif_uuid):
|
||||||
|
args = ["xe", "vif-param-get", "param-name=other-config",
|
||||||
|
"param-key=nicira-iface-id", "uuid=%s" % xs_vif_uuid]
|
||||||
|
try:
|
||||||
|
return utils.execute(args, run_as_root=True).strip()
|
||||||
|
except Exception as e:
|
||||||
|
with excutils.save_and_reraise_exception():
|
||||||
|
LOG.error(_LE("Unable to execute %(cmd)s. "
|
||||||
|
"Exception: %(exception)s"),
|
||||||
|
{'cmd': args, 'exception': e})
|
||||||
|
|
||||||
|
# returns a VIF object for each VIF port
|
||||||
|
def get_vif_ports(self):
|
||||||
|
edge_ports = []
|
||||||
|
port_names = self.get_port_name_list()
|
||||||
|
for name in port_names:
|
||||||
|
external_ids = self.db_get_val("Interface", name, "external_ids",
|
||||||
|
check_error=True)
|
||||||
|
ofport = self.db_get_val("Interface", name, "ofport",
|
||||||
|
check_error=True)
|
||||||
|
if "iface-id" in external_ids and "attached-mac" in external_ids:
|
||||||
|
p = VifPort(name, ofport, external_ids["iface-id"],
|
||||||
|
external_ids["attached-mac"], self)
|
||||||
|
edge_ports.append(p)
|
||||||
|
elif ("xs-vif-uuid" in external_ids and
|
||||||
|
"attached-mac" in external_ids):
|
||||||
|
# if this is a xenserver and iface-id is not automatically
|
||||||
|
# synced to OVS from XAPI, we grab it from XAPI directly
|
||||||
|
iface_id = self.get_xapi_iface_id(external_ids["xs-vif-uuid"])
|
||||||
|
p = VifPort(name, ofport, iface_id,
|
||||||
|
external_ids["attached-mac"], self)
|
||||||
|
edge_ports.append(p)
|
||||||
|
|
||||||
|
return edge_ports
|
||||||
|
|
||||||
|
def get_vif_port_set(self):
|
||||||
|
edge_ports = set()
|
||||||
|
port_names = self.get_port_name_list()
|
||||||
|
cmd = self.ovsdb.db_list(
|
||||||
|
'Interface', port_names,
|
||||||
|
columns=['name', 'external_ids', 'ofport'], if_exists=True)
|
||||||
|
results = cmd.execute(check_error=True)
|
||||||
|
for result in results:
|
||||||
|
if result['ofport'] == UNASSIGNED_OFPORT:
|
||||||
|
LOG.warn(_LW("Found not yet ready openvswitch port: %s"),
|
||||||
|
result['name'])
|
||||||
|
elif result['ofport'] == INVALID_OFPORT:
|
||||||
|
LOG.warn(_LW("Found failed openvswitch port: %s"),
|
||||||
|
result['name'])
|
||||||
|
elif 'attached-mac' in result['external_ids']:
|
||||||
|
external_ids = result['external_ids']
|
||||||
|
if 'iface-id' in external_ids:
|
||||||
|
edge_ports.add(external_ids['iface-id'])
|
||||||
|
elif 'xs-vif-uuid' in external_ids:
|
||||||
|
iface_id = self.get_xapi_iface_id(
|
||||||
|
external_ids['xs-vif-uuid'])
|
||||||
|
edge_ports.add(iface_id)
|
||||||
|
return edge_ports
|
||||||
|
|
||||||
|
def get_port_tag_dict(self):
|
||||||
|
"""Get a dict of port names and associated vlan tags.
|
||||||
|
|
||||||
|
e.g. the returned dict is of the following form::
|
||||||
|
|
||||||
|
{u'int-br-eth2': [],
|
||||||
|
u'patch-tun': [],
|
||||||
|
u'qr-76d9e6b6-21': 1,
|
||||||
|
u'tapce5318ff-78': 1,
|
||||||
|
u'tape1400310-e6': 1}
|
||||||
|
|
||||||
|
The TAG ID is only available in the "Port" table and is not available
|
||||||
|
in the "Interface" table queried by the get_vif_port_set() method.
|
||||||
|
|
||||||
|
"""
|
||||||
|
port_names = self.get_port_name_list()
|
||||||
|
cmd = self.ovsdb.db_list('Port', port_names, columns=['name', 'tag'])
|
||||||
|
results = cmd.execute(check_error=True)
|
||||||
|
return {p['name']: p['tag'] for p in results}
|
||||||
|
|
||||||
|
def get_vif_port_by_id(self, port_id):
|
||||||
|
ports = self.ovsdb.db_find(
|
||||||
|
'Interface', ('external_ids', '=', {'iface-id': port_id}),
|
||||||
|
('external_ids', '!=', {'attached-mac': ''}),
|
||||||
|
columns=['external_ids', 'name', 'ofport']).execute()
|
||||||
|
for port in ports:
|
||||||
|
if self.br_name != self.get_bridge_for_iface(port['name']):
|
||||||
|
continue
|
||||||
|
if port['ofport'] in [UNASSIGNED_OFPORT, INVALID_OFPORT]:
|
||||||
|
LOG.warn(_LW("ofport: %(ofport)s for VIF: %(vif)s is not a"
|
||||||
|
" positive integer"),
|
||||||
|
{'ofport': port['ofport'], 'vif': port_id})
|
||||||
|
continue
|
||||||
|
mac = port['external_ids'].get('attached-mac')
|
||||||
|
return VifPort(port['name'], port['ofport'], port_id, mac, self)
|
||||||
|
LOG.info(_LI("Port %(port_id)s not present in bridge %(br_name)s"),
|
||||||
|
{'port_id': port_id, 'br_name': self.br_name})
|
||||||
|
|
||||||
|
def delete_ports(self, all_ports=False):
|
||||||
|
if all_ports:
|
||||||
|
port_names = self.get_port_name_list()
|
||||||
|
else:
|
||||||
|
port_names = (port.port_name for port in self.get_vif_ports())
|
||||||
|
|
||||||
|
for port_name in port_names:
|
||||||
|
self.delete_port(port_name)
|
||||||
|
|
||||||
|
def get_local_port_mac(self):
|
||||||
|
"""Retrieve the mac of the bridge's local port."""
|
||||||
|
address = ip_lib.IPDevice(self.br_name).link.address
|
||||||
|
if address:
|
||||||
|
return address
|
||||||
|
else:
|
||||||
|
msg = _('Unable to determine mac address for %s') % self.br_name
|
||||||
|
raise Exception(msg)
|
||||||
|
|
||||||
|
def __enter__(self):
|
||||||
|
self.create()
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __exit__(self, exc_type, exc_value, exc_tb):
|
||||||
|
self.destroy()
|
||||||
|
|
||||||
|
|
||||||
|
class DeferredOVSBridge(object):
|
||||||
|
'''Deferred OVSBridge.
|
||||||
|
|
||||||
|
This class wraps add_flow, mod_flow and delete_flows calls to an OVSBridge
|
||||||
|
and defers their application until apply_flows call in order to perform
|
||||||
|
bulk calls. It wraps also ALLOWED_PASSTHROUGHS calls to avoid mixing
|
||||||
|
OVSBridge and DeferredOVSBridge uses.
|
||||||
|
This class can be used as a context, in such case apply_flows is called on
|
||||||
|
__exit__ except if an exception is raised.
|
||||||
|
This class is not thread-safe, that's why for every use a new instance
|
||||||
|
must be implemented.
|
||||||
|
'''
|
||||||
|
ALLOWED_PASSTHROUGHS = 'add_port', 'add_tunnel_port', 'delete_port'
|
||||||
|
|
||||||
|
def __init__(self, br, full_ordered=False,
|
||||||
|
order=('add', 'mod', 'del')):
|
||||||
|
'''Constructor.
|
||||||
|
|
||||||
|
:param br: wrapped bridge
|
||||||
|
:param full_ordered: Optional, disable flow reordering (slower)
|
||||||
|
:param order: Optional, define in which order flow are applied
|
||||||
|
'''
|
||||||
|
|
||||||
|
self.br = br
|
||||||
|
self.full_ordered = full_ordered
|
||||||
|
self.order = order
|
||||||
|
if not self.full_ordered:
|
||||||
|
self.weights = dict((y, x) for x, y in enumerate(self.order))
|
||||||
|
self.action_flow_tuples = []
|
||||||
|
|
||||||
|
def __getattr__(self, name):
|
||||||
|
if name in self.ALLOWED_PASSTHROUGHS:
|
||||||
|
return getattr(self.br, name)
|
||||||
|
raise AttributeError(name)
|
||||||
|
|
||||||
|
def add_flow(self, **kwargs):
|
||||||
|
self.action_flow_tuples.append(('add', kwargs))
|
||||||
|
|
||||||
|
def mod_flow(self, **kwargs):
|
||||||
|
self.action_flow_tuples.append(('mod', kwargs))
|
||||||
|
|
||||||
|
def delete_flows(self, **kwargs):
|
||||||
|
self.action_flow_tuples.append(('del', kwargs))
|
||||||
|
|
||||||
|
def apply_flows(self):
|
||||||
|
action_flow_tuples = self.action_flow_tuples
|
||||||
|
self.action_flow_tuples = []
|
||||||
|
if not action_flow_tuples:
|
||||||
|
return
|
||||||
|
|
||||||
|
if not self.full_ordered:
|
||||||
|
action_flow_tuples.sort(key=lambda af: self.weights[af[0]])
|
||||||
|
|
||||||
|
grouped = itertools.groupby(action_flow_tuples,
|
||||||
|
key=operator.itemgetter(0))
|
||||||
|
itemgetter_1 = operator.itemgetter(1)
|
||||||
|
for action, action_flow_list in grouped:
|
||||||
|
flows = map(itemgetter_1, action_flow_list)
|
||||||
|
self.br.do_action_flows(action, flows)
|
||||||
|
|
||||||
|
def __enter__(self):
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __exit__(self, exc_type, exc_value, traceback):
|
||||||
|
if exc_type is None:
|
||||||
|
self.apply_flows()
|
||||||
|
else:
|
||||||
|
LOG.exception(_LE("OVS flows could not be applied on bridge %s"),
|
||||||
|
self.br.br_name)
|
||||||
|
|
||||||
|
|
||||||
|
def _build_flow_expr_str(flow_dict, cmd):
|
||||||
|
flow_expr_arr = []
|
||||||
|
actions = None
|
||||||
|
|
||||||
|
if cmd == 'add':
|
||||||
|
flow_expr_arr.append("hard_timeout=%s" %
|
||||||
|
flow_dict.pop('hard_timeout', '0'))
|
||||||
|
flow_expr_arr.append("idle_timeout=%s" %
|
||||||
|
flow_dict.pop('idle_timeout', '0'))
|
||||||
|
flow_expr_arr.append("priority=%s" %
|
||||||
|
flow_dict.pop('priority', '1'))
|
||||||
|
elif 'priority' in flow_dict:
|
||||||
|
msg = _("Cannot match priority on flow deletion or modification")
|
||||||
|
raise exceptions.InvalidInput(error_message=msg)
|
||||||
|
|
||||||
|
if cmd != 'del':
|
||||||
|
if "actions" not in flow_dict:
|
||||||
|
msg = _("Must specify one or more actions on flow addition"
|
||||||
|
" or modification")
|
||||||
|
raise exceptions.InvalidInput(error_message=msg)
|
||||||
|
actions = "actions=%s" % flow_dict.pop('actions')
|
||||||
|
|
||||||
|
for key, value in flow_dict.iteritems():
|
||||||
|
if key == 'proto':
|
||||||
|
flow_expr_arr.append(value)
|
||||||
|
else:
|
||||||
|
flow_expr_arr.append("%s=%s" % (key, str(value)))
|
||||||
|
|
||||||
|
if actions:
|
||||||
|
flow_expr_arr.append(actions)
|
||||||
|
|
||||||
|
return ','.join(flow_expr_arr)
|
@ -21,8 +21,8 @@ from oslo_log import log as logging
|
|||||||
from oslo_utils import importutils
|
from oslo_utils import importutils
|
||||||
import six
|
import six
|
||||||
|
|
||||||
|
from neutron.agent.common import ovs_lib
|
||||||
from neutron.agent.linux import ip_lib
|
from neutron.agent.linux import ip_lib
|
||||||
from neutron.agent.linux import ovs_lib
|
|
||||||
from neutron.agent.linux import utils
|
from neutron.agent.linux import utils
|
||||||
from neutron.common import constants as n_const
|
from neutron.common import constants as n_const
|
||||||
from neutron.common import exceptions
|
from neutron.common import exceptions
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# Copyright 2011 VMware, Inc.
|
# Copyright 2015 Cloudbase Solutions.
|
||||||
# All Rights Reserved.
|
# All Rights Reserved.
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||||
@ -13,512 +13,14 @@
|
|||||||
# 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 collections
|
# This is a placeholder so that the vendor code that import the ovs_lib
|
||||||
import itertools
|
# module from agent/linux doesn't fail
|
||||||
import operator
|
# TODO(atuvenie) remove this module after opening the liberty cycle
|
||||||
|
|
||||||
from oslo_config import cfg
|
from neutron.agent.common import ovs_lib
|
||||||
from oslo_log import log as logging
|
|
||||||
from oslo_utils import excutils
|
|
||||||
import retrying
|
|
||||||
import six
|
|
||||||
|
|
||||||
from neutron.agent.linux import ip_lib
|
INVALID_OFPORT = ovs_lib.INVALID_OFPORT
|
||||||
from neutron.agent.linux import utils
|
BaseOVS = ovs_lib.BaseOVS
|
||||||
from neutron.agent.ovsdb import api as ovsdb
|
OVSBridge = ovs_lib.OVSBridge
|
||||||
from neutron.common import exceptions
|
VifPort = ovs_lib.VifPort
|
||||||
from neutron.i18n import _LE, _LI, _LW
|
_build_flow_expr_str = ovs_lib._build_flow_expr_str
|
||||||
from neutron.plugins.common import constants
|
|
||||||
|
|
||||||
# Default timeout for ovs-vsctl command
|
|
||||||
DEFAULT_OVS_VSCTL_TIMEOUT = 10
|
|
||||||
|
|
||||||
# Special return value for an invalid OVS ofport
|
|
||||||
INVALID_OFPORT = -1
|
|
||||||
UNASSIGNED_OFPORT = []
|
|
||||||
|
|
||||||
# OVS bridge fail modes
|
|
||||||
FAILMODE_SECURE = 'secure'
|
|
||||||
|
|
||||||
OPTS = [
|
|
||||||
cfg.IntOpt('ovs_vsctl_timeout',
|
|
||||||
default=DEFAULT_OVS_VSCTL_TIMEOUT,
|
|
||||||
help=_('Timeout in seconds for ovs-vsctl commands')),
|
|
||||||
]
|
|
||||||
cfg.CONF.register_opts(OPTS)
|
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
def _ofport_result_pending(result):
|
|
||||||
"""Return True if ovs-vsctl indicates the result is still pending."""
|
|
||||||
# ovs-vsctl can return '[]' for an ofport that has not yet been assigned
|
|
||||||
try:
|
|
||||||
int(result)
|
|
||||||
return False
|
|
||||||
except (ValueError, TypeError):
|
|
||||||
return True
|
|
||||||
|
|
||||||
|
|
||||||
def _ofport_retry(fn):
|
|
||||||
"""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
|
|
||||||
on the fact that instance methods receive self as the first argument.
|
|
||||||
"""
|
|
||||||
@six.wraps(fn)
|
|
||||||
def wrapped(*args, **kwargs):
|
|
||||||
self = args[0]
|
|
||||||
new_fn = retrying.retry(
|
|
||||||
retry_on_result=_ofport_result_pending,
|
|
||||||
stop_max_delay=self.vsctl_timeout * 1000,
|
|
||||||
wait_exponential_multiplier=10,
|
|
||||||
wait_exponential_max=1000,
|
|
||||||
retry_on_exception=lambda _: False)(fn)
|
|
||||||
return new_fn(*args, **kwargs)
|
|
||||||
return wrapped
|
|
||||||
|
|
||||||
|
|
||||||
class VifPort(object):
|
|
||||||
def __init__(self, port_name, ofport, vif_id, vif_mac, switch):
|
|
||||||
self.port_name = port_name
|
|
||||||
self.ofport = ofport
|
|
||||||
self.vif_id = vif_id
|
|
||||||
self.vif_mac = vif_mac
|
|
||||||
self.switch = switch
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return ("iface-id=" + self.vif_id + ", vif_mac=" +
|
|
||||||
self.vif_mac + ", port_name=" + self.port_name +
|
|
||||||
", ofport=" + str(self.ofport) + ", bridge_name=" +
|
|
||||||
self.switch.br_name)
|
|
||||||
|
|
||||||
|
|
||||||
class BaseOVS(object):
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
self.vsctl_timeout = cfg.CONF.ovs_vsctl_timeout
|
|
||||||
self.ovsdb = ovsdb.API.get(self)
|
|
||||||
|
|
||||||
def add_bridge(self, bridge_name):
|
|
||||||
self.ovsdb.add_br(bridge_name).execute()
|
|
||||||
br = OVSBridge(bridge_name)
|
|
||||||
# Don't return until vswitchd sets up the internal port
|
|
||||||
br.get_port_ofport(bridge_name)
|
|
||||||
return br
|
|
||||||
|
|
||||||
def delete_bridge(self, bridge_name):
|
|
||||||
self.ovsdb.del_br(bridge_name).execute()
|
|
||||||
|
|
||||||
def bridge_exists(self, bridge_name):
|
|
||||||
return self.ovsdb.br_exists(bridge_name).execute()
|
|
||||||
|
|
||||||
def port_exists(self, port_name):
|
|
||||||
cmd = self.ovsdb.db_get('Port', port_name, 'name')
|
|
||||||
return bool(cmd.execute(check_error=False, log_errors=False))
|
|
||||||
|
|
||||||
def get_bridge_for_iface(self, iface):
|
|
||||||
return self.ovsdb.iface_to_br(iface).execute()
|
|
||||||
|
|
||||||
def get_bridges(self):
|
|
||||||
return self.ovsdb.list_br().execute(check_error=True)
|
|
||||||
|
|
||||||
def get_bridge_external_bridge_id(self, bridge):
|
|
||||||
return self.ovsdb.br_get_external_id(bridge, 'bridge-id').execute()
|
|
||||||
|
|
||||||
def set_db_attribute(self, table_name, record, column, value,
|
|
||||||
check_error=False):
|
|
||||||
self.ovsdb.db_set(table_name, record, (column, value)).execute(
|
|
||||||
check_error=check_error)
|
|
||||||
|
|
||||||
def clear_db_attribute(self, table_name, record, column):
|
|
||||||
self.ovsdb.db_clear(table_name, record, column).execute()
|
|
||||||
|
|
||||||
def db_get_val(self, table, record, column, check_error=False):
|
|
||||||
return self.ovsdb.db_get(table, record, column).execute(
|
|
||||||
check_error=check_error)
|
|
||||||
|
|
||||||
|
|
||||||
class OVSBridge(BaseOVS):
|
|
||||||
def __init__(self, br_name):
|
|
||||||
super(OVSBridge, self).__init__()
|
|
||||||
self.br_name = br_name
|
|
||||||
|
|
||||||
def set_controller(self, controllers):
|
|
||||||
self.ovsdb.set_controller(self.br_name,
|
|
||||||
controllers).execute(check_error=True)
|
|
||||||
|
|
||||||
def del_controller(self):
|
|
||||||
self.ovsdb.del_controller(self.br_name).execute(check_error=True)
|
|
||||||
|
|
||||||
def get_controller(self):
|
|
||||||
return self.ovsdb.get_controller(self.br_name).execute(
|
|
||||||
check_error=True)
|
|
||||||
|
|
||||||
def set_secure_mode(self):
|
|
||||||
self.ovsdb.set_fail_mode(self.br_name, FAILMODE_SECURE).execute(
|
|
||||||
check_error=True)
|
|
||||||
|
|
||||||
def set_protocols(self, protocols):
|
|
||||||
self.set_db_attribute('Bridge', self.br_name, 'protocols', protocols,
|
|
||||||
check_error=True)
|
|
||||||
|
|
||||||
def create(self):
|
|
||||||
self.ovsdb.add_br(self.br_name).execute()
|
|
||||||
# Don't return until vswitchd sets up the internal port
|
|
||||||
self.get_port_ofport(self.br_name)
|
|
||||||
|
|
||||||
def destroy(self):
|
|
||||||
self.delete_bridge(self.br_name)
|
|
||||||
|
|
||||||
def reset_bridge(self, secure_mode=False):
|
|
||||||
with self.ovsdb.transaction() as txn:
|
|
||||||
txn.add(self.ovsdb.del_br(self.br_name))
|
|
||||||
txn.add(self.ovsdb.add_br(self.br_name))
|
|
||||||
if secure_mode:
|
|
||||||
txn.add(self.ovsdb.set_fail_mode(self.br_name,
|
|
||||||
FAILMODE_SECURE))
|
|
||||||
|
|
||||||
def add_port(self, port_name, *interface_attr_tuples):
|
|
||||||
with self.ovsdb.transaction() as txn:
|
|
||||||
txn.add(self.ovsdb.add_port(self.br_name, port_name))
|
|
||||||
if interface_attr_tuples:
|
|
||||||
txn.add(self.ovsdb.db_set('Interface', port_name,
|
|
||||||
*interface_attr_tuples))
|
|
||||||
return self.get_port_ofport(port_name)
|
|
||||||
|
|
||||||
def replace_port(self, port_name, *interface_attr_tuples):
|
|
||||||
"""Replace existing port or create it, and configure port interface."""
|
|
||||||
with self.ovsdb.transaction() as txn:
|
|
||||||
txn.add(self.ovsdb.del_port(port_name))
|
|
||||||
txn.add(self.ovsdb.add_port(self.br_name, port_name,
|
|
||||||
may_exist=False))
|
|
||||||
if interface_attr_tuples:
|
|
||||||
txn.add(self.ovsdb.db_set('Interface', port_name,
|
|
||||||
*interface_attr_tuples))
|
|
||||||
# Don't return until the port has been assigned by vswitchd
|
|
||||||
self.get_port_ofport(port_name)
|
|
||||||
|
|
||||||
def delete_port(self, port_name):
|
|
||||||
self.ovsdb.del_port(port_name, self.br_name).execute()
|
|
||||||
|
|
||||||
def run_ofctl(self, cmd, args, process_input=None):
|
|
||||||
full_args = ["ovs-ofctl", cmd, self.br_name] + args
|
|
||||||
try:
|
|
||||||
return utils.execute(full_args, run_as_root=True,
|
|
||||||
process_input=process_input)
|
|
||||||
except Exception as e:
|
|
||||||
LOG.error(_LE("Unable to execute %(cmd)s. Exception: "
|
|
||||||
"%(exception)s"),
|
|
||||||
{'cmd': full_args, 'exception': e})
|
|
||||||
|
|
||||||
def count_flows(self):
|
|
||||||
flow_list = self.run_ofctl("dump-flows", []).split("\n")[1:]
|
|
||||||
return len(flow_list) - 1
|
|
||||||
|
|
||||||
def remove_all_flows(self):
|
|
||||||
self.run_ofctl("del-flows", [])
|
|
||||||
|
|
||||||
@_ofport_retry
|
|
||||||
def _get_port_ofport(self, port_name):
|
|
||||||
return self.db_get_val("Interface", port_name, "ofport")
|
|
||||||
|
|
||||||
def get_port_ofport(self, port_name):
|
|
||||||
"""Get the port's assigned ofport, retrying if not yet assigned."""
|
|
||||||
ofport = INVALID_OFPORT
|
|
||||||
try:
|
|
||||||
ofport = self._get_port_ofport(port_name)
|
|
||||||
except retrying.RetryError as e:
|
|
||||||
LOG.exception(_LE("Timed out retrieving ofport on port %(pname)s. "
|
|
||||||
"Exception: %(exception)s"),
|
|
||||||
{'pname': port_name, 'exception': e})
|
|
||||||
return ofport
|
|
||||||
|
|
||||||
def get_datapath_id(self):
|
|
||||||
return self.db_get_val('Bridge',
|
|
||||||
self.br_name, 'datapath_id')
|
|
||||||
|
|
||||||
def do_action_flows(self, action, kwargs_list):
|
|
||||||
flow_strs = [_build_flow_expr_str(kw, action) for kw in kwargs_list]
|
|
||||||
self.run_ofctl('%s-flows' % action, ['-'], '\n'.join(flow_strs))
|
|
||||||
|
|
||||||
def add_flow(self, **kwargs):
|
|
||||||
self.do_action_flows('add', [kwargs])
|
|
||||||
|
|
||||||
def mod_flow(self, **kwargs):
|
|
||||||
self.do_action_flows('mod', [kwargs])
|
|
||||||
|
|
||||||
def delete_flows(self, **kwargs):
|
|
||||||
self.do_action_flows('del', [kwargs])
|
|
||||||
|
|
||||||
def dump_flows_for_table(self, table):
|
|
||||||
retval = None
|
|
||||||
flow_str = "table=%s" % table
|
|
||||||
flows = self.run_ofctl("dump-flows", [flow_str])
|
|
||||||
if flows:
|
|
||||||
retval = '\n'.join(item for item in flows.splitlines()
|
|
||||||
if 'NXST' not in item)
|
|
||||||
return retval
|
|
||||||
|
|
||||||
def deferred(self, **kwargs):
|
|
||||||
return DeferredOVSBridge(self, **kwargs)
|
|
||||||
|
|
||||||
def add_tunnel_port(self, port_name, remote_ip, local_ip,
|
|
||||||
tunnel_type=constants.TYPE_GRE,
|
|
||||||
vxlan_udp_port=constants.VXLAN_UDP_PORT,
|
|
||||||
dont_fragment=True):
|
|
||||||
attrs = [('type', tunnel_type)]
|
|
||||||
# TODO(twilson) This is an OrderedDict solely to make a test happy
|
|
||||||
options = collections.OrderedDict()
|
|
||||||
vxlan_uses_custom_udp_port = (
|
|
||||||
tunnel_type == constants.TYPE_VXLAN and
|
|
||||||
vxlan_udp_port != constants.VXLAN_UDP_PORT
|
|
||||||
)
|
|
||||||
if vxlan_uses_custom_udp_port:
|
|
||||||
options['dst_port'] = vxlan_udp_port
|
|
||||||
options['df_default'] = str(dont_fragment).lower()
|
|
||||||
options['remote_ip'] = remote_ip
|
|
||||||
options['local_ip'] = local_ip
|
|
||||||
options['in_key'] = 'flow'
|
|
||||||
options['out_key'] = 'flow'
|
|
||||||
attrs.append(('options', options))
|
|
||||||
|
|
||||||
return self.add_port(port_name, *attrs)
|
|
||||||
|
|
||||||
def add_patch_port(self, local_name, remote_name):
|
|
||||||
attrs = [('type', 'patch'),
|
|
||||||
('options', {'peer': remote_name})]
|
|
||||||
return self.add_port(local_name, *attrs)
|
|
||||||
|
|
||||||
def get_port_name_list(self):
|
|
||||||
return self.ovsdb.list_ports(self.br_name).execute(check_error=True)
|
|
||||||
|
|
||||||
def get_port_stats(self, port_name):
|
|
||||||
return self.db_get_val("Interface", port_name, "statistics")
|
|
||||||
|
|
||||||
def get_xapi_iface_id(self, xs_vif_uuid):
|
|
||||||
args = ["xe", "vif-param-get", "param-name=other-config",
|
|
||||||
"param-key=nicira-iface-id", "uuid=%s" % xs_vif_uuid]
|
|
||||||
try:
|
|
||||||
return utils.execute(args, run_as_root=True).strip()
|
|
||||||
except Exception as e:
|
|
||||||
with excutils.save_and_reraise_exception():
|
|
||||||
LOG.error(_LE("Unable to execute %(cmd)s. "
|
|
||||||
"Exception: %(exception)s"),
|
|
||||||
{'cmd': args, 'exception': e})
|
|
||||||
|
|
||||||
# returns a VIF object for each VIF port
|
|
||||||
def get_vif_ports(self):
|
|
||||||
edge_ports = []
|
|
||||||
port_names = self.get_port_name_list()
|
|
||||||
for name in port_names:
|
|
||||||
external_ids = self.db_get_val("Interface", name, "external_ids",
|
|
||||||
check_error=True)
|
|
||||||
ofport = self.db_get_val("Interface", name, "ofport",
|
|
||||||
check_error=True)
|
|
||||||
if "iface-id" in external_ids and "attached-mac" in external_ids:
|
|
||||||
p = VifPort(name, ofport, external_ids["iface-id"],
|
|
||||||
external_ids["attached-mac"], self)
|
|
||||||
edge_ports.append(p)
|
|
||||||
elif ("xs-vif-uuid" in external_ids and
|
|
||||||
"attached-mac" in external_ids):
|
|
||||||
# if this is a xenserver and iface-id is not automatically
|
|
||||||
# synced to OVS from XAPI, we grab it from XAPI directly
|
|
||||||
iface_id = self.get_xapi_iface_id(external_ids["xs-vif-uuid"])
|
|
||||||
p = VifPort(name, ofport, iface_id,
|
|
||||||
external_ids["attached-mac"], self)
|
|
||||||
edge_ports.append(p)
|
|
||||||
|
|
||||||
return edge_ports
|
|
||||||
|
|
||||||
def get_vif_port_set(self):
|
|
||||||
edge_ports = set()
|
|
||||||
port_names = self.get_port_name_list()
|
|
||||||
cmd = self.ovsdb.db_list(
|
|
||||||
'Interface', port_names,
|
|
||||||
columns=['name', 'external_ids', 'ofport'], if_exists=True)
|
|
||||||
results = cmd.execute(check_error=True)
|
|
||||||
for result in results:
|
|
||||||
if result['ofport'] == UNASSIGNED_OFPORT:
|
|
||||||
LOG.warn(_LW("Found not yet ready openvswitch port: %s"),
|
|
||||||
result['name'])
|
|
||||||
elif result['ofport'] == INVALID_OFPORT:
|
|
||||||
LOG.warn(_LW("Found failed openvswitch port: %s"),
|
|
||||||
result['name'])
|
|
||||||
elif 'attached-mac' in result['external_ids']:
|
|
||||||
external_ids = result['external_ids']
|
|
||||||
if 'iface-id' in external_ids:
|
|
||||||
edge_ports.add(external_ids['iface-id'])
|
|
||||||
elif 'xs-vif-uuid' in external_ids:
|
|
||||||
iface_id = self.get_xapi_iface_id(
|
|
||||||
external_ids['xs-vif-uuid'])
|
|
||||||
edge_ports.add(iface_id)
|
|
||||||
return edge_ports
|
|
||||||
|
|
||||||
def get_port_tag_dict(self):
|
|
||||||
"""Get a dict of port names and associated vlan tags.
|
|
||||||
|
|
||||||
e.g. the returned dict is of the following form::
|
|
||||||
|
|
||||||
{u'int-br-eth2': [],
|
|
||||||
u'patch-tun': [],
|
|
||||||
u'qr-76d9e6b6-21': 1,
|
|
||||||
u'tapce5318ff-78': 1,
|
|
||||||
u'tape1400310-e6': 1}
|
|
||||||
|
|
||||||
The TAG ID is only available in the "Port" table and is not available
|
|
||||||
in the "Interface" table queried by the get_vif_port_set() method.
|
|
||||||
|
|
||||||
"""
|
|
||||||
port_names = self.get_port_name_list()
|
|
||||||
cmd = self.ovsdb.db_list('Port', port_names, columns=['name', 'tag'])
|
|
||||||
results = cmd.execute(check_error=True)
|
|
||||||
return {p['name']: p['tag'] for p in results}
|
|
||||||
|
|
||||||
def get_vif_port_by_id(self, port_id):
|
|
||||||
ports = self.ovsdb.db_find(
|
|
||||||
'Interface', ('external_ids', '=', {'iface-id': port_id}),
|
|
||||||
('external_ids', '!=', {'attached-mac': ''}),
|
|
||||||
columns=['external_ids', 'name', 'ofport']).execute()
|
|
||||||
for port in ports:
|
|
||||||
if self.br_name != self.get_bridge_for_iface(port['name']):
|
|
||||||
continue
|
|
||||||
if port['ofport'] in [UNASSIGNED_OFPORT, INVALID_OFPORT]:
|
|
||||||
LOG.warn(_LW("ofport: %(ofport)s for VIF: %(vif)s is not a"
|
|
||||||
" positive integer"),
|
|
||||||
{'ofport': port['ofport'], 'vif': port_id})
|
|
||||||
continue
|
|
||||||
mac = port['external_ids'].get('attached-mac')
|
|
||||||
return VifPort(port['name'], port['ofport'], port_id, mac, self)
|
|
||||||
LOG.info(_LI("Port %(port_id)s not present in bridge %(br_name)s"),
|
|
||||||
{'port_id': port_id, 'br_name': self.br_name})
|
|
||||||
|
|
||||||
def delete_ports(self, all_ports=False):
|
|
||||||
if all_ports:
|
|
||||||
port_names = self.get_port_name_list()
|
|
||||||
else:
|
|
||||||
port_names = (port.port_name for port in self.get_vif_ports())
|
|
||||||
|
|
||||||
for port_name in port_names:
|
|
||||||
self.delete_port(port_name)
|
|
||||||
|
|
||||||
def get_local_port_mac(self):
|
|
||||||
"""Retrieve the mac of the bridge's local port."""
|
|
||||||
address = ip_lib.IPDevice(self.br_name).link.address
|
|
||||||
if address:
|
|
||||||
return address
|
|
||||||
else:
|
|
||||||
msg = _('Unable to determine mac address for %s') % self.br_name
|
|
||||||
raise Exception(msg)
|
|
||||||
|
|
||||||
def __enter__(self):
|
|
||||||
self.create()
|
|
||||||
return self
|
|
||||||
|
|
||||||
def __exit__(self, exc_type, exc_value, exc_tb):
|
|
||||||
self.destroy()
|
|
||||||
|
|
||||||
|
|
||||||
class DeferredOVSBridge(object):
|
|
||||||
'''Deferred OVSBridge.
|
|
||||||
|
|
||||||
This class wraps add_flow, mod_flow and delete_flows calls to an OVSBridge
|
|
||||||
and defers their application until apply_flows call in order to perform
|
|
||||||
bulk calls. It wraps also ALLOWED_PASSTHROUGHS calls to avoid mixing
|
|
||||||
OVSBridge and DeferredOVSBridge uses.
|
|
||||||
This class can be used as a context, in such case apply_flows is called on
|
|
||||||
__exit__ except if an exception is raised.
|
|
||||||
This class is not thread-safe, that's why for every use a new instance
|
|
||||||
must be implemented.
|
|
||||||
'''
|
|
||||||
ALLOWED_PASSTHROUGHS = 'add_port', 'add_tunnel_port', 'delete_port'
|
|
||||||
|
|
||||||
def __init__(self, br, full_ordered=False,
|
|
||||||
order=('add', 'mod', 'del')):
|
|
||||||
'''Constructor.
|
|
||||||
|
|
||||||
:param br: wrapped bridge
|
|
||||||
:param full_ordered: Optional, disable flow reordering (slower)
|
|
||||||
:param order: Optional, define in which order flow are applied
|
|
||||||
'''
|
|
||||||
|
|
||||||
self.br = br
|
|
||||||
self.full_ordered = full_ordered
|
|
||||||
self.order = order
|
|
||||||
if not self.full_ordered:
|
|
||||||
self.weights = dict((y, x) for x, y in enumerate(self.order))
|
|
||||||
self.action_flow_tuples = []
|
|
||||||
|
|
||||||
def __getattr__(self, name):
|
|
||||||
if name in self.ALLOWED_PASSTHROUGHS:
|
|
||||||
return getattr(self.br, name)
|
|
||||||
raise AttributeError(name)
|
|
||||||
|
|
||||||
def add_flow(self, **kwargs):
|
|
||||||
self.action_flow_tuples.append(('add', kwargs))
|
|
||||||
|
|
||||||
def mod_flow(self, **kwargs):
|
|
||||||
self.action_flow_tuples.append(('mod', kwargs))
|
|
||||||
|
|
||||||
def delete_flows(self, **kwargs):
|
|
||||||
self.action_flow_tuples.append(('del', kwargs))
|
|
||||||
|
|
||||||
def apply_flows(self):
|
|
||||||
action_flow_tuples = self.action_flow_tuples
|
|
||||||
self.action_flow_tuples = []
|
|
||||||
if not action_flow_tuples:
|
|
||||||
return
|
|
||||||
|
|
||||||
if not self.full_ordered:
|
|
||||||
action_flow_tuples.sort(key=lambda af: self.weights[af[0]])
|
|
||||||
|
|
||||||
grouped = itertools.groupby(action_flow_tuples,
|
|
||||||
key=operator.itemgetter(0))
|
|
||||||
itemgetter_1 = operator.itemgetter(1)
|
|
||||||
for action, action_flow_list in grouped:
|
|
||||||
flows = map(itemgetter_1, action_flow_list)
|
|
||||||
self.br.do_action_flows(action, flows)
|
|
||||||
|
|
||||||
def __enter__(self):
|
|
||||||
return self
|
|
||||||
|
|
||||||
def __exit__(self, exc_type, exc_value, traceback):
|
|
||||||
if exc_type is None:
|
|
||||||
self.apply_flows()
|
|
||||||
else:
|
|
||||||
LOG.exception(_LE("OVS flows could not be applied on bridge %s"),
|
|
||||||
self.br.br_name)
|
|
||||||
|
|
||||||
|
|
||||||
def _build_flow_expr_str(flow_dict, cmd):
|
|
||||||
flow_expr_arr = []
|
|
||||||
actions = None
|
|
||||||
|
|
||||||
if cmd == 'add':
|
|
||||||
flow_expr_arr.append("hard_timeout=%s" %
|
|
||||||
flow_dict.pop('hard_timeout', '0'))
|
|
||||||
flow_expr_arr.append("idle_timeout=%s" %
|
|
||||||
flow_dict.pop('idle_timeout', '0'))
|
|
||||||
flow_expr_arr.append("priority=%s" %
|
|
||||||
flow_dict.pop('priority', '1'))
|
|
||||||
elif 'priority' in flow_dict:
|
|
||||||
msg = _("Cannot match priority on flow deletion or modification")
|
|
||||||
raise exceptions.InvalidInput(error_message=msg)
|
|
||||||
|
|
||||||
if cmd != 'del':
|
|
||||||
if "actions" not in flow_dict:
|
|
||||||
msg = _("Must specify one or more actions on flow addition"
|
|
||||||
" or modification")
|
|
||||||
raise exceptions.InvalidInput(error_message=msg)
|
|
||||||
actions = "actions=%s" % flow_dict.pop('actions')
|
|
||||||
|
|
||||||
for key, value in flow_dict.iteritems():
|
|
||||||
if key == 'proto':
|
|
||||||
flow_expr_arr.append(value)
|
|
||||||
else:
|
|
||||||
flow_expr_arr.append("%s=%s" % (key, str(value)))
|
|
||||||
|
|
||||||
if actions:
|
|
||||||
flow_expr_arr.append(actions)
|
|
||||||
|
|
||||||
return ','.join(flow_expr_arr)
|
|
||||||
|
@ -34,7 +34,7 @@ OPTS = [
|
|||||||
]
|
]
|
||||||
cfg.CONF.register_opts(OPTS, 'OVS')
|
cfg.CONF.register_opts(OPTS, 'OVS')
|
||||||
# TODO(twilson) DEFAULT.ovs_vsctl_timeout should be OVS.vsctl_timeout
|
# TODO(twilson) DEFAULT.ovs_vsctl_timeout should be OVS.vsctl_timeout
|
||||||
cfg.CONF.import_opt('ovs_vsctl_timeout', 'neutron.agent.linux.ovs_lib')
|
cfg.CONF.import_opt('ovs_vsctl_timeout', 'neutron.agent.common.ovs_lib')
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -21,6 +21,7 @@ from oslo_log import log as logging
|
|||||||
from oslo_utils import importutils
|
from oslo_utils import importutils
|
||||||
|
|
||||||
from neutron.agent.common import config as agent_config
|
from neutron.agent.common import config as agent_config
|
||||||
|
from neutron.agent.common import ovs_lib
|
||||||
from neutron.agent.dhcp import config as dhcp_config
|
from neutron.agent.dhcp import config as dhcp_config
|
||||||
from neutron.agent.l3 import agent as l3_agent
|
from neutron.agent.l3 import agent as l3_agent
|
||||||
from neutron.agent.l3 import dvr
|
from neutron.agent.l3 import dvr
|
||||||
@ -29,7 +30,6 @@ from neutron.agent.linux import dhcp
|
|||||||
from neutron.agent.linux import external_process
|
from neutron.agent.linux import external_process
|
||||||
from neutron.agent.linux import interface
|
from neutron.agent.linux import interface
|
||||||
from neutron.agent.linux import ip_lib
|
from neutron.agent.linux import ip_lib
|
||||||
from neutron.agent.linux import ovs_lib
|
|
||||||
from neutron.api.v2 import attributes
|
from neutron.api.v2 import attributes
|
||||||
from neutron.common import config
|
from neutron.common import config
|
||||||
from neutron.i18n import _LE
|
from neutron.i18n import _LE
|
||||||
|
@ -17,10 +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 config as agent_config
|
from neutron.agent.common import config as agent_config
|
||||||
|
from neutron.agent.common import ovs_lib
|
||||||
from neutron.agent.l3 import config as l3_config
|
from neutron.agent.l3 import config as l3_config
|
||||||
from neutron.agent.linux import interface
|
from neutron.agent.linux import interface
|
||||||
from neutron.agent.linux import ip_lib
|
from neutron.agent.linux import ip_lib
|
||||||
from neutron.agent.linux import ovs_lib
|
|
||||||
from neutron.common import config
|
from neutron.common import config
|
||||||
from neutron.i18n import _LI
|
from neutron.i18n import _LI
|
||||||
|
|
||||||
|
@ -19,9 +19,9 @@ import netaddr
|
|||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
import six
|
import six
|
||||||
|
|
||||||
|
from neutron.agent.common import ovs_lib
|
||||||
from neutron.agent.linux import ip_lib
|
from neutron.agent.linux import ip_lib
|
||||||
from neutron.agent.linux import ip_link_support
|
from neutron.agent.linux import ip_link_support
|
||||||
from neutron.agent.linux import ovs_lib
|
|
||||||
from neutron.agent.linux import utils as agent_utils
|
from neutron.agent.linux import utils as agent_utils
|
||||||
from neutron.common import utils
|
from neutron.common import utils
|
||||||
from neutron.i18n import _LE
|
from neutron.i18n import _LE
|
||||||
|
@ -26,8 +26,8 @@ from oslo_config import cfg
|
|||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
import oslo_messaging
|
import oslo_messaging
|
||||||
|
|
||||||
|
from neutron.agent.common import ovs_lib
|
||||||
from neutron.agent.linux import ip_lib
|
from neutron.agent.linux import ip_lib
|
||||||
from neutron.agent.linux import ovs_lib
|
|
||||||
from neutron.agent import rpc as agent_rpc
|
from neutron.agent import rpc as agent_rpc
|
||||||
from neutron.common import config as common_config
|
from neutron.common import config as common_config
|
||||||
from neutron.common import constants as n_const
|
from neutron.common import constants as n_const
|
||||||
|
@ -24,7 +24,7 @@ eventlet.monkey_patch()
|
|||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
import oslo_messaging
|
import oslo_messaging
|
||||||
|
|
||||||
from neutron.agent.linux import ovs_lib
|
from neutron.agent.common import ovs_lib
|
||||||
from neutron.agent import rpc as agent_rpc
|
from neutron.agent import rpc as agent_rpc
|
||||||
from neutron.agent import securitygroups_rpc as sg_rpc
|
from neutron.agent import securitygroups_rpc as sg_rpc
|
||||||
from neutron.common import config as common_config
|
from neutron.common import config as common_config
|
||||||
|
@ -26,9 +26,9 @@ import oslo_messaging
|
|||||||
from six import moves
|
from six import moves
|
||||||
|
|
||||||
from neutron.agent.common import config
|
from neutron.agent.common import config
|
||||||
|
from neutron.agent.common import ovs_lib
|
||||||
from neutron.agent import l2population_rpc
|
from neutron.agent import l2population_rpc
|
||||||
from neutron.agent.linux import ip_lib
|
from neutron.agent.linux import ip_lib
|
||||||
from neutron.agent.linux import ovs_lib
|
|
||||||
from neutron.agent.linux import polling
|
from neutron.agent.linux import polling
|
||||||
from neutron.agent.linux import utils
|
from neutron.agent.linux import utils
|
||||||
from neutron.agent import rpc as agent_rpc
|
from neutron.agent import rpc as agent_rpc
|
||||||
|
@ -15,9 +15,9 @@
|
|||||||
import netaddr
|
import netaddr
|
||||||
import testscenarios
|
import testscenarios
|
||||||
|
|
||||||
|
from neutron.agent.common import ovs_lib
|
||||||
from neutron.agent.linux import bridge_lib
|
from neutron.agent.linux import bridge_lib
|
||||||
from neutron.agent.linux import ip_lib
|
from neutron.agent.linux import ip_lib
|
||||||
from neutron.agent.linux import ovs_lib
|
|
||||||
from neutron.common import constants as n_const
|
from neutron.common import constants as n_const
|
||||||
from neutron.tests.common import base
|
from neutron.tests.common import base
|
||||||
from neutron.tests.common import net_helpers
|
from neutron.tests.common import net_helpers
|
||||||
|
2
neutron/tests/functional/agent/test_l3_agent.py
Executable file → Normal file
2
neutron/tests/functional/agent/test_l3_agent.py
Executable file → Normal file
@ -26,6 +26,7 @@ import webob.dec
|
|||||||
import webob.exc
|
import webob.exc
|
||||||
|
|
||||||
from neutron.agent.common import config as agent_config
|
from neutron.agent.common import config as agent_config
|
||||||
|
from neutron.agent.common import ovs_lib
|
||||||
from neutron.agent.l3 import agent as neutron_l3_agent
|
from neutron.agent.l3 import agent as neutron_l3_agent
|
||||||
from neutron.agent.l3 import dvr_snat_ns
|
from neutron.agent.l3 import dvr_snat_ns
|
||||||
from neutron.agent.l3 import namespaces
|
from neutron.agent.l3 import namespaces
|
||||||
@ -33,7 +34,6 @@ from neutron.agent import l3_agent as l3_agent_main
|
|||||||
from neutron.agent.linux import dhcp
|
from neutron.agent.linux import dhcp
|
||||||
from neutron.agent.linux import external_process
|
from neutron.agent.linux import external_process
|
||||||
from neutron.agent.linux import ip_lib
|
from neutron.agent.linux import ip_lib
|
||||||
from neutron.agent.linux import ovs_lib
|
|
||||||
from neutron.agent.linux import utils
|
from neutron.agent.linux import utils
|
||||||
from neutron.common import config as common_config
|
from neutron.common import config as common_config
|
||||||
from neutron.common import constants as l3_constants
|
from neutron.common import constants as l3_constants
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
|
|
||||||
import collections
|
import collections
|
||||||
|
|
||||||
from neutron.agent.linux import ovs_lib
|
from neutron.agent.common import ovs_lib
|
||||||
from neutron.tests.common import net_helpers
|
from neutron.tests.common import net_helpers
|
||||||
from neutron.tests.functional.agent.linux import base
|
from neutron.tests.functional.agent.linux import base
|
||||||
|
|
||||||
|
@ -17,7 +17,7 @@ import mock
|
|||||||
from oslo_serialization import jsonutils
|
from oslo_serialization import jsonutils
|
||||||
import testtools
|
import testtools
|
||||||
|
|
||||||
from neutron.agent.linux import ovs_lib
|
from neutron.agent.common import ovs_lib
|
||||||
from neutron.agent.linux import utils
|
from neutron.agent.linux import utils
|
||||||
from neutron.common import exceptions
|
from neutron.common import exceptions
|
||||||
from neutron.openstack.common import uuidutils
|
from neutron.openstack.common import uuidutils
|
@ -19,7 +19,7 @@ import mock
|
|||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
import testtools
|
import testtools
|
||||||
|
|
||||||
from neutron.agent.linux import ovs_lib
|
from neutron.agent.common import ovs_lib
|
||||||
from neutron.agent import securitygroups_rpc as sg_rpc
|
from neutron.agent import securitygroups_rpc as sg_rpc
|
||||||
from neutron.common import topics
|
from neutron.common import topics
|
||||||
from neutron.extensions import securitygroup as ext_sg
|
from neutron.extensions import securitygroup as ext_sg
|
||||||
|
@ -60,16 +60,16 @@ class TestOvsDvrNeutronAgent(base.BaseTestCase):
|
|||||||
mock.patch('neutron.plugins.openvswitch.agent.ovs_neutron_agent.'
|
mock.patch('neutron.plugins.openvswitch.agent.ovs_neutron_agent.'
|
||||||
'OVSNeutronAgent.setup_ancillary_bridges',
|
'OVSNeutronAgent.setup_ancillary_bridges',
|
||||||
return_value=[]),
|
return_value=[]),
|
||||||
mock.patch('neutron.agent.linux.ovs_lib.OVSBridge.'
|
mock.patch('neutron.agent.common.ovs_lib.OVSBridge.'
|
||||||
'create'),
|
'create'),
|
||||||
mock.patch('neutron.agent.linux.ovs_lib.OVSBridge.'
|
mock.patch('neutron.agent.common.ovs_lib.OVSBridge.'
|
||||||
'set_secure_mode'),
|
'set_secure_mode'),
|
||||||
mock.patch('neutron.agent.linux.ovs_lib.OVSBridge.'
|
mock.patch('neutron.agent.common.ovs_lib.OVSBridge.'
|
||||||
'get_local_port_mac',
|
'get_local_port_mac',
|
||||||
return_value='00:00:00:00:00:01'),
|
return_value='00:00:00:00:00:01'),
|
||||||
mock.patch('neutron.agent.linux.utils.get_interface_mac',
|
mock.patch('neutron.agent.linux.utils.get_interface_mac',
|
||||||
return_value='00:00:00:00:00:01'),
|
return_value='00:00:00:00:00:01'),
|
||||||
mock.patch('neutron.agent.linux.ovs_lib.BaseOVS.get_bridges'),
|
mock.patch('neutron.agent.common.ovs_lib.BaseOVS.get_bridges'),
|
||||||
mock.patch('neutron.openstack.common.loopingcall.'
|
mock.patch('neutron.openstack.common.loopingcall.'
|
||||||
'FixedIntervalLoopingCall',
|
'FixedIntervalLoopingCall',
|
||||||
new=MockFixedIntervalLoopingCall)):
|
new=MockFixedIntervalLoopingCall)):
|
||||||
@ -129,11 +129,11 @@ class TestOvsDvrNeutronAgent(base.BaseTestCase):
|
|||||||
physical_network = self._physical_network
|
physical_network = self._physical_network
|
||||||
segmentation_id = self._segmentation_id
|
segmentation_id = self._segmentation_id
|
||||||
network_type = p_const.TYPE_VLAN
|
network_type = p_const.TYPE_VLAN
|
||||||
with mock.patch('neutron.agent.linux.ovs_lib.OVSBridge.'
|
with mock.patch('neutron.agent.common.ovs_lib.OVSBridge.'
|
||||||
'set_db_attribute',
|
'set_db_attribute',
|
||||||
return_value=True):
|
return_value=True):
|
||||||
with contextlib.nested(
|
with contextlib.nested(
|
||||||
mock.patch('neutron.agent.linux.ovs_lib.OVSBridge.'
|
mock.patch('neutron.agent.common.ovs_lib.OVSBridge.'
|
||||||
'db_get_val',
|
'db_get_val',
|
||||||
return_value=self._old_local_vlan),
|
return_value=self._old_local_vlan),
|
||||||
mock.patch.object(self.agent.dvr_agent.plugin_rpc,
|
mock.patch.object(self.agent.dvr_agent.plugin_rpc,
|
||||||
@ -249,11 +249,11 @@ class TestOvsDvrNeutronAgent(base.BaseTestCase):
|
|||||||
self._compute_port.vif_mac = '77:88:99:00:11:22'
|
self._compute_port.vif_mac = '77:88:99:00:11:22'
|
||||||
physical_network = self._physical_network
|
physical_network = self._physical_network
|
||||||
segmentation_id = self._segmentation_id
|
segmentation_id = self._segmentation_id
|
||||||
with mock.patch('neutron.agent.linux.ovs_lib.OVSBridge.'
|
with mock.patch('neutron.agent.common.ovs_lib.OVSBridge.'
|
||||||
'set_db_attribute',
|
'set_db_attribute',
|
||||||
return_value=True):
|
return_value=True):
|
||||||
with contextlib.nested(
|
with contextlib.nested(
|
||||||
mock.patch('neutron.agent.linux.ovs_lib.OVSBridge.'
|
mock.patch('neutron.agent.common.ovs_lib.OVSBridge.'
|
||||||
'db_get_val',
|
'db_get_val',
|
||||||
return_value=self._old_local_vlan),
|
return_value=self._old_local_vlan),
|
||||||
mock.patch.object(self.agent.dvr_agent.plugin_rpc,
|
mock.patch.object(self.agent.dvr_agent.plugin_rpc,
|
||||||
@ -381,11 +381,11 @@ class TestOvsDvrNeutronAgent(base.BaseTestCase):
|
|||||||
|
|
||||||
def test_port_bound_for_dvr_with_csnat_ports(self, ofport=10):
|
def test_port_bound_for_dvr_with_csnat_ports(self, ofport=10):
|
||||||
self._setup_for_dvr_test()
|
self._setup_for_dvr_test()
|
||||||
with mock.patch('neutron.agent.linux.ovs_lib.OVSBridge.'
|
with mock.patch('neutron.agent.common.ovs_lib.OVSBridge.'
|
||||||
'set_db_attribute',
|
'set_db_attribute',
|
||||||
return_value=True):
|
return_value=True):
|
||||||
with contextlib.nested(
|
with contextlib.nested(
|
||||||
mock.patch('neutron.agent.linux.ovs_lib.OVSBridge.'
|
mock.patch('neutron.agent.common.ovs_lib.OVSBridge.'
|
||||||
'db_get_val',
|
'db_get_val',
|
||||||
return_value=self._old_local_vlan),
|
return_value=self._old_local_vlan),
|
||||||
mock.patch.object(
|
mock.patch.object(
|
||||||
@ -429,11 +429,11 @@ class TestOvsDvrNeutronAgent(base.BaseTestCase):
|
|||||||
else:
|
else:
|
||||||
gateway_ip = '2001:100::1'
|
gateway_ip = '2001:100::1'
|
||||||
cidr = '2001:100::0/64'
|
cidr = '2001:100::0/64'
|
||||||
with mock.patch('neutron.agent.linux.ovs_lib.OVSBridge.'
|
with mock.patch('neutron.agent.common.ovs_lib.OVSBridge.'
|
||||||
'set_db_attribute',
|
'set_db_attribute',
|
||||||
return_value=True):
|
return_value=True):
|
||||||
with contextlib.nested(
|
with contextlib.nested(
|
||||||
mock.patch('neutron.agent.linux.ovs_lib.OVSBridge.'
|
mock.patch('neutron.agent.common.ovs_lib.OVSBridge.'
|
||||||
'db_get_val',
|
'db_get_val',
|
||||||
return_value=self._old_local_vlan),
|
return_value=self._old_local_vlan),
|
||||||
mock.patch.object(
|
mock.patch.object(
|
||||||
@ -510,11 +510,11 @@ class TestOvsDvrNeutronAgent(base.BaseTestCase):
|
|||||||
else:
|
else:
|
||||||
gateway_ip = '2001:100::1'
|
gateway_ip = '2001:100::1'
|
||||||
cidr = '2001:100::0/64'
|
cidr = '2001:100::0/64'
|
||||||
with mock.patch('neutron.agent.linux.ovs_lib.OVSBridge.'
|
with mock.patch('neutron.agent.common.ovs_lib.OVSBridge.'
|
||||||
'set_db_attribute',
|
'set_db_attribute',
|
||||||
return_value=True):
|
return_value=True):
|
||||||
with contextlib.nested(
|
with contextlib.nested(
|
||||||
mock.patch('neutron.agent.linux.ovs_lib.OVSBridge.'
|
mock.patch('neutron.agent.common.ovs_lib.OVSBridge.'
|
||||||
'db_get_val',
|
'db_get_val',
|
||||||
return_value=self._old_local_vlan),
|
return_value=self._old_local_vlan),
|
||||||
mock.patch.object(
|
mock.patch.object(
|
||||||
@ -587,11 +587,11 @@ class TestOvsDvrNeutronAgent(base.BaseTestCase):
|
|||||||
|
|
||||||
def test_treat_devices_removed_for_dvr_csnat_port(self, ofport=10):
|
def test_treat_devices_removed_for_dvr_csnat_port(self, ofport=10):
|
||||||
self._setup_for_dvr_test()
|
self._setup_for_dvr_test()
|
||||||
with mock.patch('neutron.agent.linux.ovs_lib.OVSBridge.'
|
with mock.patch('neutron.agent.common.ovs_lib.OVSBridge.'
|
||||||
'set_db_attribute',
|
'set_db_attribute',
|
||||||
return_value=True):
|
return_value=True):
|
||||||
with contextlib.nested(
|
with contextlib.nested(
|
||||||
mock.patch('neutron.agent.linux.ovs_lib.OVSBridge.'
|
mock.patch('neutron.agent.common.ovs_lib.OVSBridge.'
|
||||||
'db_get_val',
|
'db_get_val',
|
||||||
return_value=self._old_local_vlan),
|
return_value=self._old_local_vlan),
|
||||||
mock.patch.object(
|
mock.patch.object(
|
||||||
|
@ -22,9 +22,9 @@ from oslo_config import cfg
|
|||||||
from oslo_log import log
|
from oslo_log import log
|
||||||
import testtools
|
import testtools
|
||||||
|
|
||||||
|
from neutron.agent.common import ovs_lib
|
||||||
from neutron.agent.linux import async_process
|
from neutron.agent.linux import async_process
|
||||||
from neutron.agent.linux import ip_lib
|
from neutron.agent.linux import ip_lib
|
||||||
from neutron.agent.linux import ovs_lib
|
|
||||||
from neutron.agent.linux import utils
|
from neutron.agent.linux import utils
|
||||||
from neutron.common import constants as n_const
|
from neutron.common import constants as n_const
|
||||||
from neutron.plugins.common import constants as p_const
|
from neutron.plugins.common import constants as p_const
|
||||||
@ -115,16 +115,16 @@ class TestOvsNeutronAgent(base.BaseTestCase):
|
|||||||
mock.patch('neutron.plugins.openvswitch.agent.ovs_neutron_agent.'
|
mock.patch('neutron.plugins.openvswitch.agent.ovs_neutron_agent.'
|
||||||
'OVSNeutronAgent.setup_ancillary_bridges',
|
'OVSNeutronAgent.setup_ancillary_bridges',
|
||||||
return_value=[]),
|
return_value=[]),
|
||||||
mock.patch('neutron.agent.linux.ovs_lib.OVSBridge.'
|
mock.patch('neutron.agent.common.ovs_lib.OVSBridge.'
|
||||||
'create'),
|
'create'),
|
||||||
mock.patch('neutron.agent.linux.ovs_lib.OVSBridge.'
|
mock.patch('neutron.agent.common.ovs_lib.OVSBridge.'
|
||||||
'set_secure_mode'),
|
'set_secure_mode'),
|
||||||
mock.patch('neutron.agent.linux.ovs_lib.OVSBridge.'
|
mock.patch('neutron.agent.common.ovs_lib.OVSBridge.'
|
||||||
'get_local_port_mac',
|
'get_local_port_mac',
|
||||||
return_value='00:00:00:00:00:01'),
|
return_value='00:00:00:00:00:01'),
|
||||||
mock.patch('neutron.agent.linux.utils.get_interface_mac',
|
mock.patch('neutron.agent.linux.utils.get_interface_mac',
|
||||||
return_value='00:00:00:00:00:01'),
|
return_value='00:00:00:00:00:01'),
|
||||||
mock.patch('neutron.agent.linux.ovs_lib.BaseOVS.get_bridges'),
|
mock.patch('neutron.agent.common.ovs_lib.BaseOVS.get_bridges'),
|
||||||
mock.patch('neutron.openstack.common.loopingcall.'
|
mock.patch('neutron.openstack.common.loopingcall.'
|
||||||
'FixedIntervalLoopingCall',
|
'FixedIntervalLoopingCall',
|
||||||
new=MockFixedIntervalLoopingCall)):
|
new=MockFixedIntervalLoopingCall)):
|
||||||
@ -147,9 +147,9 @@ class TestOvsNeutronAgent(base.BaseTestCase):
|
|||||||
ovs_neutron_agent.LocalVLANMapping(
|
ovs_neutron_agent.LocalVLANMapping(
|
||||||
old_local_vlan, None, None, None))
|
old_local_vlan, None, None, None))
|
||||||
with contextlib.nested(
|
with contextlib.nested(
|
||||||
mock.patch('neutron.agent.linux.ovs_lib.OVSBridge.'
|
mock.patch('neutron.agent.common.ovs_lib.OVSBridge.'
|
||||||
'set_db_attribute', return_value=True),
|
'set_db_attribute', return_value=True),
|
||||||
mock.patch('neutron.agent.linux.ovs_lib.OVSBridge.'
|
mock.patch('neutron.agent.common.ovs_lib.OVSBridge.'
|
||||||
'db_get_val', return_value=old_local_vlan),
|
'db_get_val', return_value=old_local_vlan),
|
||||||
mock.patch.object(self.agent.int_br, 'delete_flows')
|
mock.patch.object(self.agent.int_br, 'delete_flows')
|
||||||
) as (set_ovs_db_func, get_ovs_db_func, delete_flows_func):
|
) as (set_ovs_db_func, get_ovs_db_func, delete_flows_func):
|
||||||
@ -191,9 +191,9 @@ class TestOvsNeutronAgent(base.BaseTestCase):
|
|||||||
port = mock.Mock()
|
port = mock.Mock()
|
||||||
port.ofport = 1
|
port.ofport = 1
|
||||||
with contextlib.nested(
|
with contextlib.nested(
|
||||||
mock.patch('neutron.agent.linux.ovs_lib.OVSBridge.'
|
mock.patch('neutron.agent.common.ovs_lib.OVSBridge.'
|
||||||
'set_db_attribute', return_value=True),
|
'set_db_attribute', return_value=True),
|
||||||
mock.patch('neutron.agent.linux.ovs_lib.OVSBridge.'
|
mock.patch('neutron.agent.common.ovs_lib.OVSBridge.'
|
||||||
'db_get_val', return_value=cur_tag),
|
'db_get_val', return_value=cur_tag),
|
||||||
mock.patch.object(self.agent.int_br, 'add_flow')
|
mock.patch.object(self.agent.int_br, 'add_flow')
|
||||||
) as (set_ovs_db_func, get_ovs_db_func, add_flow_func):
|
) as (set_ovs_db_func, get_ovs_db_func, add_flow_func):
|
||||||
@ -1121,14 +1121,14 @@ class AncillaryBridgesTest(base.BaseTestCase):
|
|||||||
'OVSNeutronAgent.setup_integration_br'),
|
'OVSNeutronAgent.setup_integration_br'),
|
||||||
mock.patch('neutron.agent.linux.utils.get_interface_mac',
|
mock.patch('neutron.agent.linux.utils.get_interface_mac',
|
||||||
return_value='00:00:00:00:00:01'),
|
return_value='00:00:00:00:00:01'),
|
||||||
mock.patch('neutron.agent.linux.ovs_lib.OVSBridge.'
|
mock.patch('neutron.agent.common.ovs_lib.OVSBridge.'
|
||||||
'get_local_port_mac',
|
'get_local_port_mac',
|
||||||
return_value='00:00:00:00:00:01'),
|
return_value='00:00:00:00:00:01'),
|
||||||
mock.patch('neutron.agent.linux.ovs_lib.OVSBridge.'
|
mock.patch('neutron.agent.common.ovs_lib.OVSBridge.'
|
||||||
'set_secure_mode'),
|
'set_secure_mode'),
|
||||||
mock.patch('neutron.agent.linux.ovs_lib.BaseOVS.get_bridges',
|
mock.patch('neutron.agent.common.ovs_lib.BaseOVS.get_bridges',
|
||||||
return_value=bridges),
|
return_value=bridges),
|
||||||
mock.patch('neutron.agent.linux.ovs_lib.BaseOVS.'
|
mock.patch('neutron.agent.common.ovs_lib.BaseOVS.'
|
||||||
'get_bridge_external_bridge_id',
|
'get_bridge_external_bridge_id',
|
||||||
side_effect=pullup_side_effect)):
|
side_effect=pullup_side_effect)):
|
||||||
self.agent = ovs_neutron_agent.OVSNeutronAgent(**self.kwargs)
|
self.agent = ovs_neutron_agent.OVSNeutronAgent(**self.kwargs)
|
||||||
|
@ -21,8 +21,8 @@ import mock
|
|||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
from oslo_log import log
|
from oslo_log import log
|
||||||
|
|
||||||
|
from neutron.agent.common import ovs_lib
|
||||||
from neutron.agent.linux import ip_lib
|
from neutron.agent.linux import ip_lib
|
||||||
from neutron.agent.linux import ovs_lib
|
|
||||||
from neutron.plugins.common import constants as p_const
|
from neutron.plugins.common import constants as p_const
|
||||||
from neutron.plugins.openvswitch.agent import ovs_neutron_agent
|
from neutron.plugins.openvswitch.agent import ovs_neutron_agent
|
||||||
from neutron.plugins.openvswitch.common import constants
|
from neutron.plugins.openvswitch.common import constants
|
||||||
|
@ -16,9 +16,9 @@
|
|||||||
import mock
|
import mock
|
||||||
|
|
||||||
from neutron.agent.common import config
|
from neutron.agent.common import config
|
||||||
|
from neutron.agent.common import ovs_lib
|
||||||
from neutron.agent.linux import interface
|
from neutron.agent.linux import interface
|
||||||
from neutron.agent.linux import ip_lib
|
from neutron.agent.linux import ip_lib
|
||||||
from neutron.agent.linux import ovs_lib
|
|
||||||
from neutron.agent.linux import utils
|
from neutron.agent.linux import utils
|
||||||
from neutron.common import constants
|
from neutron.common import constants
|
||||||
from neutron.extensions import flavor
|
from neutron.extensions import flavor
|
||||||
@ -273,7 +273,7 @@ class TestOVSInterfaceDriver(TestBase):
|
|||||||
def test_unplug(self, bridge=None):
|
def test_unplug(self, bridge=None):
|
||||||
if not bridge:
|
if not bridge:
|
||||||
bridge = 'br-int'
|
bridge = 'br-int'
|
||||||
with mock.patch('neutron.agent.linux.ovs_lib.OVSBridge') as ovs_br:
|
with mock.patch('neutron.agent.common.ovs_lib.OVSBridge') as ovs_br:
|
||||||
ovs = interface.OVSInterfaceDriver(self.conf)
|
ovs = interface.OVSInterfaceDriver(self.conf)
|
||||||
ovs.unplug('tap0')
|
ovs.unplug('tap0')
|
||||||
ovs_br.assert_has_calls([mock.call(bridge),
|
ovs_br.assert_has_calls([mock.call(bridge),
|
||||||
@ -347,7 +347,7 @@ class TestOVSInterfaceDriverWithVeth(TestOVSInterfaceDriver):
|
|||||||
def test_unplug(self, bridge=None):
|
def test_unplug(self, bridge=None):
|
||||||
if not bridge:
|
if not bridge:
|
||||||
bridge = 'br-int'
|
bridge = 'br-int'
|
||||||
with mock.patch('neutron.agent.linux.ovs_lib.OVSBridge') as ovs_br:
|
with mock.patch('neutron.agent.common.ovs_lib.OVSBridge') as ovs_br:
|
||||||
ovs = interface.OVSInterfaceDriver(self.conf)
|
ovs = interface.OVSInterfaceDriver(self.conf)
|
||||||
ovs.unplug('ns-0', bridge=bridge)
|
ovs.unplug('ns-0', bridge=bridge)
|
||||||
ovs_br.assert_has_calls([mock.call(bridge),
|
ovs_br.assert_has_calls([mock.call(bridge),
|
||||||
|
@ -96,9 +96,10 @@ class TestNetnsCleanup(base.BaseTestCase):
|
|||||||
device.name = 'tap1'
|
device.name = 'tap1'
|
||||||
device.link.delete.side_effect = RuntimeError
|
device.link.delete.side_effect = RuntimeError
|
||||||
|
|
||||||
with mock.patch('neutron.agent.linux.ovs_lib.OVSBridge') as ovs_br_cls:
|
with mock.patch(
|
||||||
|
'neutron.agent.common.ovs_lib.OVSBridge') as ovs_br_cls:
|
||||||
br_patch = mock.patch(
|
br_patch = mock.patch(
|
||||||
'neutron.agent.linux.ovs_lib.BaseOVS.get_bridge_for_iface')
|
'neutron.agent.common.ovs_lib.BaseOVS.get_bridge_for_iface')
|
||||||
with br_patch as mock_get_bridge_for_iface:
|
with br_patch as mock_get_bridge_for_iface:
|
||||||
mock_get_bridge_for_iface.return_value = 'br-int'
|
mock_get_bridge_for_iface.return_value = 'br-int'
|
||||||
ovs_bridge = mock.Mock()
|
ovs_bridge = mock.Mock()
|
||||||
@ -119,9 +120,10 @@ class TestNetnsCleanup(base.BaseTestCase):
|
|||||||
device.name = 'tap1'
|
device.name = 'tap1'
|
||||||
device.link.delete.side_effect = RuntimeError
|
device.link.delete.side_effect = RuntimeError
|
||||||
|
|
||||||
with mock.patch('neutron.agent.linux.ovs_lib.OVSBridge') as ovs_br_cls:
|
with mock.patch(
|
||||||
|
'neutron.agent.common.ovs_lib.OVSBridge') as ovs_br_cls:
|
||||||
br_patch = mock.patch(
|
br_patch = mock.patch(
|
||||||
'neutron.agent.linux.ovs_lib.BaseOVS.get_bridge_for_iface')
|
'neutron.agent.common.ovs_lib.BaseOVS.get_bridge_for_iface')
|
||||||
with br_patch as mock_get_bridge_for_iface:
|
with br_patch as mock_get_bridge_for_iface:
|
||||||
with mock.patch.object(util.LOG, 'debug') as debug:
|
with mock.patch.object(util.LOG, 'debug') as debug:
|
||||||
mock_get_bridge_for_iface.return_value = None
|
mock_get_bridge_for_iface.return_value = None
|
||||||
|
@ -17,8 +17,8 @@ import contextlib
|
|||||||
import itertools
|
import itertools
|
||||||
import mock
|
import mock
|
||||||
|
|
||||||
|
from neutron.agent.common import ovs_lib
|
||||||
from neutron.agent.linux import ip_lib
|
from neutron.agent.linux import ip_lib
|
||||||
from neutron.agent.linux import ovs_lib
|
|
||||||
from neutron.cmd import ovs_cleanup as util
|
from neutron.cmd import ovs_cleanup as util
|
||||||
from neutron.openstack.common import uuidutils
|
from neutron.openstack.common import uuidutils
|
||||||
from neutron.tests import base
|
from neutron.tests import base
|
||||||
@ -37,9 +37,9 @@ class TestOVSCleanup(base.BaseTestCase):
|
|||||||
mock.patch('neutron.common.config.setup_logging'),
|
mock.patch('neutron.common.config.setup_logging'),
|
||||||
mock.patch('neutron.cmd.ovs_cleanup.setup_conf',
|
mock.patch('neutron.cmd.ovs_cleanup.setup_conf',
|
||||||
return_value=conf),
|
return_value=conf),
|
||||||
mock.patch('neutron.agent.linux.ovs_lib.BaseOVS.get_bridges',
|
mock.patch('neutron.agent.common.ovs_lib.BaseOVS.get_bridges',
|
||||||
return_value=bridges),
|
return_value=bridges),
|
||||||
mock.patch('neutron.agent.linux.ovs_lib.OVSBridge'),
|
mock.patch('neutron.agent.common.ovs_lib.OVSBridge'),
|
||||||
mock.patch.object(util, 'collect_neutron_ports',
|
mock.patch.object(util, 'collect_neutron_ports',
|
||||||
return_value=ports),
|
return_value=ports),
|
||||||
mock.patch.object(util, 'delete_neutron_ports')
|
mock.patch.object(util, 'delete_neutron_ports')
|
||||||
@ -60,7 +60,7 @@ class TestOVSCleanup(base.BaseTestCase):
|
|||||||
'99:00:aa:bb:cc:dd', 'br')
|
'99:00:aa:bb:cc:dd', 'br')
|
||||||
ports = [[port1, port2], [port3]]
|
ports = [[port1, port2], [port3]]
|
||||||
portnames = [p.port_name for p in itertools.chain(*ports)]
|
portnames = [p.port_name for p in itertools.chain(*ports)]
|
||||||
with mock.patch('neutron.agent.linux.ovs_lib.OVSBridge') as ovs:
|
with mock.patch('neutron.agent.common.ovs_lib.OVSBridge') as ovs:
|
||||||
ovs.return_value.get_vif_ports.side_effect = ports
|
ovs.return_value.get_vif_ports.side_effect = ports
|
||||||
bridges = ['br-int', 'br-ex']
|
bridges = ['br-int', 'br-ex']
|
||||||
ret = util.collect_neutron_ports(bridges)
|
ret = util.collect_neutron_ports(bridges)
|
||||||
|
Loading…
Reference in New Issue
Block a user