]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
Remove List events API from Cisco N1kv Neutron
authorDhanashree Gosavi <dhgosavi@cisco.com>
Mon, 10 Feb 2014 12:19:50 +0000 (04:19 -0800)
committerSourabh Patwardhan <sopatwar@cisco.com>
Tue, 22 Apr 2014 22:37:31 +0000 (15:37 -0700)
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

neutron/plugins/cisco/db/n1kv_db_v2.py
neutron/plugins/cisco/n1kv/n1kv_client.py
neutron/plugins/cisco/n1kv/n1kv_neutron_plugin.py
neutron/tests/unit/cisco/n1kv/fake_client.py
neutron/tests/unit/cisco/n1kv/test_n1kv_plugin.py

index 5c237115c6dd22ecbc8d0a77a7f3bf872d086a32..be54fc097dbff91241f99cc32757afd2062bd890 100644 (file)
@@ -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()
index 6164d9638880f5f3f6433005d6140ded2ba73783..79abf715e9f087d3fd936e1999db8e012d929c67 100644 (file)
@@ -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.
index badb7fe379aaccb0ac3e38e6bdd1808996aa049e..0e77f9029c821982ba5e2a82cb51fa33a7d33cba 100644 (file)
@@ -154,15 +154,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):
@@ -177,53 +175,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,
index 12a36e60b09282209094219ed3e1ff9d54a77760..55f8765195e766dbc574886ad3cf208060a03be2 100755 (executable)
@@ -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 = ["""<?xml version="1.0" encoding="utf-8"?>
+           <set name="virtual_port_profile_set">"""]
+    template = (
+        '<instance name="%(num)d"'
+        ' url="/api/n1k/virtual-port-profile/%(num)s">'
+        '<properties>'
+        '<id>00000000-0000-0000-0000-00000000000%(num)s</id>'
+        '<name>pp-%(num)s</name>'
+        '</properties>'
+        '</instance>'
+    )
+    xml.extend(template % {'num': n} for n in range(1, total_profiles + 1))
+    xml.append("</set>")
+    return ''.join(xml)
index d9dc4cf3b2c49791c5e64b58b00c7e8986fa9eaf..793f037492ded01a51beab0b94f68baffdb4110b 100644 (file)
@@ -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
@@ -69,7 +70,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):
@@ -506,6 +507,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):