From: Dhanashree Gosavi Date: Mon, 10 Feb 2014 12:19:50 +0000 (-0800) Subject: Remove List events API from Cisco N1kv Neutron X-Git-Url: https://review.fuel-infra.org/gitweb?a=commitdiff_plain;h=ed1ada6434a04f93e07982796269549a6537801b;p=openstack-build%2Fneutron-build.git Remove List events API from Cisco N1kv Neutron Earlier Cisco N1kv plugin was using list events api to poll policies from VSM. It was inefficient and caused delay in processing. So, now Cisco N1kv plugin switched to list profiles to poll policies from VSM. Change-Id: Ia734735361dc3eaee8e276ada0c80045eaa9ef96 Closes-Bug: #1292173 (cherry picked from commit e0f69d69293f0ffba22a6540f483f05baa48cd6f) --- diff --git a/neutron/plugins/cisco/db/n1kv_db_v2.py b/neutron/plugins/cisco/db/n1kv_db_v2.py index 5c237115c..be54fc097 100644 --- a/neutron/plugins/cisco/db/n1kv_db_v2.py +++ b/neutron/plugins/cisco/db/n1kv_db_v2.py @@ -850,6 +850,13 @@ def get_policy_profile(db_session, id): raise c_exc.PolicyProfileIdNotFound(profile_id=id) +def get_policy_profiles(): + """Retrieve all policy profiles.""" + db_session = db.get_session() + with db_session.begin(subtransactions=True): + return db_session.query(n1kv_models_v2.PolicyProfile) + + def create_profile_binding(db_session, tenant_id, profile_id, profile_type): """Create Network/Policy Profile association with a tenant.""" db_session = db_session or db.get_session() diff --git a/neutron/plugins/cisco/n1kv/n1kv_client.py b/neutron/plugins/cisco/n1kv/n1kv_client.py index 662199df3..82d9057ed 100644 --- a/neutron/plugins/cisco/n1kv/n1kv_client.py +++ b/neutron/plugins/cisco/n1kv/n1kv_client.py @@ -156,18 +156,6 @@ class Client(object): """ return self._get(self.port_profiles_path) - def list_events(self, event_type=None, epoch=None): - """ - Fetch all events of event_type from the VSM. - - :param event_type: type of event to be listed. - :param epoch: timestamp after which the events occurred to be listed. - :returns: XML string - """ - if event_type: - self.events_path = self.events_path + '?type=' + event_type - return self._get(self.events_path) - def create_bridge_domain(self, network, overlay_subtype): """ Create a bridge domain on VSM. diff --git a/neutron/plugins/cisco/n1kv/n1kv_neutron_plugin.py b/neutron/plugins/cisco/n1kv/n1kv_neutron_plugin.py index 028808b9f..769cb85fe 100644 --- a/neutron/plugins/cisco/n1kv/n1kv_neutron_plugin.py +++ b/neutron/plugins/cisco/n1kv/n1kv_neutron_plugin.py @@ -143,15 +143,13 @@ class N1kvNeutronPluginV2(db_base_plugin_v2.NeutronDbPluginV2, """ LOG.debug(_('_setup_vsm')) self.agent_vsm = True - # Retrieve all the policy profiles from VSM. - self._populate_policy_profiles() - # Continue to poll VSM for any create/delete of policy profiles. + # Poll VSM for create/delete of policy profile. eventlet.spawn(self._poll_policy_profiles) def _poll_policy_profiles(self): """Start a green thread to pull policy profiles from VSM.""" while True: - self._poll_policies(event_type='port_profile') + self._populate_policy_profiles() eventlet.sleep(int(c_conf.CISCO_N1K.poll_duration)) def _populate_policy_profiles(self): @@ -166,53 +164,35 @@ class N1kvNeutronPluginV2(db_base_plugin_v2.NeutronDbPluginV2, try: n1kvclient = n1kv_client.Client() policy_profiles = n1kvclient.list_port_profiles() - LOG.debug(_('_populate_policy_profiles %s'), policy_profiles) + vsm_profiles = {} + plugin_profiles = {} + # Fetch policy profiles from VSM if policy_profiles: for profile in policy_profiles['body'][c_const.SET]: - if c_const.ID and c_const.NAME in profile: - profile_id = profile[c_const.PROPERTIES][c_const.ID] - profile_name = profile[c_const. - PROPERTIES][c_const.NAME] - self._add_policy_profile(profile_name, profile_id) + profile_name = (profile[c_const.PROPERTIES]. + get(c_const.NAME, None)) + profile_id = (profile[c_const.PROPERTIES]. + get(c_const.ID, None)) + if profile_id and profile_name: + vsm_profiles[profile_id] = profile_name + # Fetch policy profiles previously populated + for profile in n1kv_db_v2.get_policy_profiles(): + plugin_profiles[profile.id] = profile.name + vsm_profiles_set = set(vsm_profiles) + plugin_profiles_set = set(plugin_profiles) + # Update database if the profile sets differ. + if vsm_profiles_set ^ plugin_profiles_set: + # Add profiles in database if new profiles were created in VSM + for pid in vsm_profiles_set - plugin_profiles_set: + self._add_policy_profile(vsm_profiles[pid], pid) + # Delete profiles from database if profiles were deleted in VSM + for pid in plugin_profiles_set - vsm_profiles_set: + self._delete_policy_profile(pid) self._remove_all_fake_policy_profiles() except (cisco_exceptions.VSMError, cisco_exceptions.VSMConnectionFailed): LOG.warning(_('No policy profile populated from VSM')) - def _poll_policies(self, event_type=None, epoch=None, tenant_id=None): - """ - Poll for Policy Profiles from Cisco Nexus1000V for any update/delete. - """ - LOG.debug(_('_poll_policies')) - try: - n1kvclient = n1kv_client.Client() - policy_profiles = n1kvclient.list_events(event_type, epoch) - if policy_profiles: - for profile in policy_profiles['body'][c_const.SET]: - if c_const.NAME in profile: - # Extract commands from the events XML. - cmd = profile[c_const.PROPERTIES]['cmd'] - cmds = cmd.split(';') - cmdwords = cmds[1].split() - profile_name = profile[c_const. - PROPERTIES][c_const.NAME] - # Delete the policy profile from db if deleted on VSM - if 'no' in cmdwords[0]: - p = self._get_policy_profile_by_name(profile_name) - if p: - self._delete_policy_profile(p['id']) - # Add policy profile to neutron DB idempotently - elif c_const.ID in profile[c_const.PROPERTIES]: - profile_id = profile[c_const. - PROPERTIES][c_const.ID] - self._add_policy_profile( - profile_name, profile_id, tenant_id) - # Replace tenant-id for profile bindings with admin's tenant-id - self._remove_all_fake_policy_profiles() - except (cisco_exceptions.VSMError, - cisco_exceptions.VSMConnectionFailed): - LOG.warning(_('No policy profile updated from VSM')) - def _extend_network_dict_provider(self, context, network): """Add extended network parameters.""" binding = n1kv_db_v2.get_network_binding(context.session, diff --git a/neutron/tests/unit/cisco/n1kv/fake_client.py b/neutron/tests/unit/cisco/n1kv/fake_client.py index 12a36e60b..55f876519 100755 --- a/neutron/tests/unit/cisco/n1kv/fake_client.py +++ b/neutron/tests/unit/cisco/n1kv/fake_client.py @@ -18,7 +18,7 @@ # @author: Sourabh Patwardhan, Cisco Systems Inc. from neutron.openstack.common import log as logging -from neutron.plugins.cisco.common import cisco_exceptions +from neutron.plugins.cisco.common import cisco_exceptions as c_exc from neutron.plugins.cisco.n1kv.n1kv_client import Client as n1kv_client LOG = logging.getLogger(__name__) @@ -36,15 +36,26 @@ class TestClient(n1kv_client): def __init__(self, **kwargs): self.broken = False self.inject_params = False + self.total_profiles = 2 super(TestClient, self).__init__() + def _get_total_profiles(self): + return self.total_profiles + def _do_request(self, method, action, body=None, headers=None): if self.broken: - raise cisco_exceptions.VSMError(reason='VSM:Internal Server Error') + raise c_exc.VSMError(reason='VSM:Internal Server Error') if self.inject_params and body: body['invalidKey'] = 'catchMeIfYouCan' if method == 'POST': return _validate_resource(action, body) + elif method == 'GET': + if 'virtual-port-profile' in action: + profiles = _policy_profile_generator_xml( + self._get_total_profiles()) + return self._deserialize(profiles, 200) + else: + raise c_exc.VSMError(reason='VSM:Internal Server Error') class TestClientInvalidRequest(TestClient): @@ -62,10 +73,33 @@ def _validate_resource(action, body=None): if 'vm-network' in action and 'port' not in action: vmnetwork_set = set(_resource_metadata['vmnetwork']) if body_set - vmnetwork_set: - raise cisco_exceptions.VSMError(reason='Invalid Request') + raise c_exc.VSMError(reason='Invalid Request') elif 'port' in action: port_set = set(_resource_metadata['port']) if body_set - port_set: - raise cisco_exceptions.VSMError(reason='Invalid Request') + raise c_exc.VSMError(reason='Invalid Request') else: return + + +def _policy_profile_generator_xml(total_profiles): + """ + Generate policy profile response in XML format. + + :param total_profiles: integer representing total number of profiles to + return + """ + xml = [""" + """] + template = ( + '' + '' + '00000000-0000-0000-0000-00000000000%(num)s' + 'pp-%(num)s' + '' + '' + ) + xml.extend(template % {'num': n} for n in range(1, total_profiles + 1)) + xml.append("") + return ''.join(xml) diff --git a/neutron/tests/unit/cisco/n1kv/test_n1kv_plugin.py b/neutron/tests/unit/cisco/n1kv/test_n1kv_plugin.py index 0891ab80e..eba1797fb 100644 --- a/neutron/tests/unit/cisco/n1kv/test_n1kv_plugin.py +++ b/neutron/tests/unit/cisco/n1kv/test_n1kv_plugin.py @@ -25,6 +25,7 @@ from neutron.api.v2 import attributes from neutron import context import neutron.db.api as db from neutron.extensions import portbindings +from neutron import manager from neutron.plugins.cisco.common import cisco_exceptions as c_exc from neutron.plugins.cisco.db import n1kv_db_v2 from neutron.plugins.cisco.db import network_db_v2 as cdb @@ -67,7 +68,7 @@ class FakeResponse(object): def _fake_setup_vsm(self): """Fake establish Communication with Cisco Nexus1000V VSM.""" self.agent_vsm = True - self._poll_policies(event_type="port_profile") + self._populate_policy_profiles() class NetworkProfileTestExtensionManager(object): @@ -504,6 +505,55 @@ class TestN1kvPorts(test_plugin.TestPortsV2, client_patch.stop() +class TestN1kvPolicyProfiles(N1kvPluginTestCase): + def test_populate_policy_profile(self): + client_patch = patch(n1kv_client.__name__ + ".Client", + new=fake_client.TestClient) + client_patch.start() + instance = n1kv_neutron_plugin.N1kvNeutronPluginV2() + instance._populate_policy_profiles() + db_session = db.get_session() + profile = n1kv_db_v2.get_policy_profile( + db_session, '00000000-0000-0000-0000-000000000001') + self.assertEqual('pp-1', profile['name']) + client_patch.stop() + + def test_populate_policy_profile_delete(self): + # Patch the Client class with the TestClient class + with patch(n1kv_client.__name__ + ".Client", + new=fake_client.TestClient): + # Patch the _get_total_profiles() method to return a custom value + with patch(fake_client.__name__ + + '.TestClient._get_total_profiles') as obj_inst: + # Return 3 policy profiles + obj_inst.return_value = 3 + plugin = manager.NeutronManager.get_plugin() + plugin._populate_policy_profiles() + db_session = db.get_session() + profile = n1kv_db_v2.get_policy_profile( + db_session, '00000000-0000-0000-0000-000000000001') + # Verify that DB contains only 3 policy profiles + self.assertEqual('pp-1', profile['name']) + profile = n1kv_db_v2.get_policy_profile( + db_session, '00000000-0000-0000-0000-000000000002') + self.assertEqual('pp-2', profile['name']) + profile = n1kv_db_v2.get_policy_profile( + db_session, '00000000-0000-0000-0000-000000000003') + self.assertEqual('pp-3', profile['name']) + self.assertRaises(c_exc.PolicyProfileIdNotFound, + n1kv_db_v2.get_policy_profile, + db_session, + '00000000-0000-0000-0000-000000000004') + # Return 2 policy profiles + obj_inst.return_value = 2 + plugin._populate_policy_profiles() + # Verify that the third policy profile is deleted + self.assertRaises(c_exc.PolicyProfileIdNotFound, + n1kv_db_v2.get_policy_profile, + db_session, + '00000000-0000-0000-0000-000000000003') + + class TestN1kvNetworks(test_plugin.TestNetworksV2, N1kvPluginTestCase):