summaryrefslogtreecommitdiffstats
path: root/networking-odl/networking_odl/ml2/ovsdb_topology.py
diff options
context:
space:
mode:
Diffstat (limited to 'networking-odl/networking_odl/ml2/ovsdb_topology.py')
-rw-r--r--networking-odl/networking_odl/ml2/ovsdb_topology.py218
1 files changed, 218 insertions, 0 deletions
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/<uuid>[/<path>]]
+ node_url = parse.urlparse(node['node-id'])
+ if node_url.scheme == 'ovsdb'\
+ and node_url.netloc == 'uuid':
+ # split_res = ['', '<uuid>', '<path>']
+ 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 [<path>]
+ 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)