Merge "Add functional test for availability_zone support"
This commit is contained in:
commit
c4e1402e62
@ -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))
|
||||
|
@ -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))
|
||||
|
@ -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):
|
||||
|
Loading…
x
Reference in New Issue
Block a user