From: Hirofumi Ichihara Date: Fri, 4 Dec 2015 14:56:20 +0000 (+0900) Subject: Add functional test for availability_zone support X-Git-Url: https://review.fuel-infra.org/gitweb?a=commitdiff_plain;h=5700afb3201b240e9cc8bfaf7a86b69ce66aacec;p=openstack-build%2Fneutron-build.git Add functional test for availability_zone support This patch adds the functional tests for availability_zone support. Change-Id: Ie7fe7cd357a952baacb95d4fc05a687c6465ad61 Co-Authored-By: IWAMOTO Toshihiro Partially-implements: blueprint add-availability-zone --- diff --git a/neutron/tests/functional/scheduler/test_dhcp_agent_scheduler.py b/neutron/tests/functional/scheduler/test_dhcp_agent_scheduler.py index e65a3874b..7037af063 100644 --- a/neutron/tests/functional/scheduler/test_dhcp_agent_scheduler.py +++ b/neutron/tests/functional/scheduler/test_dhcp_agent_scheduler.py @@ -13,6 +13,7 @@ # License for the specific language governing permissions and limitations # under the License. +import collections import six import testscenarios @@ -212,6 +213,11 @@ class TestAutoSchedule(test_dhcp_sch.TestDhcpSchedulerBaseTestCase, This stores the expected networks that should have been scheduled (or that could have already been scheduled) for each agent after the 'auto_schedule_networks' function is called. + + no_network_with_az_match + If this parameter is True, there is no unscheduled network with + availability_zone_hints matches to an availability_zone of agents + to be scheduled. The default is False. """ scenarios = [ @@ -320,6 +326,16 @@ class TestAutoSchedule(test_dhcp_sch.TestDhcpSchedulerBaseTestCase, expected_auto_schedule_return_value=False, expected_hosted_networks={'agent-0': [], 'agent-1': []})), + + ('No agents scheduled if unscheduled network does not match AZ', + dict(agent_count=1, + max_agents_per_network=1, + network_count=1, + networks_with_dhcp_disabled=[], + hosted_networks={}, + expected_auto_schedule_return_value=True, + expected_hosted_networks={'agent-0': []}, + no_network_with_az_match=True)), ] def _strip_host_index(self, name): @@ -346,8 +362,10 @@ class TestAutoSchedule(test_dhcp_sch.TestDhcpSchedulerBaseTestCase, return subnets def get_network(self, context, net_id): - # TODO(hichihara): add test cases of AZ scheduler - return {'availability_zone_hints': []} + az_hints = [] + if getattr(self, 'no_network_with_az_match', False): + az_hints = ['not-match'] + return {'availability_zone_hints': az_hints} def _get_hosted_networks_on_dhcp_agent(self, agent_id): query = self.ctx.session.query( @@ -400,3 +418,144 @@ class TestAutoSchedule(test_dhcp_sch.TestDhcpSchedulerBaseTestCase, def test_auto_schedule(self): for i in range(self.agent_count): self._test_auto_schedule(i) + + +class TestAZAwareWeightScheduler(test_dhcp_sch.TestDhcpSchedulerBaseTestCase, + agentschedulers_db.DhcpAgentSchedulerDbMixin, + agents_db.AgentDbMixin, + common_db_mixin.CommonDbMixin): + """Test various scenarios for AZAwareWeightScheduler.schedule. + + az_count + Number of AZs. + + network_az_hints + Number of AZs in availability_zone_hints of the network. + + agent_count[each az] + Number of dhcp agents (also number of hosts). + + max_agents_per_network + Maximum DHCP Agents that can be scheduled for a network. + + scheduled_agent_count[each az] + Number of agents the network has previously scheduled + + down_agent_count[each az] + Number of dhcp agents which are down + + expected_scheduled_agent_count[each az] + Number of scheduled agents the schedule() should return + or 'None' if the schedule() cannot schedule the network. + """ + + scenarios = [ + ('Single hint, Single agent, Scheduled an agent of the specified AZ', + dict(az_count=2, + network_az_hints=1, + agent_count=[1, 1], + max_agents_per_network=1, + scheduled_agent_count=[0, 0], + down_agent_count=[0, 0], + expected_scheduled_agent_count=[1, 0])), + + ('Multi hints, Multi agents Scheduled agents of the specified AZs', + dict(az_count=3, + network_az_hints=2, + agent_count=[1, 1, 1], + max_agents_per_network=2, + scheduled_agent_count=[0, 0, 0], + down_agent_count=[0, 0, 0], + expected_scheduled_agent_count=[1, 1, 0])), + + ('Single hint, Multi agents, Scheduled agents of the specified AZ', + dict(az_count=2, + network_az_hints=1, + agent_count=[2, 1], + max_agents_per_network=2, + scheduled_agent_count=[0, 0], + down_agent_count=[0, 0], + expected_scheduled_agent_count=[2, 0])), + + ('Multi hints, Multi agents, Only single AZ available', + dict(az_count=2, + network_az_hints=2, + agent_count=[2, 1], + max_agents_per_network=2, + scheduled_agent_count=[0, 0], + down_agent_count=[0, 1], + expected_scheduled_agent_count=[2, 0])), + + ('Multi hints, Multi agents, Not enough agents', + dict(az_count=3, + network_az_hints=3, + agent_count=[1, 1, 1], + max_agents_per_network=3, + scheduled_agent_count=[0, 0, 0], + down_agent_count=[0, 1, 0], + expected_scheduled_agent_count=[1, 0, 1])), + + ('Multi hints, Multi agents, Partially scheduled, Another AZ selected', + dict(az_count=3, + network_az_hints=2, + agent_count=[1, 1, 1], + max_agents_per_network=2, + scheduled_agent_count=[1, 0, 0], + down_agent_count=[0, 0, 0], + expected_scheduled_agent_count=[0, 1, 0])), + + ('No hint, Scheduled independent to AZ', + dict(az_count=3, + network_az_hints=0, + agent_count=[1, 1, 1], + max_agents_per_network=3, + scheduled_agent_count=[0, 0, 0], + down_agent_count=[0, 0, 0], + expected_scheduled_agent_count=[1, 1, 1])), + ] + + def _set_network_az_hints(self): + self.network['availability_zone_hints'] = [] + for i in range(self.network_az_hints): + self.network['availability_zone_hints'].append('az%s' % i) + + def test_schedule_network(self): + self.config(dhcp_agents_per_network=self.max_agents_per_network) + scheduler = dhcp_agent_scheduler.AZAwareWeightScheduler() + self._set_network_az_hints() + + # create dhcp agents + for i in range(self.az_count): + az = 'az%s' % i + hosts = ['%s-host-%s' % (az, j) + for j in range(self.agent_count[i])] + dhcp_agents = self._create_and_set_agents_down( + hosts, down_agent_count=self.down_agent_count[i], az=az) + + active_agents = dhcp_agents[self.down_agent_count[i]:] + + # schedule some agents before calling schedule + if self.scheduled_agent_count[i]: + # schedule the network + schedule_agents = active_agents[:self.scheduled_agent_count[i]] + scheduler.resource_filter.bind( + self.ctx, schedule_agents, self.network_id) + + actual_scheduled_agents = scheduler.schedule(self, self.ctx, + self.network) + scheduled_azs = collections.defaultdict(int) + for agent in actual_scheduled_agents: + scheduled_azs[agent['availability_zone']] += 1 + + hosted_agents = self.list_dhcp_agents_hosting_network( + self.ctx, self.network_id) + hosted_azs = collections.defaultdict(int) + for agent in hosted_agents['agents']: + hosted_azs[agent['availability_zone']] += 1 + + for i in range(self.az_count): + self.assertEqual(self.expected_scheduled_agent_count[i], + scheduled_azs.get('az%s' % i, 0)) + self.assertEqual(self.scheduled_agent_count[i] + + scheduled_azs.get('az%s' % i, 0), + hosted_azs.get('az%s' % i, 0)) diff --git a/neutron/tests/functional/scheduler/test_l3_agent_scheduler.py b/neutron/tests/functional/scheduler/test_l3_agent_scheduler.py index ca89515ba..45d22b6d4 100644 --- a/neutron/tests/functional/scheduler/test_l3_agent_scheduler.py +++ b/neutron/tests/functional/scheduler/test_l3_agent_scheduler.py @@ -13,6 +13,7 @@ # License for the specific language governing permissions and limitations # under the License. +import collections import random import testscenarios @@ -272,3 +273,276 @@ class L3LeastRoutersSchedulerTestCase(L3SchedulerBaseTest): self.scheduled_router_count) # The test self._test_auto_schedule(self.expected_scheduled_router_count) + + +class L3AZSchedulerBaseTest(test_db_base_plugin_v2.NeutronDbPluginV2TestCase): + + def setUp(self): + core_plugin = 'neutron.plugins.ml2.plugin.Ml2Plugin' + super(L3AZSchedulerBaseTest, self).setUp(plugin=core_plugin) + + self.l3_plugin = l3_router_plugin.L3RouterPlugin() + self.adminContext = context.get_admin_context() + self.adminContext.tenant_id = '_func_test_tenant_' + + def _create_l3_agent(self, host, context, agent_mode='legacy', plugin=None, + state=True, az='nova'): + agent = helpers.register_l3_agent(host, agent_mode, az=az) + helpers.set_agent_admin_state(agent.id, state) + return agent + + def _create_legacy_agents(self, agent_count, down_agent_count, az): + # Creates legacy l3 agents and sets admin state based on + # down agent count. + hosts = ['%s-host-%s' % (az, i) for i in range(agent_count)] + l3_agents = [ + self._create_l3_agent(hosts[i], self.adminContext, 'legacy', + self.l3_plugin, (i >= down_agent_count), + az=az) + for i in range(agent_count)] + return l3_agents + + def _create_router(self, az_hints, ha): + router = {'name': 'router1', 'admin_state_up': True, + 'availability_zone_hints': az_hints} + if ha: + router['ha'] = True + return self.l3_plugin.create_router( + self.adminContext, {'router': router}) + + +class L3AZLeastRoutersSchedulerTestCase(L3AZSchedulerBaseTest): + + """Test various scenarios for AZ router scheduler. + + az_count + Number of AZs. + + router_az_hints + Number of AZs in availability_zone_hints of the router. + + agent_count[each az] + Number of l3 agents (also number of hosts). + + max_l3_agents_per_router + Maximum number of agents on which a router will be scheduled. + 0 means test for regular router. + + min_l3_agents_per_router + Minimum number of agents on which a router will be scheduled. + N/A for regular router test. + + down_agent_count[each az] + Number of l3 agents which are down. + + expected_scheduled_agent_count[each az] + Number of newly scheduled l3 agents. + """ + + scenarios = [ + ('Regular router, Scheduled specified AZ', + dict(az_count=2, + router_az_hints=1, + agent_count=[1, 1], + max_l3_agents_per_router=0, + min_l3_agents_per_router=0, + down_agent_count=[0, 0], + expected_scheduled_agent_count=[1, 0])), + + ('HA router, Scheduled specified AZs', + dict(az_count=3, + router_az_hints=2, + agent_count=[1, 1, 1], + max_l3_agents_per_router=2, + min_l3_agents_per_router=2, + down_agent_count=[0, 0, 0], + expected_scheduled_agent_count=[1, 1, 0])), + + ('HA router, max_l3_agents_per_routers > az_hints', + dict(az_count=2, + router_az_hints=2, + agent_count=[2, 1], + max_l3_agents_per_router=3, + min_l3_agents_per_router=2, + down_agent_count=[0, 0], + expected_scheduled_agent_count=[2, 1])), + + ('HA router, not enough agents', + dict(az_count=3, + router_az_hints=2, + agent_count=[2, 2, 2], + max_l3_agents_per_router=3, + min_l3_agents_per_router=2, + down_agent_count=[1, 1, 0], + expected_scheduled_agent_count=[1, 1, 0])), + ] + + def test_schedule_router(self): + scheduler = l3_agent_scheduler.AZLeastRoutersScheduler() + ha = False + if self.max_l3_agents_per_router: + self.config(max_l3_agents_per_router=self.max_l3_agents_per_router) + self.config(min_l3_agents_per_router=self.min_l3_agents_per_router) + ha = True + + # create l3 agents + for i in range(self.az_count): + az = 'az%s' % i + self._create_legacy_agents(self.agent_count[i], + self.down_agent_count[i], az) + + # create router. + # note that ha-router needs enough agents beforehand. + az_hints = ['az%s' % i for i in range(self.router_az_hints)] + router = self._create_router(az_hints, ha) + + scheduler.schedule(self.l3_plugin, self.adminContext, router['id']) + # schedule returns only one agent. so get all agents scheduled. + scheduled_agents = self.l3_plugin.get_l3_agents_hosting_routers( + self.adminContext, [router['id']]) + + scheduled_azs = collections.defaultdict(int) + for agent in scheduled_agents: + scheduled_azs[agent['availability_zone']] += 1 + + for i in range(self.az_count): + self.assertEqual(self.expected_scheduled_agent_count[i], + scheduled_azs.get('az%s' % i, 0)) + + +class L3AZAutoScheduleTestCaseBase(L3AZSchedulerBaseTest): + + """Test various scenarios for AZ router scheduler. + + az_count + Number of AZs. + + router_az_hints + Number of AZs in availability_zone_hints of the router. + + agent_az + AZ of newly activated l3 agent. + + agent_count[each az] + Number of l3 agents (also number of hosts). + + max_l3_agents_per_router + Maximum number of agents on which a router will be scheduled. + 0 means test for regular router. + + min_l3_agents_per_router + Minimum number of agents on which a router will be scheduled. + N/A for regular router test. + + down_agent_count[each az] + Number of l3 agents which are down. + + scheduled_agent_count[each az] + Number of l3 agents that have been previously scheduled + + expected_scheduled_agent_count[each az] + Number of newly scheduled l3 agents + """ + + scenarios = [ + ('Regular router, not scheduled, agent in specified AZ activated', + dict(az_count=2, + router_az_hints=1, + agent_az='az0', + agent_count=[1, 1], + max_l3_agents_per_router=0, + min_l3_agents_per_router=0, + down_agent_count=[1, 1], + scheduled_agent_count=[0, 0], + expected_scheduled_agent_count=[1, 0])), + + ('Regular router, not scheduled, agent not in specified AZ activated', + dict(az_count=2, + router_az_hints=1, + agent_az='az1', + agent_count=[1, 1], + max_l3_agents_per_router=0, + min_l3_agents_per_router=0, + down_agent_count=[1, 1], + scheduled_agent_count=[0, 0], + expected_scheduled_agent_count=[0, 0])), + + ('HA router, not scheduled, agent in specified AZ activated', + dict(az_count=3, + router_az_hints=2, + agent_az='az1', + agent_count=[1, 1, 1], + max_l3_agents_per_router=2, + min_l3_agents_per_router=2, + down_agent_count=[0, 1, 0], + scheduled_agent_count=[0, 0, 0], + expected_scheduled_agent_count=[0, 1, 0])), + + ('HA router, not scheduled, agent not in specified AZ activated', + dict(az_count=3, + router_az_hints=2, + agent_az='az2', + agent_count=[1, 1, 1], + max_l3_agents_per_router=2, + min_l3_agents_per_router=2, + down_agent_count=[0, 0, 1], + scheduled_agent_count=[0, 0, 0], + expected_scheduled_agent_count=[0, 0, 0])), + + ('HA router, partial scheduled, agent in specified AZ activated', + dict(az_count=3, + router_az_hints=2, + agent_az='az1', + agent_count=[1, 1, 1], + max_l3_agents_per_router=2, + min_l3_agents_per_router=2, + down_agent_count=[0, 1, 0], + scheduled_agent_count=[1, 0, 0], + expected_scheduled_agent_count=[1, 1, 0])), + ] + + def test_auto_schedule_router(self): + scheduler = l3_agent_scheduler.AZLeastRoutersScheduler() + ha = False + if self.max_l3_agents_per_router: + self.config(max_l3_agents_per_router=self.max_l3_agents_per_router) + self.config(min_l3_agents_per_router=self.min_l3_agents_per_router) + ha = True + + # create l3 agents + l3_agents = {} + for i in range(self.az_count): + az = 'az%s' % i + l3_agents[az] = self._create_legacy_agents( + self.agent_count[i], self.down_agent_count[i], az) + + # create router. + # note that ha-router needs enough agents beforehand. + az_hints = ['az%s' % i for i in range(self.router_az_hints)] + router = self._create_router(az_hints, ha) + + # schedule some agents before calling auto schedule + for i in range(self.az_count): + az = 'az%s' % i + for j in range(self.scheduled_agent_count[i]): + agent = l3_agents[az][j + self.down_agent_count[i]] + scheduler.bind_router(self.adminContext, router['id'], agent) + + # activate down agent and call auto_schedule_routers + activate_agent = l3_agents[self.agent_az][0] + helpers.set_agent_admin_state(activate_agent['id'], + admin_state_up=True) + + scheduler.auto_schedule_routers(self.l3_plugin, self.adminContext, + activate_agent['host'], None) + + scheduled_agents = self.l3_plugin.get_l3_agents_hosting_routers( + self.adminContext, [router['id']]) + + scheduled_azs = collections.defaultdict(int) + for agent in scheduled_agents: + scheduled_azs[agent['availability_zone']] += 1 + + for i in range(self.az_count): + self.assertEqual(self.expected_scheduled_agent_count[i], + scheduled_azs.get('az%s' % i, 0)) diff --git a/neutron/tests/unit/scheduler/test_dhcp_agent_scheduler.py b/neutron/tests/unit/scheduler/test_dhcp_agent_scheduler.py index f06fd46b1..cfce17e06 100644 --- a/neutron/tests/unit/scheduler/test_dhcp_agent_scheduler.py +++ b/neutron/tests/unit/scheduler/test_dhcp_agent_scheduler.py @@ -45,14 +45,16 @@ class TestDhcpSchedulerBaseTestCase(testlib_api.SqlTestCase): self._save_networks([self.network_id]) def _create_and_set_agents_down(self, hosts, down_agent_count=0, - admin_state_up=True): + admin_state_up=True, + az=helpers.DEFAULT_AZ): agents = [] for i, host in enumerate(hosts): is_alive = i >= down_agent_count agents.append(helpers.register_dhcp_agent( host, admin_state_up=admin_state_up, - alive=is_alive)) + alive=is_alive, + az=az)) return agents def _save_networks(self, networks):