From 4979a23b8b2c0094ced98cf05eebb692d6609937 Mon Sep 17 00:00:00 2001 From: Wojciech Dec Date: Wed, 17 Aug 2016 13:14:23 +0200 Subject: Correcting networking-odl to mitaka/stable + app topology patch Change-Id: Iddcd8dda2d49fcdd8e0f37a1d052a6fa8a24b035 Signed-off-by: Wojciech Dec --- networking-odl/networking_odl/ml2/vpp_topology.py | 194 ++++++++++++++++++++++ 1 file changed, 194 insertions(+) create mode 100644 networking-odl/networking_odl/ml2/vpp_topology.py (limited to 'networking-odl/networking_odl/ml2/vpp_topology.py') 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) -- cgit 1.2.3-korg