From c3b2c2a9a22bac5cf17813c589444d3abebaa23b Mon Sep 17 00:00:00 2001 From: Wojciech Dec Date: Tue, 16 Aug 2016 19:27:01 +0200 Subject: Adding Mitaka networking-old module with the ODL topology based port binding resolution mechanism from https://review.openstack.org/333186 Change-Id: I10d400aac9bb639c146527f0f93e6925cb74d9de Signed-off-by: Wojciech Dec --- .../networking_odl/ml2/ovsdb_topology.py | 218 +++++++++++++++++++++ 1 file changed, 218 insertions(+) create mode 100644 networking-odl/networking_odl/ml2/ovsdb_topology.py (limited to 'networking-odl/networking_odl/ml2/ovsdb_topology.py') diff --git a/networking-odl/networking_odl/ml2/ovsdb_topology.py b/networking-odl/networking_odl/ml2/ovsdb_topology.py new file mode 100644 index 0000000..f2c8ad8 --- /dev/null +++ b/networking-odl/networking_odl/ml2/ovsdb_topology.py @@ -0,0 +1,218 @@ +# 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 six.moves.urllib import parse + +from neutron.extensions import portbindings +from neutron.plugins.common import constants +from neutron.plugins.ml2 import driver_api +from neutron_lib import constants as n_const + +from networking_odl._i18n import _ +from networking_odl.ml2 import network_topology + + +LOG = log.getLogger(__name__) + + +class OvsdbNetworkTopologyParser(network_topology.NetworkTopologyParser): + + def new_element(self, uuid): + return OvsdbNetworkTopologyElement(uuid=uuid) + + def parse_network_topology(self, network_topologies): + elements_by_uuid = collections.OrderedDict() + for topology in network_topologies[ + 'network-topology']['topology']: + if topology['topology-id'].startswith('ovsdb:'): + for node in topology['node']: + # expected url format: ovsdb://uuid/[/]] + node_url = parse.urlparse(node['node-id']) + if node_url.scheme == 'ovsdb'\ + and node_url.netloc == 'uuid': + # split_res = ['', '', ''] + split_res = node_url.path.split('/', 2) + + # uuid is used to identify nodes referring to the same + # element + uuid = split_res[1] + element = elements_by_uuid.get(uuid) + if element is None: + elements_by_uuid[uuid] = element =\ + self.new_element(uuid) + + # inner_path can be [] or [] + inner_path = split_res[2:] + self._update_element_from_json_ovsdb_topology_node( + node, element, uuid, *inner_path) + + # There can be more OVS instances connected beside the same IP address + # Cache will yield more instaces for the same key + for __, element in six.iteritems(elements_by_uuid): + yield element + + def _update_element_from_json_ovsdb_topology_node( + self, node, element, uuid, path=None): + + if not path: + # global element section (root path) + + # fetch remote IP address + element.remote_ip = node["ovsdb:connection-info"]["remote-ip"] + + for vif_type_entry in node.get( + "ovsdb:interface-type-entry", []): + # Is this a good place to add others OVS VIF types? + if vif_type_entry.get("interface-type") ==\ + "ovsdb:interface-type-dpdkvhostuser": + element.support_vhost_user = True + break + else: + LOG.debug( + 'Interface type not found in network topology node %r.', + uuid) + + LOG.debug( + 'Topology element updated:\n' + ' - uuid: %(uuid)r\n' + ' - remote_ip: %(remote_ip)r\n' + ' - support_vhost_user: %(support_vhost_user)r', + {'uuid': uuid, + 'remote_ip': element.remote_ip, + 'support_vhost_user': element.support_vhost_user}) + elif path == 'bridge/br-int': + datapath_type = node.get("ovsdb:datapath-type") + if datapath_type == "ovsdb:datapath-type-netdev": + element.has_datapath_type_netdev = True + LOG.debug( + 'Topology element updated:\n' + ' - uuid: %(uuid)r\n' + ' - has_datapath_type_netdev: %(' + 'has_datapath_type_netdev)r', + {'uuid': uuid, + 'has_datapath_type_netdev': + element.has_datapath_type_netdev}) + + +class OvsdbNetworkTopologyElement(network_topology.NetworkTopologyElement): + + uuid = None + remote_ip = None # it can be None or a string + has_datapath_type_netdev = False # it can be False or True + support_vhost_user = False # it can be False or True + + # location for vhostuser sockets + vhostuser_socket_dir = '/var/run/openvswitch' + + # prefix for ovs port + port_prefix = 'vhu' + + 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): + if self.has_datapath_type_netdev and self.support_vhost_user: + return [ + portbindings.VIF_TYPE_VHOST_USER, + portbindings.VIF_TYPE_OVS] + else: + return [portbindings.VIF_TYPE_OVS] + + 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(OvsdbNetworkTopologyElement, self).to_dict() + data.update( + {'uuid': self.uuid, + '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] + + 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)[:14]) + + vif_details.update({ + portbindings.VHOST_USER_MODE: + portbindings.VHOST_USER_MODE_CLIENT, + portbindings.VHOST_USER_OVS_PLUG: True, + 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(OvsdbNetworkTopologyElement, self).__setattr__(name, value) -- cgit 1.2.3-korg