summaryrefslogtreecommitdiffstats
path: root/networking-odl/networking_odl/ml2/vpp_topology.py
diff options
context:
space:
mode:
Diffstat (limited to 'networking-odl/networking_odl/ml2/vpp_topology.py')
-rw-r--r--networking-odl/networking_odl/ml2/vpp_topology.py194
1 files changed, 194 insertions, 0 deletions
diff --git a/networking-odl/networking_odl/ml2/vpp_topology.py b/networking-odl/networking_odl/ml2/vpp_topology.py
new file mode 100644
index 0000000..c16399d
--- /dev/null
+++ b/networking-odl/networking_odl/ml2/vpp_topology.py
@@ -0,0 +1,194 @@
+# Copyright (c) 2016 OpenStack Foundation
+# 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 os
+
+from oslo_log import log
+import six
+
+from neutron.common import constants as n_const
+from neutron.extensions import portbindings
+from neutron.plugins.common import constants
+from neutron.plugins.ml2 import driver_api
+
+from networking_odl.ml2 import network_topology
+
+LOG = log.getLogger(__name__)
+HC_VPP_CAPABILITY = 'urn:opendaylight:params:xml:ns:yang:v3po'
+
+
+class VppNetworkTopologyParser(network_topology.NetworkTopologyParser):
+ def new_element(self, devname):
+ return VppNetworkTopologyElement(devname=devname)
+
+ def parse_network_topology(self, network_topologies):
+ LOG.debug("Parsing Topology using VPP Topology Parser")
+ elements_by_name = collections.OrderedDict()
+ for topology in network_topologies['network-topology']['topology']:
+ if topology['topology-id'].startswith('topology-netconf'):
+ for node in topology['node']:
+ # expected :
+ # "node-id": "name",
+ # "netconf-node-topology:host": "172.21.174.41"
+ # "netconf-node-topology:available-capabilities": {
+ # "available-capability" : contains the v3po model
+ node_name = node['node-id']
+ LOG.debug("Examining capabilities for node: %s\n",
+ node_name)
+ try:
+ capabilities = node[
+ 'netconf-node-topology:available-capabilities']
+ LOG.debug("Node's capabilities: %s\n",
+ capabilities)
+ for item in capabilities['available-capability']:
+ if HC_VPP_CAPABILITY in item:
+ LOG.debug("Found VPP matching capability for "
+ "node: %s\n", node_name)
+ element = elements_by_name.get(node_name)
+ if element is None:
+ elements_by_name[node_name] = element = \
+ self.new_element(node_name)
+
+ self._update_elmnt_from_json_netconf_topo_node(
+ node, element, node_name)
+ except KeyError:
+ LOG.debug("No netconf available capabilities found for"
+ ": %s\n", node_name)
+
+ # Can there can be more VPP instances connected beside the same IP
+ # address?
+ # Cache will yield more instaces for the same key
+ for __, element in six.iteritems(elements_by_name):
+ yield element
+
+ def _update_elmnt_from_json_netconf_topo_node(
+ self, node, element, node_name):
+
+ # fetch remote IP address
+ element.remote_ip = node["netconf-node-topology:host"]
+ # Assume Honeycomb-VPP supports vhost_user
+ element.support_vhost_user = True
+
+ LOG.debug(
+ 'Topology element updated:\n'
+ ' - VPP node name: %(node_name)r\n'
+ ' - remote_ip: %(remote_ip)r\n'
+ ' - support_vhost_user: %(support_vhost_user)r',
+ {'node_name': node_name,
+ 'remote_ip': element.remote_ip,
+ 'support_vhost_user': element.support_vhost_user})
+
+
+class VppNetworkTopologyElement(network_topology.NetworkTopologyElement):
+ devname = None # Filled in by parser
+ remote_ip = None # Filled in by parser
+ has_datapath_type_netdev = False # Placeholder for future capability
+ support_vhost_user = False # VPP supports it by default actually.
+
+ # location for vhostuser sockets.
+ # TODO(wdec): This should be configurable in the ML2 config.
+ vhostuser_socket_dir = '/tmp/'
+
+ # TODO(wdec): And also this should be configurable in ML2...
+ # prefix for port
+ port_prefix = 'socket_'
+
+ def __init__(self, **kwargs):
+ for name, value in six.iteritems(kwargs):
+ setattr(self, name, value)
+
+ @property
+ def host_addresses(self):
+ # For now it support only the remote IP found in connection info
+ return self.remote_ip,
+
+ @property
+ def valid_vif_types(self):
+ return [portbindings.VIF_TYPE_VHOST_USER]
+
+ def bind_port(self, port_context, vif_type, vif_details):
+
+ port_context_id = port_context.current['id']
+ network_context_id = port_context.network.current['id']
+
+ # Bind port to the first valid segment
+ for segment in port_context.segments_to_bind:
+ if self._is_valid_segment(segment):
+ # Guest best VIF type for given host
+ vif_details = self._get_vif_details(
+ vif_details=vif_details, port_context_id=port_context_id,
+ vif_type=vif_type)
+ LOG.debug(
+ 'Bind port with valid segment:\n'
+ '\tport: %(port)r\n'
+ '\tnetwork: %(network)r\n'
+ '\tsegment: %(segment)r\n'
+ '\tVIF type: %(vif_type)r\n'
+ '\tVIF details: %(vif_details)r',
+ {'port': port_context_id,
+ 'network': network_context_id,
+ 'segment': segment, 'vif_type': vif_type,
+ 'vif_details': vif_details})
+ port_context.set_binding(
+ segment[driver_api.ID], vif_type, vif_details,
+ status=n_const.PORT_STATUS_ACTIVE)
+ return
+
+ raise ValueError('Unable to find any valid segment in given context.')
+
+ def to_dict(self):
+ data = super(VppNetworkTopologyElement, self).to_dict()
+ data.update(
+ {'uuid': self.devname,
+ 'has_datapath_type_netdev': self.has_datapath_type_netdev,
+ 'support_vhost_user': self.support_vhost_user,
+ 'valid_vif_types': self.valid_vif_types})
+ if portbindings.VIF_TYPE_VHOST_USER in self.valid_vif_types:
+ data.update({'port_prefix': self.port_prefix,
+ 'vhostuser_socket_dir': self.vhostuser_socket_dir})
+ return data
+
+ def _is_valid_segment(self, segment):
+ """Verify a segment is valid for the OpenDaylight MechanismDriver.
+
+ Verify the requested segment is supported by ODL and return True or
+ False to indicate this to callers.
+ """
+
+ network_type = segment[driver_api.NETWORK_TYPE]
+ return network_type in [constants.TYPE_LOCAL, constants.TYPE_GRE,
+ constants.TYPE_VXLAN, constants.TYPE_VLAN,
+ constants.TYPE_FLAT]
+
+ def _get_vif_details(self, vif_details, port_context_id, vif_type):
+ vif_details = dict(vif_details)
+ if vif_type == portbindings.VIF_TYPE_VHOST_USER:
+ socket_path = os.path.join(
+ self.vhostuser_socket_dir,
+ (self.port_prefix + port_context_id))
+
+ vif_details.update({
+ portbindings.VHOST_USER_MODE:
+ portbindings.VHOST_USER_MODE_SERVER,
+ portbindings.VHOST_USER_SOCKET: socket_path
+ })
+ return vif_details
+
+ def __setattr__(self, name, value):
+ # raises Attribute error if the class hasn't this attribute
+ getattr(type(self), name)
+ super(VppNetworkTopologyElement, self).__setattr__(name, value)