From 8e2812eeaafb9f3a7e4142f1febe8ab855a9e596 Mon Sep 17 00:00:00 2001 From: Sumit Naiksatam Date: Sat, 18 Aug 2012 20:50:38 -0700 Subject: [PATCH] Fixing unit test failures in Cisco plugin Bug #1038565 The tests for bulk resource creation failure were failing since the bulk API calls were not getting relayed correctly to the virt_phy_sw.py, that has been fixed There tests for the network_multi_blade_v2.py were failing since DB initialization order was incorrect. The tests in test_api_v2.py were failing since the plugin instance was not being reset for each test. Change-Id: I449bd8a61c1f61f98a97acf489574624fe309caa --- .../cisco/models/network_multi_blade_v2.py | 3 + .../plugins/cisco/models/virt_phy_sw_v2.py | 67 ++++++- quantum/plugins/cisco/network_plugin.py | 23 ++- .../cisco/tests/unit/api-paste.ini.cisco.test | 2 +- .../tests/unit/v2/api-paste.ini.cisco.test | 8 + .../tests/unit/v2/nexus/fake_nexus_driver.py | 2 +- .../tests/unit/v2/quantumv2.conf.cisco.test | 25 ++- .../cisco/tests/unit/v2/test_api_v2.py | 6 + .../tests/unit/v2/test_network_plugin.py | 179 +++++++++++++++++- .../plugins/cisco/ucs/cisco_ucs_plugin_v2.py | 2 +- 10 files changed, 290 insertions(+), 27 deletions(-) create mode 100644 quantum/plugins/cisco/tests/unit/v2/api-paste.ini.cisco.test diff --git a/quantum/plugins/cisco/models/network_multi_blade_v2.py b/quantum/plugins/cisco/models/network_multi_blade_v2.py index e8f19eda5..83399ee19 100644 --- a/quantum/plugins/cisco/models/network_multi_blade_v2.py +++ b/quantum/plugins/cisco/models/network_multi_blade_v2.py @@ -22,6 +22,7 @@ import logging from quantum.openstack.common import importutils from quantum.plugins.cisco.common import cisco_constants as const +from quantum.plugins.cisco.common import cisco_credentials_v2 as cred from quantum.plugins.cisco.db import network_db_v2 as cdb from quantum.plugins.cisco import l2network_plugin_configuration as conf from quantum import quantum_plugin_base_v2 @@ -46,6 +47,8 @@ class NetworkMultiBladeV2(quantum_plugin_base_v2.QuantumPluginBaseV2): configured, and load the inventories those device plugins for which the inventory is configured """ + cdb.initialize() + cred.Store.initialize() self._vlan_mgr = importutils.import_object(conf.MANAGER_CLASS) for key in conf.PLUGINS[const.PLUGINS].keys(): plugin_obj = conf.PLUGINS[const.PLUGINS][key] diff --git a/quantum/plugins/cisco/models/virt_phy_sw_v2.py b/quantum/plugins/cisco/models/virt_phy_sw_v2.py index 653d39bde..2d0b14fe9 100644 --- a/quantum/plugins/cisco/models/virt_phy_sw_v2.py +++ b/quantum/plugins/cisco/models/virt_phy_sw_v2.py @@ -41,14 +41,16 @@ class VirtualPhysicalSwitchModelV2(quantum_plugin_base_v2.QuantumPluginBaseV2): One or more servers to a nexus switch. """ MANAGE_STATE = True + __native_bulk_support = True supported_extension_aliases = [] _plugins = {} _inventory = {} _methods_to_delegate = ['update_network', 'get_network', 'get_networks', - 'create_port', 'delete_port', 'update_port', - 'get_port', 'get_ports', - 'create_subnet', 'delete_subnet', 'update_subnet', - 'get_subnet', 'get_subnets'] + 'create_port', 'create_port_bulk', 'delete_port', + 'update_port', 'get_port', 'get_ports', + 'create_subnet', 'create_subnet_bulk', + 'delete_subnet', 'update_subnet', 'get_subnet', + 'get_subnets', ] def __init__(self): """ @@ -69,15 +71,32 @@ class VirtualPhysicalSwitchModelV2(quantum_plugin_base_v2.QuantumPluginBaseV2): LOG.debug("Loaded device inventory %s\n" % conf.PLUGINS[const.INVENTORY][key]) + if hasattr(self._plugins[const.VSWITCH_PLUGIN], + "supported_extension_aliases"): + self.supported_extension_aliases.extend( + self._plugins[const.VSWITCH_PLUGIN]. + supported_extension_aliases) + LOG.debug("%s.%s init done" % (__name__, self.__class__.__name__)) def __getattribute__(self, name): - methods = object.__getattribute__(self, "_methods_to_delegate") + """ + This delegates the calls to the methods implemented only by the OVS + sub-plugin. + """ + super_getattr = super(VirtualPhysicalSwitchModelV2, + self).__getattribute__ + methods = super_getattr('_methods_to_delegate') + if name in methods: - return getattr(object.__getattribute__(self, "_plugins") - [const.VSWITCH_PLUGIN], name) - else: - return object.__getattribute__(self, name) + plugin = super_getattr('_plugins')[const.VSWITCH_PLUGIN] + return getattr(plugin, name) + + try: + return super_getattr(name) + except AttributeError: + plugin = super_getattr('_plugins')[const.VSWITCH_PLUGIN] + return getattr(plugin, name) def _func_name(self, offset=0): """Get the name of the calling function""" @@ -173,6 +192,36 @@ class VirtualPhysicalSwitchModelV2(quantum_plugin_base_v2.QuantumPluginBaseV2): # TODO (Sumit): Check if we need to perform any rollback here raise + def create_network_bulk(self, context, networks): + """ + Perform this operation in the context of the configured device + plugins. + """ + LOG.debug("create_network_bulk() called\n") + try: + args = [context, networks] + ovs_output = self._plugins[ + const.VSWITCH_PLUGIN].create_network_bulk(context, networks) + vlan_ids = odb.get_vlans() + vlanids = '' + for v_id in vlan_ids: + vlanids = str(v_id[0]) + ',' + vlanids + vlanids = vlanids.strip(',') + LOG.debug("ovs_output: %s\n " % ovs_output) + ovs_networks = ovs_output + for ovs_network in ovs_networks: + vlan_id = odb.get_vlan(ovs_network['id']) + vlan_name = conf.VLAN_NAME_PREFIX + str(vlan_id) + args = [ovs_network['tenant_id'], ovs_network['name'], + ovs_network['id'], vlan_name, vlan_id, + {'vlan_ids':vlanids}] + nexus_output = self._invoke_plugin_per_device( + const.NEXUS_PLUGIN, "create_network", args) + return ovs_output + except: + # TODO (Sumit): Check if we need to perform any rollback here + raise + def update_network(self, context, id, network): """For this model this method will be delegated to vswitch plugin""" pass diff --git a/quantum/plugins/cisco/network_plugin.py b/quantum/plugins/cisco/network_plugin.py index 9339d2246..38fe98ddd 100644 --- a/quantum/plugins/cisco/network_plugin.py +++ b/quantum/plugins/cisco/network_plugin.py @@ -40,25 +40,28 @@ class PluginV2(db_base_plugin_v2.QuantumDbPluginV2): supported_extension_aliases = ["Cisco Credential", "Cisco Port Profile", "Cisco qos", "Cisco Nova Tenant", "Cisco Multiport"] - _methods_to_delegate = ['create_network', 'delete_network', - 'update_network', 'get_network', 'get_networks', - 'create_port', 'delete_port', 'update_port', - 'get_port', 'get_ports', - 'create_subnet', 'delete_subnet', 'update_subnet', - 'get_subnet', 'get_subnets'] + _methods_to_delegate = ['create_network', 'create_network_bulk', + 'delete_network', 'update_network', 'get_network', + 'get_networks', + 'create_port', 'create_port_bulk', 'delete_port', + 'update_port', 'get_port', 'get_ports', + 'create_subnet', 'create_subnet_bulk', + 'delete_subnet', 'update_subnet', + 'get_subnet', 'get_subnets', ] _master = True def __init__(self): """ - Loads the model class, initializes the DB, and credential store. + Loads the model class. """ self._model = importutils.import_object(conf.MODEL_CLASS) if hasattr(self._model, "MANAGE_STATE") and self._model.MANAGE_STATE: self._master = False LOG.debug("Model %s manages state" % conf.MODEL_CLASS) - else: - cdb.initialize() - cred.Store.initialize() + native_bulk_attr_name = ("_%s__native_bulk_support" + % self._model.__class__.__name__) + self.__native_bulk_support = getattr(self._model, + native_bulk_attr_name, False) if hasattr(self._model, "supported_extension_aliases"): self.supported_extension_aliases.extend( diff --git a/quantum/plugins/cisco/tests/unit/api-paste.ini.cisco.test b/quantum/plugins/cisco/tests/unit/api-paste.ini.cisco.test index 9163752c5..f732fc917 100644 --- a/quantum/plugins/cisco/tests/unit/api-paste.ini.cisco.test +++ b/quantum/plugins/cisco/tests/unit/api-paste.ini.cisco.test @@ -2,7 +2,7 @@ pipeline = extensions extensions_test_app [filter:extensions] -paste.filter_factory = quantum.extensions.extensions:plugin_aware_extension_middleware_factory +paste.filter_factory = quantum.common.extensions:plugin_aware_extension_middleware_factory [app:extensions_test_app] paste.app_factory = quantum.plugins.cisco.tests.unit.test_cisco_extension:app_factory diff --git a/quantum/plugins/cisco/tests/unit/v2/api-paste.ini.cisco.test b/quantum/plugins/cisco/tests/unit/v2/api-paste.ini.cisco.test new file mode 100644 index 000000000..f732fc917 --- /dev/null +++ b/quantum/plugins/cisco/tests/unit/v2/api-paste.ini.cisco.test @@ -0,0 +1,8 @@ +[pipeline:extensions_app_with_filter] +pipeline = extensions extensions_test_app + +[filter:extensions] +paste.filter_factory = quantum.common.extensions:plugin_aware_extension_middleware_factory + +[app:extensions_test_app] +paste.app_factory = quantum.plugins.cisco.tests.unit.test_cisco_extension:app_factory diff --git a/quantum/plugins/cisco/tests/unit/v2/nexus/fake_nexus_driver.py b/quantum/plugins/cisco/tests/unit/v2/nexus/fake_nexus_driver.py index 2180ab2f1..206a2cc03 100644 --- a/quantum/plugins/cisco/tests/unit/v2/nexus/fake_nexus_driver.py +++ b/quantum/plugins/cisco/tests/unit/v2/nexus/fake_nexus_driver.py @@ -77,7 +77,7 @@ class CiscoNEXUSFakeDriver(): pass def create_vlan(self, vlan_name, vlan_id, nexus_host, nexus_user, - nexus_password, nexus_ports, nexus_ssh_port): + nexus_password, nexus_ports, nexus_ssh_port, vlan_ids): """ Creates a VLAN and Enable on trunk mode an interface on Nexus Switch given the VLAN ID and Name and Interface Number diff --git a/quantum/plugins/cisco/tests/unit/v2/quantumv2.conf.cisco.test b/quantum/plugins/cisco/tests/unit/v2/quantumv2.conf.cisco.test index 89ff3e642..56511a739 100644 --- a/quantum/plugins/cisco/tests/unit/v2/quantumv2.conf.cisco.test +++ b/quantum/plugins/cisco/tests/unit/v2/quantumv2.conf.cisco.test @@ -12,9 +12,32 @@ bind_host = 0.0.0.0 bind_port = 9696 # Path to the extensions -api_extensions_path = ../../../../extensions +api_extensions_path = ../../../../../extensions # Paste configuration file api_paste_config = api-paste.ini.cisco.test core_plugin = quantum.plugins.cisco.network_plugin.PluginV2 + +# The messaging module to use, defaults to kombu. +rpc_backend = quantum.openstack.common.rpc.impl_fake + +[QUOTAS] +# resource name(s) that are supported in quota features +quota_items = network,subnet,port + +# default number of resource allowed per tenant, minus for unlimited +default_quota = -1 + +# number of networks allowed per tenant, and minus means unlimited +# quota_network = 10 + +# number of subnets allowed per tenant, and minus means unlimited +# quota_subnet = 10 + +# number of ports allowed per tenant, and minus means unlimited +# quota_port = 50 + +# default driver to use for quota checks +# quota_driver = quantum.quota.ConfDriver +quota_driver = quantum.extensions._quotav2_driver.DbQuotaDriver diff --git a/quantum/plugins/cisco/tests/unit/v2/test_api_v2.py b/quantum/plugins/cisco/tests/unit/v2/test_api_v2.py index 43d81f2ba..c20427de7 100644 --- a/quantum/plugins/cisco/tests/unit/v2/test_api_v2.py +++ b/quantum/plugins/cisco/tests/unit/v2/test_api_v2.py @@ -20,6 +20,8 @@ import webtest from quantum.api.v2 import router from quantum.common import config +from quantum.extensions.extensions import PluginAwareExtensionManager +from quantum.manager import QuantumManager from quantum.openstack.common import cfg from quantum.tests.unit import test_api_v2 @@ -35,6 +37,10 @@ class APIv2TestCase(test_api_v2.APIv2TestCase): def setUp(self): plugin = 'quantum.plugins.cisco.network_plugin.PluginV2' + # Ensure 'stale' patched copies of the plugin are never returned + QuantumManager._instance = None + # Ensure existing ExtensionManager is not used + PluginAwareExtensionManager._instance = None # Create the default configurations args = ['--config-file', curdir('quantumv2.conf.cisco.test')] config.parse(args=args) diff --git a/quantum/plugins/cisco/tests/unit/v2/test_network_plugin.py b/quantum/plugins/cisco/tests/unit/v2/test_network_plugin.py index 89a4fa801..ccfd8e8b8 100644 --- a/quantum/plugins/cisco/tests/unit/v2/test_network_plugin.py +++ b/quantum/plugins/cisco/tests/unit/v2/test_network_plugin.py @@ -20,8 +20,13 @@ import os from quantum.api.v2.router import APIRouter from quantum.common import config +from quantum.common.test_lib import test_config +from quantum import context from quantum.db import api as db +from quantum.extensions import _quotav2_model as quotav2_model from quantum.manager import QuantumManager +from quantum.plugins.cisco.common import cisco_constants as const +from quantum.plugins.cisco.db import network_db_v2 from quantum.plugins.cisco.db import network_models_v2 from quantum.openstack.common import cfg from quantum.tests.unit import test_db_plugin @@ -50,11 +55,35 @@ class NetworkPluginV2TestCase(test_db_plugin.QuantumDbPluginV2TestCase): plugin = 'quantum.plugins.cisco.network_plugin.PluginV2' # Create the default configurations args = ['--config-file', curdir('quantumv2.conf.cisco.test')] + # If test_config specifies some config-file, use it, as well + for config_file in test_config.get('config_files', []): + args.extend(['--config-file', config_file]) config.parse(args=args) # Update the plugin cfg.CONF.set_override('core_plugin', plugin) cfg.CONF.set_override('base_mac', "12:34:56:78:90:ab") - self.api = APIRouter() + cfg.CONF.max_dns_nameservers = 2 + cfg.CONF.max_subnet_host_routes = 2 + + def new_init(): + db.configure_db({'sql_connection': 'sqlite://', + 'base': network_models_v2.model_base.BASEV2}) + + with mock.patch.object(network_db_v2, + 'initialize', new=new_init): + self.api = APIRouter() + + def _is_native_bulk_supported(): + plugin_obj = QuantumManager.get_plugin() + native_bulk_attr_name = ("_%s__native_bulk_support" + % plugin_obj.__class__.__name__) + return getattr(plugin_obj, native_bulk_attr_name, False) + + self._skip_native_bulk = not _is_native_bulk_supported() + + ext_mgr = test_config.get('extension_manager', None) + if ext_mgr: + self.ext_api = test_extensions.setup_extensions_middleware(ext_mgr) LOG.debug("%s.%s.%s done" % (__name__, self.__class__.__name__, inspect.stack()[0][3])) @@ -65,6 +94,16 @@ class NetworkPluginV2TestCase(test_db_plugin.QuantumDbPluginV2TestCase): cfg.CONF.reset() + def _get_plugin_ref(self): + plugin_obj = QuantumManager.get_plugin() + if getattr(plugin_obj, "_master"): + plugin_ref = plugin_obj + else: + plugin_ref = getattr(plugin_obj, "_model").\ + _plugins[const.VSWITCH_PLUGIN] + + return plugin_ref + class TestV2HTTPResponse(NetworkPluginV2TestCase, test_db_plugin.TestV2HTTPResponse): @@ -74,14 +113,146 @@ class TestV2HTTPResponse(NetworkPluginV2TestCase, class TestPortsV2(NetworkPluginV2TestCase, test_db_plugin.TestPortsV2): - pass + def test_create_ports_bulk_emulated_plugin_failure(self): + real_has_attr = hasattr + + #ensures the API choose the emulation code path + def fakehasattr(item, attr): + if attr.endswith('__native_bulk_support'): + return False + return real_has_attr(item, attr) + + with mock.patch('__builtin__.hasattr', + new=fakehasattr): + plugin_ref = self._get_plugin_ref() + orig = plugin_ref.create_port + with mock.patch.object(plugin_ref, + 'create_port') as patched_plugin: + + def side_effect(*args, **kwargs): + return self._do_side_effect(patched_plugin, orig, + *args, **kwargs) + + patched_plugin.side_effect = side_effect + with self.network() as net: + res = self._create_port_bulk('json', 2, + net['network']['id'], + 'test', + True) + # We expect a 500 as we injected a fault in the plugin + self._validate_behavior_on_bulk_failure(res, 'ports') + + def test_create_ports_bulk_native_plugin_failure(self): + if self._skip_native_bulk: + self.skipTest("Plugin does not support native bulk port create") + ctx = context.get_admin_context() + with self.network() as net: + plugin_ref = self._get_plugin_ref() + orig = plugin_ref.create_port + with mock.patch.object(plugin_ref, + 'create_port') as patched_plugin: + + def side_effect(*args, **kwargs): + return self._do_side_effect(patched_plugin, orig, + *args, **kwargs) + + patched_plugin.side_effect = side_effect + res = self._create_port_bulk('json', 2, net['network']['id'], + 'test', True, context=ctx) + # We expect a 500 as we injected a fault in the plugin + self._validate_behavior_on_bulk_failure(res, 'ports') class TestNetworksV2(NetworkPluginV2TestCase, test_db_plugin.TestNetworksV2): - pass + def test_create_networks_bulk_emulated_plugin_failure(self): + real_has_attr = hasattr + + def fakehasattr(item, attr): + if attr.endswith('__native_bulk_support'): + return False + return real_has_attr(item, attr) + + plugin_ref = self._get_plugin_ref() + orig = plugin_ref.create_network + #ensures the API choose the emulation code path + with mock.patch('__builtin__.hasattr', + new=fakehasattr): + with mock.patch.object(plugin_ref, + 'create_network') as patched_plugin: + def side_effect(*args, **kwargs): + return self._do_side_effect(patched_plugin, orig, + *args, **kwargs) + patched_plugin.side_effect = side_effect + res = self._create_network_bulk('json', 2, 'test', True) + LOG.debug("response is %s" % res) + # We expect a 500 as we injected a fault in the plugin + self._validate_behavior_on_bulk_failure(res, 'networks') + + def test_create_networks_bulk_native_plugin_failure(self): + if self._skip_native_bulk: + self.skipTest("Plugin does not support native bulk network create") + plugin_ref = self._get_plugin_ref() + orig = plugin_ref.create_network + with mock.patch.object(plugin_ref, + 'create_network') as patched_plugin: + + def side_effect(*args, **kwargs): + return self._do_side_effect(patched_plugin, orig, + *args, **kwargs) + + patched_plugin.side_effect = side_effect + res = self._create_network_bulk('json', 2, 'test', True) + # We expect a 500 as we injected a fault in the plugin + self._validate_behavior_on_bulk_failure(res, 'networks') class TestSubnetsV2(NetworkPluginV2TestCase, test_db_plugin.TestSubnetsV2): - pass + def test_create_subnets_bulk_emulated_plugin_failure(self): + real_has_attr = hasattr + + #ensures the API choose the emulation code path + def fakehasattr(item, attr): + if attr.endswith('__native_bulk_support'): + return False + return real_has_attr(item, attr) + + with mock.patch('__builtin__.hasattr', + new=fakehasattr): + plugin_ref = self._get_plugin_ref() + orig = plugin_ref.create_subnet + with mock.patch.object(plugin_ref, + 'create_subnet') as patched_plugin: + + def side_effect(*args, **kwargs): + self._do_side_effect(patched_plugin, orig, + *args, **kwargs) + + patched_plugin.side_effect = side_effect + with self.network() as net: + res = self._create_subnet_bulk('json', 2, + net['network']['id'], + 'test') + # We expect a 500 as we injected a fault in the plugin + self._validate_behavior_on_bulk_failure(res, 'subnets') + + def test_create_subnets_bulk_native_plugin_failure(self): + if self._skip_native_bulk: + self.skipTest("Plugin does not support native bulk subnet create") + plugin_ref = self._get_plugin_ref() + orig = plugin_ref.create_subnet + with mock.patch.object(plugin_ref, + 'create_subnet') as patched_plugin: + def side_effect(*args, **kwargs): + return self._do_side_effect(patched_plugin, orig, + *args, **kwargs) + + patched_plugin.side_effect = side_effect + with self.network() as net: + res = self._create_subnet_bulk('json', 2, + net['network']['id'], + 'test') + + # We expect a 500 as we injected a fault in the plugin + self._validate_behavior_on_bulk_failure(res, 'subnets') diff --git a/quantum/plugins/cisco/ucs/cisco_ucs_plugin_v2.py b/quantum/plugins/cisco/ucs/cisco_ucs_plugin_v2.py index b831c1c6e..0de87e70c 100644 --- a/quantum/plugins/cisco/ucs/cisco_ucs_plugin_v2.py +++ b/quantum/plugins/cisco/ucs/cisco_ucs_plugin_v2.py @@ -23,7 +23,7 @@ from quantum.db import api as db from quantum.openstack.common import importutils from quantum.plugins.cisco.common import cisco_constants as const -from quantum.plugins.cisco.common import cisco_credentials as cred +from quantum.plugins.cisco.common import cisco_credentials_v2 as cred from quantum.plugins.cisco.common import cisco_exceptions as cexc from quantum.plugins.cisco.common import cisco_utils as cutil from quantum.plugins.cisco.db import network_db_v2 as cdb -- 2.45.2