]> 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)
committerThomas Goirand <thomas@goirand.fr>
Mon, 9 Jun 2014 15:06:55 +0000 (23:06 +0800)
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)

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 662199df3b41da2987b5fdd9660e4bb63b410fbb..82d9057edb81b98cdd18e7309a240716b54e0551 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 028808b9f278542dbb626e4e0c389576bb23faaf..769cb85feb03b4bffaa6d5b9a07c1372fec319bc 100644 (file)
@@ -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,
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 0891ab80eb357ad12702c20ccd2408700fa95514..eba1797fbd0c825abd7ab6a18adecd8917b11224 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
@@ -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):