aboutsummaryrefslogtreecommitdiffstats
path: root/networking_sfc/services/sfc/common
diff options
context:
space:
mode:
Diffstat (limited to 'networking_sfc/services/sfc/common')
-rw-r--r--networking_sfc/services/sfc/common/__init__.py0
-rw-r--r--networking_sfc/services/sfc/common/config.py27
-rw-r--r--networking_sfc/services/sfc/common/context.py85
-rw-r--r--networking_sfc/services/sfc/common/exceptions.py46
-rw-r--r--networking_sfc/services/sfc/common/ovs_ext_lib.py187
5 files changed, 345 insertions, 0 deletions
diff --git a/networking_sfc/services/sfc/common/__init__.py b/networking_sfc/services/sfc/common/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/networking_sfc/services/sfc/common/__init__.py
diff --git a/networking_sfc/services/sfc/common/config.py b/networking_sfc/services/sfc/common/config.py
new file mode 100644
index 0000000..29acd1c
--- /dev/null
+++ b/networking_sfc/services/sfc/common/config.py
@@ -0,0 +1,27 @@
+# Copyright 2015 Futurewei. 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.
+
+from oslo_config import cfg
+
+
+SFC_DRIVER_OPTS = [
+ cfg.ListOpt('drivers',
+ default=['dummy'],
+ help=_("An ordered list of service chain drivers "
+ "entrypoints to be loaded from the "
+ "networking_sfc.sfc.drivers namespace.")),
+]
+
+
+cfg.CONF.register_opts(SFC_DRIVER_OPTS, "sfc")
diff --git a/networking_sfc/services/sfc/common/context.py b/networking_sfc/services/sfc/common/context.py
new file mode 100644
index 0000000..7d3b451
--- /dev/null
+++ b/networking_sfc/services/sfc/common/context.py
@@ -0,0 +1,85 @@
+# Copyright 2015 Futurewei. 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.
+
+
+class SfcPluginContext(object):
+ """SFC context base class."""
+ def __init__(self, plugin, plugin_context):
+ self._plugin = plugin
+ self._plugin_context = plugin_context
+
+
+class PortChainContext(SfcPluginContext):
+
+ def __init__(self, plugin, plugin_context, portchain,
+ original_portchain=None):
+ super(PortChainContext, self).__init__(plugin, plugin_context)
+ self._portchain = portchain
+ self._original_portchain = original_portchain
+
+ @property
+ def current(self):
+ return self._portchain
+
+ @property
+ def original(self):
+ return self._original_portchain
+
+
+class FlowClassifierContext(SfcPluginContext):
+ def __init__(self, plugin, plugin_context, flowclassifier,
+ original_flowclassifier=None):
+ super(FlowClassifierContext, self).__init__(plugin, plugin_context)
+ self._flowclassifier = flowclassifier
+ self._original_flowclassifier = original_flowclassifier
+
+ @property
+ def current(self):
+ return self._flowclassifier
+
+ @property
+ def original(self):
+ return self._original_flowclassifier
+
+
+class PortPairContext(SfcPluginContext):
+ def __init__(self, plugin, plugin_context, portpair,
+ original_portpair=None):
+ super(PortPairContext, self).__init__(plugin, plugin_context)
+ self._portpair = portpair
+ self._original_portpair = original_portpair
+
+ @property
+ def current(self):
+ return self._portpair
+
+ @property
+ def original(self):
+ return self._original_portpair
+
+
+class PortPairGroupContext(SfcPluginContext):
+ def __init__(self, plugin, plugin_context, portpairgroup,
+ original_portpairgroup=None):
+ super(PortPairGroupContext, self).__init__(plugin, plugin_context)
+ self._portpairgroup = portpairgroup
+ self._original_portpairgroup = original_portpairgroup
+
+ @property
+ def current(self):
+ return self._portpairgroup
+
+ @property
+ def original(self):
+ return self._original_portpairgroup
diff --git a/networking_sfc/services/sfc/common/exceptions.py b/networking_sfc/services/sfc/common/exceptions.py
new file mode 100644
index 0000000..7d1b9d9
--- /dev/null
+++ b/networking_sfc/services/sfc/common/exceptions.py
@@ -0,0 +1,46 @@
+# Copyright 2015 Futurewei. 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.
+"""Exceptions used by SFC plugin and drivers."""
+
+from neutron.common import exceptions
+
+
+class SfcDriverError(exceptions.NeutronException):
+ """SFC driver call failed."""
+ message = _("%(method)s failed.")
+
+
+class SfcException(exceptions.NeutronException):
+ """Base for SFC driver exceptions returned to user."""
+ pass
+
+
+class SfcBadRequest(exceptions.BadRequest, SfcException):
+ """Base for SFC driver bad request exceptions returned to user."""
+ pass
+
+
+class SfcNoSubnetGateway(SfcDriverError):
+ """No subnet gateway."""
+ message = _("There is no %(type)s of ip prefix %(cidr)s.")
+
+
+class SfcNoSuchSubnet(SfcDriverError):
+ """No such subnet."""
+ message = _("There is no %(type)s of %(cidr)s.")
+
+
+class FlowClassifierInvalid(SfcDriverError):
+ """Invalid flow classifier."""
+ message = _("There is no %(type)s assigned.")
diff --git a/networking_sfc/services/sfc/common/ovs_ext_lib.py b/networking_sfc/services/sfc/common/ovs_ext_lib.py
new file mode 100644
index 0000000..01fbd04
--- /dev/null
+++ b/networking_sfc/services/sfc/common/ovs_ext_lib.py
@@ -0,0 +1,187 @@
+# Copyright 2015 Huawei. 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
+from neutron.agent.common import ovs_lib
+from neutron.agent.common import utils
+from neutron.common import exceptions
+from neutron.i18n import _LE
+from neutron.plugins.common import constants
+from neutron.plugins.ml2.drivers.openvswitch.agent.openflow.ovs_ofctl import (
+ ovs_bridge)
+from oslo_log import log as logging
+import six
+
+
+# Special return value for an invalid OVS ofport
+INVALID_OFPORT = '-1'
+
+LOG = logging.getLogger(__name__)
+
+
+def get_port_mask(min_port, max_port):
+ """get port/mask serial by port range."""
+ if min_port < 1 or max_port > 0xffff or min_port > max_port:
+ msg = _("the port range is invalid")
+ raise exceptions.InvalidInput(error_message=msg)
+ masks = []
+ while min_port <= max_port:
+ mask = 0xffff
+ while mask != 0:
+ next_mask = (mask << 1) & 0xffff
+ port_start = min_port & next_mask
+ port_end = min_port + (next_mask ^ 0xffff)
+ if port_start == min_port and port_end <= max_port:
+ mask = next_mask
+ else:
+ break
+ masks.append('0x%x/0x%x' % (min_port, mask))
+ min_port = min_port + (mask ^ 0xffff) + 1
+
+ return masks
+
+
+class OVSBridgeExt(ovs_bridge.OVSAgentBridge):
+ def setup_controllers(self, conf):
+ self.set_protocols("[]")
+ self.del_controller()
+
+ def dump_flows_full_match(self, flow_str):
+ retval = None
+ flows = self.run_ofctl("dump-flows", [flow_str])
+ if flows:
+ retval = '\n'.join(item for item in flows.splitlines()
+ if 'NXST' not in item and 'OFPST' not in item)
+ return retval
+
+ def mod_flow(self, **kwargs):
+ flow_copy = kwargs.copy()
+ flow_copy.pop('actions')
+ flow_str = ovs_lib._build_flow_expr_str(flow_copy, 'del')
+ dump_flows = self.dump_flows_full_match(flow_str)
+ if dump_flows == '':
+ self.do_action_flows('add', [kwargs])
+ else:
+ self.do_action_flows('mod', [kwargs])
+
+ def add_nsh_tunnel_port(self, port_name, remote_ip, local_ip,
+ tunnel_type=constants.TYPE_GRE,
+ vxlan_udp_port=constants.VXLAN_UDP_PORT,
+ dont_fragment=True,
+ in_nsp=None,
+ in_nsi=None):
+ attrs = [('type', tunnel_type)]
+ # 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'] = 'flow'
+ options['local_ip'] = local_ip
+ options['in_key'] = 'flow'
+ options['out_key'] = 'flow'
+ if in_nsp is not None and in_nsi is not None:
+ options['nsp'] = str(in_nsp)
+ options['nsi'] = str(in_nsi)
+ elif in_nsp is None and in_nsi is None:
+ options['nsp'] = 'flow'
+ options['nsi'] = 'flow'
+ attrs.append(('options', options))
+ ofport = self.add_port(port_name, *attrs)
+ if (
+ tunnel_type == constants.TYPE_VXLAN and
+ ofport == INVALID_OFPORT
+ ):
+ LOG.error(
+ _LE('Unable to create VXLAN tunnel port for service chain. '
+ 'Please ensure that an openvswitch version that supports '
+ 'VXLAN for service chain is installed.')
+ )
+ return ofport
+
+ def run_ofctl(self, cmd, args, process_input=None):
+ # We need to dump-groups according to group Id,
+ # which is a feature of OpenFlow1.5
+ full_args = [
+ "ovs-ofctl", "-O openflow13", cmd, self.br_name
+ ] + args
+ try:
+ return utils.execute(full_args, run_as_root=True,
+ process_input=process_input)
+ except Exception as e:
+ LOG.exception(e)
+ LOG.error(_LE("Unable to execute %(args)s."),
+ {'args': full_args})
+
+ def do_action_groups(self, action, kwargs_list):
+ group_strs = [_build_group_expr_str(kw, action) for kw in kwargs_list]
+ if action == 'add' or action == 'del':
+ self.run_ofctl('%s-groups' % action, ['-'], '\n'.join(group_strs))
+ elif action == 'mod':
+ self.run_ofctl('%s-group' % action, ['-'], '\n'.join(group_strs))
+ else:
+ msg = _("Action is illegal")
+ raise exceptions.InvalidInput(error_message=msg)
+
+ def add_group(self, **kwargs):
+ self.do_action_groups('add', [kwargs])
+
+ def mod_group(self, **kwargs):
+ self.do_action_groups('mod', [kwargs])
+
+ def delete_group(self, **kwargs):
+ self.do_action_groups('del', [kwargs])
+
+ def dump_group_for_id(self, group_id):
+ retval = None
+ group_str = "%d" % group_id
+ group = self.run_ofctl("dump-groups", [group_str])
+ if group:
+ retval = '\n'.join(item for item in group.splitlines()
+ if 'NXST' not in item)
+ return retval
+
+
+def _build_group_expr_str(group_dict, cmd):
+ group_expr_arr = []
+ buckets = None
+ groupId = None
+
+ if cmd != 'del':
+ if "group_id" not in group_dict:
+ msg = _("Must specify one groupId on groupo addition"
+ " or modification")
+ raise exceptions.InvalidInput(error_message=msg)
+ groupId = "group_id=%s" % group_dict.pop('group_id')
+
+ if "buckets" not in group_dict:
+ msg = _("Must specify one or more buckets on group addition"
+ " or modification")
+ raise exceptions.InvalidInput(error_message=msg)
+ buckets = "%s" % group_dict.pop('buckets')
+
+ if groupId:
+ group_expr_arr.append(groupId)
+
+ for key, value in six.iteritems(group_dict):
+ group_expr_arr.append("%s=%s" % (key, value))
+
+ if buckets:
+ group_expr_arr.append(buckets)
+
+ return ','.join(group_expr_arr)