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 e0f69d6929)
This commit is contained in:
Dhanashree Gosavi 2014-02-10 04:19:50 -08:00 committed by Ihar Hrachyshka
parent 9f13ef4b64
commit 47a4954923
5 changed files with 120 additions and 61 deletions

View 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()

View 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.

View 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,

View File

@ -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)

View 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):