From: Numan Siddique Date: Wed, 10 Dec 2014 20:37:24 +0000 (+0530) Subject: Support 'alive' filter for get_agents() in agents_db X-Git-Url: https://review.fuel-infra.org/gitweb?a=commitdiff_plain;h=b08fe1b9dc4b7247c5ed8c4f2e1c8e07ba56a739;p=openstack-build%2Fneutron-build.git Support 'alive' filter for get_agents() in agents_db Presently 'alive' filter is not supported when the neutron cli calls 'neutron agent_list --alive False'. This patch fixes the issue by pruning the agent list according to the 'alive' filter. If the client passes invalid value to the alive filter, then InvalidInput exception is raised. DocImpact APIImpact Change-Id: I8935c09d82f6e88252560a5232adf22002cbae08 Closes-bug: #1398398 --- diff --git a/neutron/db/agents_db.py b/neutron/db/agents_db.py index 2516886a4..ad309be87 100644 --- a/neutron/db/agents_db.py +++ b/neutron/db/agents_db.py @@ -25,6 +25,7 @@ import sqlalchemy as sa from sqlalchemy.orm import exc from sqlalchemy import sql +from neutron.api.v2 import attributes from neutron.db import model_base from neutron.db import models_v2 from neutron.extensions import agent as ext_agent @@ -143,9 +144,15 @@ class AgentDbMixin(ext_agent.AgentPluginBase): return query.all() def get_agents(self, context, filters=None, fields=None): - return self._get_collection(context, Agent, - self._make_agent_dict, - filters=filters, fields=fields) + agents = self._get_collection(context, Agent, + self._make_agent_dict, + filters=filters, fields=fields) + alive = filters and filters.get('alive', None) + if alive: + # alive filter will be a list + alive = attributes.convert_to_boolean(alive[0]) + agents = [agent for agent in agents if agent['alive'] == alive] + return agents def _get_agent_by_type_and_host(self, context, agent_type, host): query = self._model_query(context, Agent) diff --git a/neutron/tests/unit/db/test_agent_db.py b/neutron/tests/unit/db/test_agent_db.py index b0a1daef3..e46665fb1 100644 --- a/neutron/tests/unit/db/test_agent_db.py +++ b/neutron/tests/unit/db/test_agent_db.py @@ -13,28 +13,73 @@ # See the License for the specific language governing permissions and # limitations under the License. +import datetime import mock + from oslo.db import exception as exc from oslo.utils import timeutils +import testscenarios from neutron.common import constants +from neutron.common import exceptions as n_exc from neutron import context from neutron.db import agents_db from neutron.db import db_base_plugin_v2 as base_plugin from neutron.tests.unit import testlib_api +# the below code is required for the following reason +# (as documented in testscenarios) +"""Multiply tests depending on their 'scenarios' attribute. + + This can be assigned to 'load_tests' in any test module to make this + automatically work across tests in the module. +""" +load_tests = testscenarios.load_tests_apply_scenarios + class FakePlugin(base_plugin.NeutronDbPluginV2, agents_db.AgentDbMixin): """A fake plugin class containing all DB methods.""" -class TestAgentsDbMixin(testlib_api.SqlTestCase): +class TestAgentsDbBase(testlib_api.SqlTestCase): def setUp(self): - super(TestAgentsDbMixin, self).setUp() - + super(TestAgentsDbBase, self).setUp() self.context = context.get_admin_context() self.plugin = FakePlugin() + def _get_agents(self, hosts, agent_type): + return [ + agents_db.Agent( + binary='foo-agent', + host=host, + agent_type=agent_type, + topic='foo_topic', + configurations="", + created_at=timeutils.utcnow(), + started_at=timeutils.utcnow(), + heartbeat_timestamp=timeutils.utcnow()) + for host in hosts + ] + + def _save_agents(self, agents): + for agent in agents: + with self.context.session.begin(subtransactions=True): + self.context.session.add(agent) + + def _create_and_save_agents(self, hosts, agent_type, down_agents_count=0): + agents = self._get_agents(hosts, agent_type) + # bring down the specified agents + for agent in agents[:down_agents_count]: + agent['heartbeat_timestamp'] -= datetime.timedelta(minutes=60) + + self._save_agents(agents) + return agents + + +class TestAgentsDbMixin(TestAgentsDbBase): + def setUp(self): + super(TestAgentsDbMixin, self).setUp() + self.agent_status = { 'agent_type': 'Open vSwitch agent', 'binary': 'neutron-openvswitch-agent', @@ -42,27 +87,12 @@ class TestAgentsDbMixin(testlib_api.SqlTestCase): 'topic': 'N/A' } - def _add_agent(self, agent_id, agent_type, agent_host): - with self.context.session.begin(subtransactions=True): - now = timeutils.utcnow() - agent = agents_db.Agent(id=agent_id, - agent_type=agent_type, - binary='foo_binary', - topic='foo_topic', - host=agent_host, - created_at=now, - started_at=now, - admin_state_up=True, - heartbeat_timestamp=now, - configurations='') - self.context.session.add(agent) - return agent - def test_get_enabled_agent_on_host_found(self): - agent = self._add_agent('foo_id', constants.AGENT_TYPE_L3, 'foo_host') + agents = self._create_and_save_agents(['foo_host'], + constants.AGENT_TYPE_L3) expected = self.plugin.get_enabled_agent_on_host( self.context, constants.AGENT_TYPE_L3, 'foo_host') - self.assertEqual(expected, agent) + self.assertEqual(expected, agents[0]) def test_get_enabled_agent_on_host_not_found(self): with mock.patch.object(agents_db.LOG, 'debug') as mock_log: @@ -111,3 +141,56 @@ class TestAgentsDbMixin(testlib_api.SqlTestCase): self.assertEqual(add_mock.call_count, 2, "Agent entry creation hasn't been retried") + + +class TestAgentsDbGetAgents(TestAgentsDbBase): + scenarios = [ + ('Get all agents', dict(agents=5, down_agents=2, + agents_alive=None, + expected_agents=5)), + + ('Get alive agents (True)', dict(agents=5, down_agents=2, + agents_alive='True', + expected_agents=3)), + + ('Get down agents (False)', dict(agents=5, down_agents=2, + agents_alive='False', + expected_agents=2)), + + ('Get alive agents (true)', dict(agents=5, down_agents=2, + agents_alive='true', + expected_agents=3)), + + ('Get down agents (false)', dict(agents=5, down_agents=2, + agents_alive='false', + expected_agents=2)), + + ('Get agents invalid alive filter', dict(agents=5, down_agents=2, + agents_alive='invalid', + expected_agents=None)), + ] + + def setUp(self): + # ensure that the first scenario will execute with nosetests + if not hasattr(self, 'agents'): + self.__dict__.update(self.scenarios[0][1]) + super(TestAgentsDbGetAgents, self).setUp() + + def test_get_agents(self): + hosts = ['host-%s' % i for i in range(self.agents)] + self._create_and_save_agents(hosts, constants.AGENT_TYPE_L3, + down_agents_count=self.down_agents) + if self.agents_alive == 'invalid': + self.assertRaises(n_exc.InvalidInput, self.plugin.get_agents, + self.context, + filters={'alive': [self.agents_alive]}) + else: + returned_agents = self.plugin.get_agents( + self.context, filters={'alive': [self.agents_alive]} + if self.agents_alive else None) + self.assertEqual(self.expected_agents, len(returned_agents)) + if self.agents_alive: + alive = (self.agents_alive == 'True' or + self.agents_alive == 'true') + for agent in returned_agents: + self.assertEqual(alive, agent['alive'])