]> review.fuel-infra Code Review - openstack-build/neutron-build.git/commitdiff
Add functional test for availability_zone support
authorHirofumi Ichihara <ichihara.hirofumi@lab.ntt.co.jp>
Fri, 4 Dec 2015 14:56:20 +0000 (23:56 +0900)
committerHirofumi Ichihara <ichihara.hirofumi@lab.ntt.co.jp>
Mon, 14 Dec 2015 01:07:47 +0000 (10:07 +0900)
This patch adds the functional tests for availability_zone support.

Change-Id: Ie7fe7cd357a952baacb95d4fc05a687c6465ad61
Co-Authored-By: IWAMOTO Toshihiro <iwamoto@valinux.co.jp>
Partially-implements: blueprint add-availability-zone

neutron/tests/functional/scheduler/test_dhcp_agent_scheduler.py
neutron/tests/functional/scheduler/test_l3_agent_scheduler.py
neutron/tests/unit/scheduler/test_dhcp_agent_scheduler.py

index e65a3874b39516ed6e6850d39718d2b3cf4107be..7037af0637c4bdd74416e99b107b3c4d4ce06dad 100644 (file)
@@ -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))
index ca89515ba556098940a8295f60e7090978d39dbc..45d22b6d4466a55db90f74acae7525cd75da6d96 100644 (file)
@@ -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))
index f06fd46b185baf6e74526f2bcb3c1dcbcca122e2..cfce17e06533d642c9a9ae1bfd8be456deb4612d 100644 (file)
@@ -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):