]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
Cisco plugin portbinding extension support
authorArvind Somya <asomya@cisco.com>
Thu, 29 Aug 2013 17:23:52 +0000 (13:23 -0400)
committerArvind Somya <asomya@cisco.com>
Thu, 19 Sep 2013 15:57:20 +0000 (11:57 -0400)
This commit adds portbinding extension support to the Cisco plugin.

Change-Id: I87554607860b040b693edeecc2706ca8edbe49b6
Fixes: Bug #1218033
neutron/plugins/cisco/models/virt_phy_sw_v2.py
neutron/tests/unit/cisco/test_network_plugin.py

index aea40f79e44530bcfff1049a9b89aba797250487..3e8fa2d6471135f028fe7d14a6ccc7d38393d545 100644 (file)
@@ -23,11 +23,11 @@ import inspect
 import logging
 import sys
 
-from novaclient.v1_1 import client as nova_client
 from oslo.config import cfg
 
 from neutron.api.v2 import attributes
 from neutron.db import api as db_api
+from neutron.extensions import portbindings
 from neutron.extensions import providernet as provider
 from neutron import neutron_plugin_base_v2
 from neutron.openstack.common import importutils
@@ -51,7 +51,7 @@ class VirtualPhysicalSwitchModelV2(neutron_plugin_base_v2.NeutronPluginBaseV2):
     """
     MANAGE_STATE = True
     __native_bulk_support = True
-    supported_extension_aliases = ["provider"]
+    supported_extension_aliases = ["provider", "binding"]
     _plugins = {}
     _methods_to_delegate = ['create_network_bulk',
                             'get_network', 'get_networks',
@@ -176,21 +176,6 @@ class VirtualPhysicalSwitchModelV2(neutron_plugin_base_v2.NeutronPluginBaseV2):
             raise cexc.NetworkSegmentIDNotFound(net_id=network_id)
         return binding_seg_id.segmentation_id
 
-    def _get_instance_host(self, tenant_id, instance_id):
-        keystone_conf = cfg.CONF.keystone_authtoken
-        keystone_auth_url = '%s://%s:%s/v2.0/' % (keystone_conf.auth_protocol,
-                                                  keystone_conf.auth_host,
-                                                  keystone_conf.auth_port)
-        nc = nova_client.Client(keystone_conf.admin_user,
-                                keystone_conf.admin_password,
-                                keystone_conf.admin_tenant_name,
-                                keystone_auth_url,
-                                no_cache=True)
-        serv = nc.servers.get(instance_id)
-        host = serv.__getattr__('OS-EXT-SRV-ATTR:host')
-
-        return host
-
     def _get_provider_vlan_id(self, network):
         if (all(attributes.is_attr_set(network.get(attr))
                 for attr in (provider.NETWORK_TYPE,
@@ -275,7 +260,7 @@ class VirtualPhysicalSwitchModelV2(neutron_plugin_base_v2.NeutronPluginBaseV2):
         pass
 
     def _invoke_nexus_for_net_create(self, context, tenant_id, net_id,
-                                     instance_id):
+                                     instance_id, host_id):
         if not self.config_nexus:
             return False
 
@@ -287,16 +272,30 @@ class VirtualPhysicalSwitchModelV2(neutron_plugin_base_v2.NeutronPluginBaseV2):
         attachment = {
             const.TENANT_ID: tenant_id,
             const.INSTANCE_ID: instance_id,
-            const.HOST_NAME: self._get_instance_host(tenant_id, instance_id),
+            const.HOST_NAME: host_id,
         }
         self._invoke_plugin_per_device(
             const.NEXUS_PLUGIN,
             'create_network',
             [network, attachment])
 
-    @staticmethod
-    def _should_call_create_net(device_owner, instance_id):
-        return (instance_id and device_owner != 'network:dhcp')
+    def _check_valid_port_device_owner(self, port):
+        """Check the port for valid device_owner.
+
+        Don't call the nexus plugin for router and dhcp
+        port owners.
+        """
+        return port['device_owner'].startswith('compute')
+
+    def _get_port_host_id_from_bindings(self, port):
+        """Get host_id from portbindings."""
+        host_id = None
+
+        if (portbindings.HOST_ID in port and
+            attributes.is_attr_set(port[portbindings.HOST_ID])):
+            host_id = port[portbindings.HOST_ID]
+
+        return host_id
 
     def create_port(self, context, port):
         """Create port.
@@ -309,16 +308,18 @@ class VirtualPhysicalSwitchModelV2(neutron_plugin_base_v2.NeutronPluginBaseV2):
         ovs_output = self._invoke_plugin_per_device(const.VSWITCH_PLUGIN,
                                                     self._func_name(),
                                                     args)
-        try:
-            instance_id = port['port']['device_id']
-            device_owner = port['port']['device_owner']
+        instance_id = port['port']['device_id']
 
-            if self._should_call_create_net(device_owner, instance_id):
+        # Only call nexus plugin if there's a valid instance_id, host_id
+        # and device_owner
+        try:
+            host_id = self._get_port_host_id_from_bindings(port['port'])
+            if (instance_id and host_id and
+                self._check_valid_port_device_owner(port['port'])):
                 net_id = port['port']['network_id']
                 tenant_id = port['port']['tenant_id']
                 self._invoke_nexus_for_net_create(
-                    context, tenant_id, net_id, instance_id)
-
+                    context, tenant_id, net_id, instance_id, host_id)
         except Exception:
             # Create network on the Nexus plugin has failed, so we need
             # to rollback the port creation on the VSwitch plugin.
@@ -356,17 +357,19 @@ class VirtualPhysicalSwitchModelV2(neutron_plugin_base_v2.NeutronPluginBaseV2):
         ovs_output = self._invoke_plugin_per_device(const.VSWITCH_PLUGIN,
                                                     self._func_name(),
                                                     args)
-        try:
-            net_id = old_port['network_id']
-            instance_id = ''
-            if 'device_id' in port['port']:
-                instance_id = port['port']['device_id']
+        net_id = old_port['network_id']
+        instance_id = ''
+        if 'device_id' in port['port']:
+            instance_id = port['port']['device_id']
 
-            # Check if there's a new device_id
-            if instance_id and not old_device:
+        # Check if there's a new device_id
+        try:
+            host_id = self._get_port_host_id_from_bindings(port['port'])
+            if (instance_id and not old_device and host_id and
+                self._check_valid_port_device_owner(port['port'])):
                 tenant_id = old_port['tenant_id']
                 self._invoke_nexus_for_net_create(
-                    context, tenant_id, net_id, instance_id)
+                    context, tenant_id, net_id, instance_id, host_id)
 
             return ovs_output[0]
         except Exception:
@@ -392,8 +395,11 @@ class VirtualPhysicalSwitchModelV2(neutron_plugin_base_v2.NeutronPluginBaseV2):
         """
         LOG.debug(_("delete_port() called"))
         port = self.get_port(context, id)
-        exclude_list = ('', 'compute:none', 'network:dhcp')
-        if self.config_nexus and port['device_owner'] not in exclude_list:
+
+        host_id = self._get_port_host_id_from_bindings(port)
+
+        if (self.config_nexus and host_id and
+            self._check_valid_port_device_owner(port)):
             vlan_id = self._get_segmentation_id(port['network_id'])
             n_args = [port['device_id'], vlan_id]
             self._invoke_plugin_per_device(const.NEXUS_PLUGIN,
@@ -411,8 +417,9 @@ class VirtualPhysicalSwitchModelV2(neutron_plugin_base_v2.NeutronPluginBaseV2):
                 tenant_id = port['tenant_id']
                 net_id = port['network_id']
                 instance_id = port['device_id']
-                self._invoke_nexus_for_net_create(context, tenant_id,
-                                                  net_id, instance_id)
+                host_id = port[portbindings.HOST_ID]
+                self._invoke_nexus_for_net_create(context, tenant_id, net_id,
+                                                  instance_id, host_id)
             finally:
                 # Raise the original exception.
                 raise exc_info[0], exc_info[1], exc_info[2]
index 656f0086126e37423a0fa29ca0b70aecbe3ac342..b336d40f6e8d75c5423a0a5c04f18e34197756c7 100644 (file)
@@ -26,6 +26,7 @@ from neutron.common import exceptions as q_exc
 from neutron import context
 from neutron.db import db_base_plugin_v2 as base_plugin
 from neutron.db import l3_db
+from neutron.extensions import portbindings
 from neutron.extensions import providernet as provider
 from neutron.manager import NeutronManager
 from neutron.plugins.cisco.common import cisco_constants as const
@@ -35,6 +36,7 @@ from neutron.plugins.cisco.db import nexus_db_v2
 from neutron.plugins.cisco.models import virt_phy_sw_v2
 from neutron.plugins.openvswitch.common import config as ovs_config
 from neutron.plugins.openvswitch import ovs_db_v2
+from neutron.tests.unit import _test_extension_portbindings as test_bindings
 from neutron.tests.unit import test_db_plugin
 
 LOG = logging.getLogger(__name__)
@@ -83,7 +85,8 @@ class TestCiscoV2HTTPResponse(CiscoNetworkPluginV2TestCase,
 
 
 class TestCiscoPortsV2(CiscoNetworkPluginV2TestCase,
-                       test_db_plugin.TestPortsV2):
+                       test_db_plugin.TestPortsV2,
+                       test_bindings.PortBindingsHostTestCaseMixin):
 
     def setUp(self):
         """Configure for end-to-end neutron testing using a mock ncclient.
@@ -135,16 +138,6 @@ class TestCiscoPortsV2(CiscoNetworkPluginV2TestCase,
         }
         mock.patch.dict(cisco_config.device_dictionary, nexus_config).start()
 
-        patches = {
-            '_should_call_create_net': True,
-            '_get_instance_host': 'testhost'
-        }
-        for func in patches:
-            mock_sw = mock.patch.object(
-                virt_phy_sw_v2.VirtualPhysicalSwitchModelV2,
-                func).start()
-            mock_sw.return_value = patches[func]
-
         super(TestCiscoPortsV2, self).setUp()
 
     @contextlib.contextmanager
@@ -169,7 +162,7 @@ class TestCiscoPortsV2(CiscoNetworkPluginV2TestCase,
 
     @contextlib.contextmanager
     def _create_port_res(self, name='myname', cidr='1.0.0.0/24',
-                         do_delete=True):
+                         do_delete=True, host_id='testhost'):
         """Create a network, subnet, and port and yield the result.
 
         Create a network, subnet, and port, yield the result,
@@ -181,12 +174,16 @@ class TestCiscoPortsV2(CiscoNetworkPluginV2TestCase,
                           end of testing
 
         """
+        ctx = context.get_admin_context()
         with self.network(name=name) as network:
             with self.subnet(network=network, cidr=cidr) as subnet:
                 net_id = subnet['subnet']['network_id']
-                res = self._create_port(self.fmt, net_id,
-                                        device_id='testdev',
-                                        device_owner='testowner')
+                args = (portbindings.HOST_ID, 'device_id', 'device_owner')
+                port_dict = {portbindings.HOST_ID: host_id,
+                             'device_id': 'testdev',
+                             'device_owner': 'compute:None'}
+                res = self._create_port(self.fmt, net_id, arg_list=args,
+                                        context=ctx, **port_dict)
                 port = self.deserialize(self.fmt, res)
                 try:
                     yield res
@@ -405,12 +402,9 @@ class TestCiscoPortsV2(CiscoNetworkPluginV2TestCase,
         a fictitious host name during port creation.
 
         """
-        with mock.patch.object(virt_phy_sw_v2.VirtualPhysicalSwitchModelV2,
-                               '_get_instance_host') as mock_get_instance:
-            mock_get_instance.return_value = 'fictitious_host'
-            with self._create_port_res(do_delete=False) as res:
-                self._assertExpectedHTTP(res.status_int,
-                                         c_exc.NexusComputeHostNotConfigured)
+        with self._create_port_res(do_delete=False, host_id='fakehost') as res:
+            self._assertExpectedHTTP(res.status_int,
+                                     c_exc.NexusComputeHostNotConfigured)
 
     def test_nexus_bind_fail_rollback(self):
         """Test for proper rollback following add Nexus DB binding failure.
@@ -450,7 +444,8 @@ class TestCiscoPortsV2(CiscoNetworkPluginV2TestCase,
                 device_id = "00fff4d0-e4a8-4a3a-8906-4c4cdafb59f1"
                 if orig_port['port']['device_id'] == device_id:
                     device_id = "600df00d-e4a8-4a3a-8906-feed600df00d"
-                data = {'port': {'device_id': device_id}}
+                data = {'port': {'device_id': device_id,
+                                 portbindings.HOST_ID: 'testhost'}}
                 port_id = orig_port['port']['id']
                 req = self.new_update_request('ports', data, port_id)
                 res = req.get_response(self.api)