]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
Add VIF binding extensions
authorGary Kotton <gkotton@redhat.com>
Sat, 10 Nov 2012 05:56:07 +0000 (05:56 +0000)
committerGary Kotton <gkotton@redhat.com>
Wed, 5 Dec 2012 09:01:15 +0000 (09:01 +0000)
The is part of the blueprint vif-plugging-improvements.

The patch adds an extension to Quantum that enables the plugin to
return VIF details.

At the moment it supports openvswitch and linuxbridge.

Change-Id: Ib9b4d34e668e2ddc61c152c2c4cd4a01f2d0de40

etc/policy.json
quantum/extensions/portbindings.py [new file with mode: 0644]
quantum/plugins/linuxbridge/lb_quantum_plugin.py
quantum/plugins/openvswitch/ovs_quantum_plugin.py
quantum/tests/unit/linuxbridge/test_linuxbridge_plugin.py
quantum/tests/unit/openvswitch/test_openvswitch_plugin.py

index d5641de42695adfb2cce3ef63821522307c3d14b..2961b3882417f4c9a4888cb33f542d23c299127c 100644 (file)
@@ -15,6 +15,9 @@
     "extension:router:add_router_interface": "rule:admin_or_owner",
     "extension:router:remove_router_interface": "rule:admin_or_owner",
 
+    "extension:port_binding:view": "rule:admin_only",
+    "extension:port_binding:set": "rule:admin_only",
+
     "subnets:private:read": "rule:admin_or_owner",
     "subnets:private:write": "rule:admin_or_owner",
     "subnets:shared:read": "rule:regular_user",
diff --git a/quantum/extensions/portbindings.py b/quantum/extensions/portbindings.py
new file mode 100644 (file)
index 0000000..e3276c8
--- /dev/null
@@ -0,0 +1,82 @@
+# Copyright (c) 2012 OpenStack, LLC.
+#
+# 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 quantum.api.v2 import attributes
+
+# The service will return the vif type for the specific port.
+VIF_TYPE = 'binding:vif_type'
+# In some cases different implementations may be run on different hosts.
+# The host on which the port will be allocated.
+HOST_ID = 'binding:host_id'
+# The profile will be a dictionary that enables the application running
+# on the specific host to pass and receive vif port specific information to
+# the plugin.
+PROFILE = 'binding:profile'
+
+VIF_TYPE_OVS = 'ovs'
+VIF_TYPE_BRIDGE = 'bridge'
+VIF_TYPE_802_QBG = '802.1qbg'
+VIF_TYPE_802_QBH = '802.1qbh'
+VIF_TYPE_OTHER = 'other'
+
+EXTENDED_ATTRIBUTES_2_0 = {
+    'ports': {
+        VIF_TYPE: {'allow_post': False, 'allow_put': False,
+                   'default': attributes.ATTR_NOT_SPECIFIED,
+                   'is_visible': True},
+        HOST_ID: {'allow_post': True, 'allow_put': True,
+                  'default': attributes.ATTR_NOT_SPECIFIED,
+                  'is_visible': True},
+        PROFILE: {'allow_post': True, 'allow_put': True,
+                  'default': attributes.ATTR_NOT_SPECIFIED,
+                  'is_visible': True},
+    }
+}
+
+
+class Portbindings(object):
+    """Extension class supporting port bindings.
+
+    This class is used by quantum's extension framework to make
+    metadata about the port bindings available to external applications.
+
+    With admin rights one will be able to update and read the values.
+    """
+
+    @classmethod
+    def get_name(cls):
+        return "Port Binding"
+
+    @classmethod
+    def get_alias(cls):
+        return "binding"
+
+    @classmethod
+    def get_description(cls):
+        return "Expose port bindings of a virtual port to external application"
+
+    @classmethod
+    def get_namespace(cls):
+        return "http://docs.openstack.org/ext/binding/api/v1.0"
+
+    @classmethod
+    def get_updated(cls):
+        return "2012-11-14T10:00:00-00:00"
+
+    def get_extended_resources(self, version):
+        if version == "2.0":
+            return EXTENDED_ATTRIBUTES_2_0
+        else:
+            return {}
index 711a059c2ea6629736e7ee18d3ce7cb7cbc13f0d..0a407752ef063362a6309e13dbe280396c159ad4 100644 (file)
@@ -25,6 +25,7 @@ from quantum.db import db_base_plugin_v2
 from quantum.db import dhcp_rpc_base
 from quantum.db import l3_db
 from quantum.db import l3_rpc_base
+from quantum.extensions import portbindings
 from quantum.extensions import providernet as provider
 from quantum.openstack.common import cfg
 from quantum.openstack.common import log as logging
@@ -143,6 +144,9 @@ class LinuxBridgePluginV2(db_base_plugin_v2.QuantumDbPluginV2,
     handled, by adding support for extended attributes to the
     QuantumDbPluginV2 base class. When that occurs, this class should
     be updated to take advantage of it.
+
+    The port binding extension enables an external application relay
+    information to and from the plugin.
     """
 
     # This attribute specifies whether the plugin supports or not
@@ -150,7 +154,12 @@ class LinuxBridgePluginV2(db_base_plugin_v2.QuantumDbPluginV2,
     # is qualified by class
     __native_bulk_support = True
 
-    supported_extension_aliases = ["provider", "router"]
+    supported_extension_aliases = ["provider", "router", "binding"]
+
+    network_view = "extension:provider_network:view"
+    network_set = "extension:provider_network:set"
+    binding_view = "extension:port_binding:view"
+    binding_set = "extension:port_binding:set"
 
     def __init__(self):
         db.initialize()
@@ -197,6 +206,12 @@ class LinuxBridgePluginV2(db_base_plugin_v2.QuantumDbPluginV2,
                 self._add_network(entry)
         LOG.debug("network VLAN ranges: %s" % self.network_vlan_ranges)
 
+    def _check_view_auth(self, context, resource, action):
+        return policy.check(context, action, resource)
+
+    def _enforce_set_auth(self, context, resource, action):
+        policy.enforce(context, action, resource)
+
     def _add_network_vlan_range(self, physical_network, vlan_min, vlan_max):
         self._add_network(physical_network)
         self.network_vlan_ranges[physical_network].append((vlan_min, vlan_max))
@@ -208,18 +223,8 @@ class LinuxBridgePluginV2(db_base_plugin_v2.QuantumDbPluginV2,
     # REVISIT(rkukura) Use core mechanism for attribute authorization
     # when available.
 
-    def _check_provider_view_auth(self, context, network):
-        return policy.check(context,
-                            "extension:provider_network:view",
-                            network)
-
-    def _enforce_provider_set_auth(self, context, network):
-        return policy.enforce(context,
-                              "extension:provider_network:set",
-                              network)
-
     def _extend_network_dict_provider(self, context, network):
-        if self._check_provider_view_auth(context, network):
+        if self._check_view_auth(context, network, self.network_view):
             binding = db.get_network_binding(context.session, network['id'])
             if binding.vlan_id == constants.FLAT_VLAN_ID:
                 network[provider.NETWORK_TYPE] = constants.TYPE_FLAT
@@ -248,7 +253,7 @@ class LinuxBridgePluginV2(db_base_plugin_v2.QuantumDbPluginV2,
             return (None, None, None)
 
         # Authorize before exposing plugin details to client
-        self._enforce_provider_set_auth(context, attrs)
+        self._enforce_set_auth(context, attrs, self.network_set)
 
         if not network_type_set:
             msg = _("provider:network_type required")
@@ -312,7 +317,7 @@ class LinuxBridgePluginV2(db_base_plugin_v2.QuantumDbPluginV2,
             return
 
         # Authorize before exposing plugin details to client
-        self._enforce_provider_set_auth(context, attrs)
+        self._enforce_set_auth(context, attrs, self.network_set)
 
         msg = _("plugin does not support updating provider attributes")
         raise q_exc.InvalidInput(error_message=msg)
@@ -392,6 +397,26 @@ class LinuxBridgePluginV2(db_base_plugin_v2.QuantumDbPluginV2,
 
         return [self._fields(net, fields) for net in nets]
 
+    def _extend_port_dict_binding(self, context, port):
+        if self._check_view_auth(context, port, self.binding_view):
+            port[portbindings.VIF_TYPE] = portbindings.VIF_TYPE_BRIDGE
+        return port
+
+    def create_port(self, context, port):
+        port = super(LinuxBridgePluginV2, self).create_port(context, port)
+        return self._extend_port_dict_binding(context, port)
+
+    def get_port(self, context, id, fields=None):
+        port = super(LinuxBridgePluginV2, self).get_port(context, id, fields)
+        return self._fields(self._extend_port_dict_binding(context, port),
+                            fields)
+
+    def get_ports(self, context, filters=None, fields=None):
+        ports = super(LinuxBridgePluginV2, self).get_ports(context, filters,
+                                                           fields)
+        return [self._fields(self._extend_port_dict_binding(context, port),
+                             fields) for port in ports]
+
     def update_port(self, context, id, port):
         original_port = super(LinuxBridgePluginV2, self).get_port(context,
                                                                   id)
@@ -402,7 +427,7 @@ class LinuxBridgePluginV2(db_base_plugin_v2.QuantumDbPluginV2,
             self.notifier.port_update(context, port,
                                       binding.physical_network,
                                       binding.vlan_id)
-        return port
+        return self._extend_port_dict_binding(context, port)
 
     def delete_port(self, context, id, l3_port_check=True):
 
index eea17377cb635d9c725942d14d2acd3ca54eb3c6..195bac2f8999dc889849c558704e04915166548f 100644 (file)
@@ -31,6 +31,7 @@ from quantum.db import db_base_plugin_v2
 from quantum.db import dhcp_rpc_base
 from quantum.db import l3_db
 from quantum.db import l3_rpc_base
+from quantum.extensions import portbindings
 from quantum.extensions import providernet as provider
 from quantum.openstack.common import cfg
 from quantum.openstack.common import log as logging
@@ -124,7 +125,7 @@ class OVSRpcCallbacks(dhcp_rpc_base.DhcpRpcCallbackMixin,
 
 
 class AgentNotifierApi(proxy.RpcProxy):
-    '''Agent side of the linux bridge rpc API.
+    '''Agent side of the openvswitch rpc API.
 
     API version history:
         1.0 - Initial version.
@@ -184,13 +185,21 @@ class OVSQuantumPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
     handled, by adding support for extended attributes to the
     QuantumDbPluginV2 base class. When that occurs, this class should
     be updated to take advantage of it.
+
+    The port binding extension enables an external application relay
+    information to and from the plugin.
     """
 
     # This attribute specifies whether the plugin supports or not
     # bulk operations. Name mangling is used in order to ensure it
     # is qualified by class
     __native_bulk_support = True
-    supported_extension_aliases = ["provider", "router"]
+    supported_extension_aliases = ["provider", "router", "binding"]
+
+    network_view = "extension:provider_network:view"
+    network_set = "extension:provider_network:set"
+    binding_view = "extension:port_binding:view"
+    binding_set = "extension:port_binding:set"
 
     def __init__(self, configfile=None):
         ovs_db_v2.initialize()
@@ -271,18 +280,14 @@ class OVSQuantumPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
     # TODO(rkukura) Use core mechanism for attribute authorization
     # when available.
 
-    def _check_provider_view_auth(self, context, network):
-        return policy.check(context,
-                            "extension:provider_network:view",
-                            network)
+    def _check_view_auth(self, context, resource, action):
+        return policy.check(context, action, resource)
 
-    def _enforce_provider_set_auth(self, context, network):
-        return policy.enforce(context,
-                              "extension:provider_network:set",
-                              network)
+    def _enforce_set_auth(self, context, resource, action):
+        policy.enforce(context, action, resource)
 
     def _extend_network_dict_provider(self, context, network):
-        if self._check_provider_view_auth(context, network):
+        if self._check_view_auth(context, network, self.network_view):
             binding = ovs_db_v2.get_network_binding(context.session,
                                                     network['id'])
             network[provider.NETWORK_TYPE] = binding.network_type
@@ -313,7 +318,7 @@ class OVSQuantumPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
             return (None, None, None)
 
         # Authorize before exposing plugin details to client
-        self._enforce_provider_set_auth(context, attrs)
+        self._enforce_set_auth(context, attrs, self.network_set)
 
         if not network_type_set:
             msg = _("provider:network_type required")
@@ -390,7 +395,7 @@ class OVSQuantumPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
             return
 
         # Authorize before exposing plugin details to client
-        self._enforce_provider_set_auth(context, attrs)
+        self._enforce_set_auth(context, attrs, self.network_set)
 
         msg = _("plugin does not support updating provider attributes")
         raise q_exc.InvalidInput(error_message=msg)
@@ -480,6 +485,26 @@ class OVSQuantumPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
 
         return [self._fields(net, fields) for net in nets]
 
+    def _extend_port_dict_binding(self, context, port):
+        if self._check_view_auth(context, port, self.binding_view):
+            port[portbindings.VIF_TYPE] = portbindings.VIF_TYPE_OVS
+        return port
+
+    def create_port(self, context, port):
+        port = super(OVSQuantumPluginV2, self).create_port(context, port)
+        return self._extend_port_dict_binding(context, port)
+
+    def get_port(self, context, id, fields=None):
+        port = super(OVSQuantumPluginV2, self).get_port(context, id, fields)
+        return self._fields(self._extend_port_dict_binding(context, port),
+                            fields)
+
+    def get_ports(self, context, filters=None, fields=None):
+        ports = super(OVSQuantumPluginV2, self).get_ports(context, filters,
+                                                          fields)
+        return [self._fields(self._extend_port_dict_binding(context, port),
+                             fields) for port in ports]
+
     def update_port(self, context, id, port):
         original_port = super(OVSQuantumPluginV2, self).get_port(context,
                                                                  id)
@@ -491,7 +516,7 @@ class OVSQuantumPluginV2(db_base_plugin_v2.QuantumDbPluginV2,
                                       binding.network_type,
                                       binding.segmentation_id,
                                       binding.physical_network)
-        return port
+        return self._extend_port_dict_binding(context, port)
 
     def delete_port(self, context, id, l3_port_check=True):
 
index 3a2c0b7c2d89d1a7b543b50ced8b6ccf2dae3f6f..dd510e7a3bbb337fc6e40ab48732e014f08a5cfb 100644 (file)
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+import contextlib
+
+from quantum import context
+from quantum.extensions import portbindings
+from quantum.manager import QuantumManager
+from quantum.openstack.common import cfg
 from quantum.tests.unit import test_db_plugin as test_plugin
 
 
@@ -37,7 +43,41 @@ class TestLinuxBridgeV2HTTPResponse(test_plugin.TestV2HTTPResponse,
 
 class TestLinuxBridgePortsV2(test_plugin.TestPortsV2,
                              LinuxBridgePluginV2TestCase):
-    pass
+    def test_port_vif_details(self):
+        plugin = QuantumManager.get_plugin()
+        with self.port(name='name') as port:
+            port_id = port['port']['id']
+            self.assertEqual(port['port']['binding:vif_type'],
+                             portbindings.VIF_TYPE_BRIDGE)
+            # By default user is admin - now test non admin user
+            ctx = context.Context(user_id=None,
+                                  tenant_id=self._tenant_id,
+                                  is_admin=False,
+                                  read_deleted="no")
+            non_admin_port = plugin.get_port(ctx, port_id)
+            self.assertTrue('status' in non_admin_port)
+            self.assertFalse('binding:vif_type' in non_admin_port)
+
+    def test_ports_vif_details(self):
+        cfg.CONF.set_default('allow_overlapping_ips', True)
+        plugin = QuantumManager.get_plugin()
+        with contextlib.nested(self.port(), self.port()) as (port1, port2):
+            ctx = context.get_admin_context()
+            ports = plugin.get_ports(ctx)
+            self.assertEqual(len(ports), 2)
+            for port in ports:
+                self.assertEqual(port['binding:vif_type'],
+                                 portbindings.VIF_TYPE_BRIDGE)
+            # By default user is admin - now test non admin user
+            ctx = context.Context(user_id=None,
+                                  tenant_id=self._tenant_id,
+                                  is_admin=False,
+                                  read_deleted="no")
+            ports = plugin.get_ports(ctx)
+            self.assertEqual(len(ports), 2)
+            for non_admin_port in ports:
+                self.assertTrue('status' in non_admin_port)
+                self.assertFalse('binding:vif_type' in non_admin_port)
 
 
 class TestLinuxBridgeNetworksV2(test_plugin.TestNetworksV2,
index 6727d9221035456215678032294ddfa6ffaf673d..49a10918196c4c2da4afbd282982435bd6050f51 100644 (file)
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+import contextlib
+
+from quantum import context
+from quantum.extensions import portbindings
+from quantum.manager import QuantumManager
+from quantum.openstack.common import cfg
 from quantum.tests.unit import test_db_plugin as test_plugin
 
 
@@ -37,7 +43,41 @@ class TestOpenvswitchV2HTTPResponse(test_plugin.TestV2HTTPResponse,
 
 class TestOpenvswitchPortsV2(test_plugin.TestPortsV2,
                              OpenvswitchPluginV2TestCase):
-    pass
+    def test_port_vif_details(self):
+        plugin = QuantumManager.get_plugin()
+        with self.port(name='name') as port:
+            port_id = port['port']['id']
+            self.assertEqual(port['port']['binding:vif_type'],
+                             portbindings.VIF_TYPE_OVS)
+            # By default user is admin - now test non admin user
+            ctx = context.Context(user_id=None,
+                                  tenant_id=self._tenant_id,
+                                  is_admin=False,
+                                  read_deleted="no")
+            non_admin_port = plugin.get_port(ctx, port_id)
+            self.assertTrue('status' in non_admin_port)
+            self.assertFalse('binding:vif_type' in non_admin_port)
+
+    def test_ports_vif_details(self):
+        cfg.CONF.set_default('allow_overlapping_ips', True)
+        plugin = QuantumManager.get_plugin()
+        with contextlib.nested(self.port(), self.port()) as (port1, port2):
+            ctx = context.get_admin_context()
+            ports = plugin.get_ports(ctx)
+            self.assertEqual(len(ports), 2)
+            for port in ports:
+                self.assertEqual(port['binding:vif_type'],
+                                 portbindings.VIF_TYPE_OVS)
+            # By default user is admin - now test non admin user
+            ctx = context.Context(user_id=None,
+                                  tenant_id=self._tenant_id,
+                                  is_admin=False,
+                                  read_deleted="no")
+            ports = plugin.get_ports(ctx)
+            self.assertEqual(len(ports), 2)
+            for non_admin_port in ports:
+                self.assertTrue('status' in non_admin_port)
+                self.assertFalse('binding:vif_type' in non_admin_port)
 
 
 class TestOpenvswitchNetworksV2(test_plugin.TestNetworksV2,