Moving tests into scheduler subdirectory
This commit is contained in:
@@ -313,8 +313,3 @@ class HostFilterScheduler(zone_aware_scheduler.ZoneAwareScheduler):
|
|||||||
instance_type = request_spec['instance_type']
|
instance_type = request_spec['instance_type']
|
||||||
name, query = driver.instance_type_to_filter(instance_type)
|
name, query = driver.instance_type_to_filter(instance_type)
|
||||||
return driver.filter_hosts(self.zone_manager, query)
|
return driver.filter_hosts(self.zone_manager, query)
|
||||||
|
|
||||||
def weigh_hosts(self, num, request_spec, hosts):
|
|
||||||
"""Derived classes must override this method and return
|
|
||||||
a lists of hosts in [{weight, hostname}] format."""
|
|
||||||
return [dict(weight=1, hostname=host) for host, caps in hosts]
|
|
||||||
|
|||||||
@@ -1,211 +0,0 @@
|
|||||||
# Copyright 2011 OpenStack LLC.
|
|
||||||
# All Rights Reserved.
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
"""
|
|
||||||
Tests For Scheduler Host Filter Drivers.
|
|
||||||
"""
|
|
||||||
|
|
||||||
import json
|
|
||||||
|
|
||||||
from nova import exception
|
|
||||||
from nova import flags
|
|
||||||
from nova import test
|
|
||||||
from nova.scheduler import host_filter
|
|
||||||
|
|
||||||
FLAGS = flags.FLAGS
|
|
||||||
|
|
||||||
|
|
||||||
class FakeZoneManager:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class HostFilterTestCase(test.TestCase):
|
|
||||||
"""Test case for host filter drivers."""
|
|
||||||
|
|
||||||
def _host_caps(self, multiplier):
|
|
||||||
# Returns host capabilities in the following way:
|
|
||||||
# host1 = memory:free 10 (100max)
|
|
||||||
# disk:available 100 (1000max)
|
|
||||||
# hostN = memory:free 10 + 10N
|
|
||||||
# disk:available 100 + 100N
|
|
||||||
# in other words: hostN has more resources than host0
|
|
||||||
# which means ... don't go above 10 hosts.
|
|
||||||
return {'host_name-description': 'XenServer %s' % multiplier,
|
|
||||||
'host_hostname': 'xs-%s' % multiplier,
|
|
||||||
'host_memory_total': 100,
|
|
||||||
'host_memory_overhead': 10,
|
|
||||||
'host_memory_free': 10 + multiplier * 10,
|
|
||||||
'host_memory_free-computed': 10 + multiplier * 10,
|
|
||||||
'host_other-config': {},
|
|
||||||
'host_ip_address': '192.168.1.%d' % (100 + multiplier),
|
|
||||||
'host_cpu_info': {},
|
|
||||||
'disk_available': 100 + multiplier * 100,
|
|
||||||
'disk_total': 1000,
|
|
||||||
'disk_used': 0,
|
|
||||||
'host_uuid': 'xxx-%d' % multiplier,
|
|
||||||
'host_name-label': 'xs-%s' % multiplier}
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
super(HostFilterTestCase, self).setUp()
|
|
||||||
self.old_flag = FLAGS.default_host_filter_driver
|
|
||||||
FLAGS.default_host_filter_driver = \
|
|
||||||
'nova.scheduler.host_filter.AllHostsFilter'
|
|
||||||
self.instance_type = dict(name='tiny',
|
|
||||||
memory_mb=50,
|
|
||||||
vcpus=10,
|
|
||||||
local_gb=500,
|
|
||||||
flavorid=1,
|
|
||||||
swap=500,
|
|
||||||
rxtx_quota=30000,
|
|
||||||
rxtx_cap=200)
|
|
||||||
|
|
||||||
self.zone_manager = FakeZoneManager()
|
|
||||||
states = {}
|
|
||||||
for x in xrange(10):
|
|
||||||
states['host%02d' % (x + 1)] = {'compute': self._host_caps(x)}
|
|
||||||
self.zone_manager.service_states = states
|
|
||||||
|
|
||||||
def tearDown(self):
|
|
||||||
FLAGS.default_host_filter_driver = self.old_flag
|
|
||||||
super(HostFilterTestCase, self).tearDown()
|
|
||||||
|
|
||||||
def test_choose_driver(self):
|
|
||||||
# Test default driver ...
|
|
||||||
driver = host_filter.choose_driver()
|
|
||||||
self.assertEquals(driver._full_name(),
|
|
||||||
'nova.scheduler.host_filter.AllHostsFilter')
|
|
||||||
# Test valid driver ...
|
|
||||||
driver = host_filter.choose_driver(
|
|
||||||
'nova.scheduler.host_filter.InstanceTypeFilter')
|
|
||||||
self.assertEquals(driver._full_name(),
|
|
||||||
'nova.scheduler.host_filter.InstanceTypeFilter')
|
|
||||||
# Test invalid driver ...
|
|
||||||
try:
|
|
||||||
host_filter.choose_driver('does not exist')
|
|
||||||
self.fail("Should not find driver")
|
|
||||||
except exception.SchedulerHostFilterDriverNotFound:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def test_all_host_driver(self):
|
|
||||||
driver = host_filter.AllHostsFilter()
|
|
||||||
cooked = driver.instance_type_to_filter(self.instance_type)
|
|
||||||
hosts = driver.filter_hosts(self.zone_manager, cooked)
|
|
||||||
self.assertEquals(10, len(hosts))
|
|
||||||
for host, capabilities in hosts:
|
|
||||||
self.assertTrue(host.startswith('host'))
|
|
||||||
|
|
||||||
def test_instance_type_driver(self):
|
|
||||||
driver = host_filter.InstanceTypeFilter()
|
|
||||||
# filter all hosts that can support 50 ram and 500 disk
|
|
||||||
name, cooked = driver.instance_type_to_filter(self.instance_type)
|
|
||||||
self.assertEquals('nova.scheduler.host_filter.InstanceTypeFilter',
|
|
||||||
name)
|
|
||||||
hosts = driver.filter_hosts(self.zone_manager, cooked)
|
|
||||||
self.assertEquals(6, len(hosts))
|
|
||||||
just_hosts = [host for host, caps in hosts]
|
|
||||||
just_hosts.sort()
|
|
||||||
self.assertEquals('host05', just_hosts[0])
|
|
||||||
self.assertEquals('host10', just_hosts[5])
|
|
||||||
|
|
||||||
def test_json_driver(self):
|
|
||||||
driver = host_filter.JsonFilter()
|
|
||||||
# filter all hosts that can support 50 ram and 500 disk
|
|
||||||
name, cooked = driver.instance_type_to_filter(self.instance_type)
|
|
||||||
self.assertEquals('nova.scheduler.host_filter.JsonFilter', name)
|
|
||||||
hosts = driver.filter_hosts(self.zone_manager, cooked)
|
|
||||||
self.assertEquals(6, len(hosts))
|
|
||||||
just_hosts = [host for host, caps in hosts]
|
|
||||||
just_hosts.sort()
|
|
||||||
self.assertEquals('host05', just_hosts[0])
|
|
||||||
self.assertEquals('host10', just_hosts[5])
|
|
||||||
|
|
||||||
# Try some custom queries
|
|
||||||
|
|
||||||
raw = ['or',
|
|
||||||
['and',
|
|
||||||
['<', '$compute.host_memory_free', 30],
|
|
||||||
['<', '$compute.disk_available', 300]
|
|
||||||
],
|
|
||||||
['and',
|
|
||||||
['>', '$compute.host_memory_free', 70],
|
|
||||||
['>', '$compute.disk_available', 700]
|
|
||||||
]
|
|
||||||
]
|
|
||||||
cooked = json.dumps(raw)
|
|
||||||
hosts = driver.filter_hosts(self.zone_manager, cooked)
|
|
||||||
|
|
||||||
self.assertEquals(5, len(hosts))
|
|
||||||
just_hosts = [host for host, caps in hosts]
|
|
||||||
just_hosts.sort()
|
|
||||||
for index, host in zip([1, 2, 8, 9, 10], just_hosts):
|
|
||||||
self.assertEquals('host%02d' % index, host)
|
|
||||||
|
|
||||||
raw = ['not',
|
|
||||||
['=', '$compute.host_memory_free', 30],
|
|
||||||
]
|
|
||||||
cooked = json.dumps(raw)
|
|
||||||
hosts = driver.filter_hosts(self.zone_manager, cooked)
|
|
||||||
|
|
||||||
self.assertEquals(9, len(hosts))
|
|
||||||
just_hosts = [host for host, caps in hosts]
|
|
||||||
just_hosts.sort()
|
|
||||||
for index, host in zip([1, 2, 4, 5, 6, 7, 8, 9, 10], just_hosts):
|
|
||||||
self.assertEquals('host%02d' % index, host)
|
|
||||||
|
|
||||||
raw = ['in', '$compute.host_memory_free', 20, 40, 60, 80, 100]
|
|
||||||
cooked = json.dumps(raw)
|
|
||||||
hosts = driver.filter_hosts(self.zone_manager, cooked)
|
|
||||||
|
|
||||||
self.assertEquals(5, len(hosts))
|
|
||||||
just_hosts = [host for host, caps in hosts]
|
|
||||||
just_hosts.sort()
|
|
||||||
for index, host in zip([2, 4, 6, 8, 10], just_hosts):
|
|
||||||
self.assertEquals('host%02d' % index, host)
|
|
||||||
|
|
||||||
# Try some bogus input ...
|
|
||||||
raw = ['unknown command', ]
|
|
||||||
cooked = json.dumps(raw)
|
|
||||||
try:
|
|
||||||
driver.filter_hosts(self.zone_manager, cooked)
|
|
||||||
self.fail("Should give KeyError")
|
|
||||||
except KeyError, e:
|
|
||||||
pass
|
|
||||||
|
|
||||||
self.assertTrue(driver.filter_hosts(self.zone_manager, json.dumps([])))
|
|
||||||
self.assertTrue(driver.filter_hosts(self.zone_manager, json.dumps({})))
|
|
||||||
self.assertTrue(driver.filter_hosts(self.zone_manager, json.dumps(
|
|
||||||
['not', True, False, True, False]
|
|
||||||
)))
|
|
||||||
|
|
||||||
try:
|
|
||||||
driver.filter_hosts(self.zone_manager, json.dumps(
|
|
||||||
'not', True, False, True, False
|
|
||||||
))
|
|
||||||
self.fail("Should give KeyError")
|
|
||||||
except KeyError, e:
|
|
||||||
pass
|
|
||||||
|
|
||||||
self.assertFalse(driver.filter_hosts(self.zone_manager, json.dumps(
|
|
||||||
['=', '$foo', 100]
|
|
||||||
)))
|
|
||||||
self.assertFalse(driver.filter_hosts(self.zone_manager, json.dumps(
|
|
||||||
['=', '$.....', 100]
|
|
||||||
)))
|
|
||||||
self.assertFalse(driver.filter_hosts(self.zone_manager, json.dumps(
|
|
||||||
['>', ['and', ['or', ['not', ['<', ['>=', ['<=', ['in', ]]]]]]]]
|
|
||||||
)))
|
|
||||||
|
|
||||||
self.assertFalse(driver.filter_hosts(self.zone_manager, json.dumps(
|
|
||||||
['=', {}, ['>', '$missing....foo']]
|
|
||||||
)))
|
|
||||||
@@ -1,181 +0,0 @@
|
|||||||
# Copyright 2011 OpenStack LLC.
|
|
||||||
# All Rights Reserved.
|
|
||||||
#
|
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
||||||
# not use this file except in compliance with the License. You may obtain
|
|
||||||
# a copy of the License at
|
|
||||||
#
|
|
||||||
# http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
#
|
|
||||||
# Unless required by applicable law or agreed to in writing, software
|
|
||||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
||||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
||||||
# License for the specific language governing permissions and limitations
|
|
||||||
# under the License.
|
|
||||||
"""
|
|
||||||
Tests For Least Cost Scheduler
|
|
||||||
"""
|
|
||||||
|
|
||||||
from nova import flags
|
|
||||||
from nova import test
|
|
||||||
from nova.scheduler import least_cost
|
|
||||||
|
|
||||||
MB = 1024 * 1024
|
|
||||||
FLAGS = flags.FLAGS
|
|
||||||
|
|
||||||
|
|
||||||
class FakeHost(object):
|
|
||||||
def __init__(self, host_id, free_ram, io):
|
|
||||||
self.id = host_id
|
|
||||||
self.free_ram = free_ram
|
|
||||||
self.io = io
|
|
||||||
|
|
||||||
|
|
||||||
class WeightedSumTestCase(test.TestCase):
|
|
||||||
def test_empty_domain(self):
|
|
||||||
domain = []
|
|
||||||
weighted_fns = []
|
|
||||||
result = least_cost.weighted_sum(domain, weighted_fns)
|
|
||||||
expected = []
|
|
||||||
self.assertEqual(expected, result)
|
|
||||||
|
|
||||||
def test_basic_costing(self):
|
|
||||||
hosts = [
|
|
||||||
FakeHost(1, 512 * MB, 100),
|
|
||||||
FakeHost(2, 256 * MB, 400),
|
|
||||||
FakeHost(3, 512 * MB, 100)
|
|
||||||
]
|
|
||||||
|
|
||||||
weighted_fns = [
|
|
||||||
(1, lambda h: h.free_ram), # Fill-first, free_ram is a *cost*
|
|
||||||
(2, lambda h: h.io), # Avoid high I/O
|
|
||||||
]
|
|
||||||
|
|
||||||
costs = least_cost.weighted_sum(
|
|
||||||
domain=hosts, weighted_fns=weighted_fns)
|
|
||||||
|
|
||||||
# Each 256 MB unit of free-ram contributes 0.5 points by way of:
|
|
||||||
# cost = weight * (score/max_score) = 1 * (256/512) = 0.5
|
|
||||||
# Each 100 iops of IO adds 0.5 points by way of:
|
|
||||||
# cost = 2 * (100/400) = 2 * 0.25 = 0.5
|
|
||||||
expected = [1.5, 2.5, 1.5]
|
|
||||||
self.assertEqual(expected, costs)
|
|
||||||
|
|
||||||
|
|
||||||
# TODO(sirp): unify this with test_host_filter tests? possibility of sharing
|
|
||||||
# test setup code
|
|
||||||
class FakeZoneManager:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class LeastCostSchedulerTestCase(test.TestCase):
|
|
||||||
def _host_caps(self, multiplier):
|
|
||||||
# Returns host capabilities in the following way:
|
|
||||||
# host1 = memory:free 10 (100max)
|
|
||||||
# disk:available 100 (1000max)
|
|
||||||
# hostN = memory:free 10 + 10N
|
|
||||||
# disk:available 100 + 100N
|
|
||||||
# in other words: hostN has more resources than host0
|
|
||||||
# which means ... don't go above 10 hosts.
|
|
||||||
return {'host_name-description': 'XenServer %s' % multiplier,
|
|
||||||
'host_hostname': 'xs-%s' % multiplier,
|
|
||||||
'host_memory_total': 100,
|
|
||||||
'host_memory_overhead': 10,
|
|
||||||
'host_memory_free': 10 + multiplier * 10,
|
|
||||||
'host_memory_free-computed': 10 + multiplier * 10,
|
|
||||||
'host_other-config': {},
|
|
||||||
'host_ip_address': '192.168.1.%d' % (100 + multiplier),
|
|
||||||
'host_cpu_info': {},
|
|
||||||
'disk_available': 100 + multiplier * 100,
|
|
||||||
'disk_total': 1000,
|
|
||||||
'disk_used': 0,
|
|
||||||
'host_uuid': 'xxx-%d' % multiplier,
|
|
||||||
'host_name-label': 'xs-%s' % multiplier}
|
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
super(LeastCostSchedulerTestCase, self).setUp()
|
|
||||||
#self.old_flag = FLAGS.default_host_filter_driver
|
|
||||||
#FLAGS.default_host_filter_driver = \
|
|
||||||
# 'nova.scheduler.host_filter.AllHostsFilter'
|
|
||||||
self.instance_type = dict(name='tiny',
|
|
||||||
memory_mb=50,
|
|
||||||
vcpus=10,
|
|
||||||
local_gb=500,
|
|
||||||
flavorid=1,
|
|
||||||
swap=500,
|
|
||||||
rxtx_quota=30000,
|
|
||||||
rxtx_cap=200)
|
|
||||||
|
|
||||||
zone_manager = FakeZoneManager()
|
|
||||||
states = {}
|
|
||||||
for x in xrange(10):
|
|
||||||
states['host%02d' % (x + 1)] = {'compute': self._host_caps(x)}
|
|
||||||
zone_manager.service_states = states
|
|
||||||
|
|
||||||
self.sched = least_cost.LeastCostScheduler()
|
|
||||||
self.sched.zone_manager = zone_manager
|
|
||||||
|
|
||||||
def tearDown(self):
|
|
||||||
#FLAGS.default_host_filter_driver = self.old_flag
|
|
||||||
super(LeastCostSchedulerTestCase, self).tearDown()
|
|
||||||
|
|
||||||
def assertWeights(self, expected, num, request_spec, hosts):
|
|
||||||
weighted = self.sched.weigh_hosts(num, request_spec, hosts)
|
|
||||||
self.assertDictListMatch(weighted, expected, approx_equal=True)
|
|
||||||
|
|
||||||
def test_no_hosts(self):
|
|
||||||
num = 1
|
|
||||||
request_spec = {}
|
|
||||||
hosts = []
|
|
||||||
|
|
||||||
expected = []
|
|
||||||
self.assertWeights(expected, num, request_spec, hosts)
|
|
||||||
|
|
||||||
def test_noop_cost_fn(self):
|
|
||||||
FLAGS.least_cost_scheduler_cost_functions = [
|
|
||||||
'nova.scheduler.least_cost.noop_cost_fn'
|
|
||||||
]
|
|
||||||
FLAGS.noop_cost_fn_weight = 1
|
|
||||||
|
|
||||||
num = 1
|
|
||||||
request_spec = {}
|
|
||||||
hosts = self.sched.filter_hosts(num, request_spec)
|
|
||||||
|
|
||||||
expected = [dict(weight=1, hostname=hostname)
|
|
||||||
for hostname, caps in hosts]
|
|
||||||
self.assertWeights(expected, num, request_spec, hosts)
|
|
||||||
|
|
||||||
def test_cost_fn_weights(self):
|
|
||||||
FLAGS.least_cost_scheduler_cost_functions = [
|
|
||||||
'nova.scheduler.least_cost.noop_cost_fn'
|
|
||||||
]
|
|
||||||
FLAGS.noop_cost_fn_weight = 2
|
|
||||||
|
|
||||||
num = 1
|
|
||||||
request_spec = {}
|
|
||||||
hosts = self.sched.filter_hosts(num, request_spec)
|
|
||||||
|
|
||||||
expected = [dict(weight=2, hostname=hostname)
|
|
||||||
for hostname, caps in hosts]
|
|
||||||
self.assertWeights(expected, num, request_spec, hosts)
|
|
||||||
|
|
||||||
def test_fill_first_cost_fn(self):
|
|
||||||
FLAGS.least_cost_scheduler_cost_functions = [
|
|
||||||
'nova.scheduler.least_cost.fill_first_cost_fn'
|
|
||||||
]
|
|
||||||
FLAGS.fill_first_cost_fn_weight = 1
|
|
||||||
|
|
||||||
num = 1
|
|
||||||
request_spec = {}
|
|
||||||
hosts = self.sched.filter_hosts(num, request_spec)
|
|
||||||
|
|
||||||
expected = []
|
|
||||||
for idx, (hostname, caps) in enumerate(hosts):
|
|
||||||
# Costs are normalized so over 10 hosts, each host with increasing
|
|
||||||
# free ram will cost 1/N more. Since the lowest cost host has some
|
|
||||||
# free ram, we add in the 1/N for the base_cost
|
|
||||||
weight = 0.1 + (0.1 * idx)
|
|
||||||
weight_dict = dict(weight=weight, hostname=hostname)
|
|
||||||
expected.append(weight_dict)
|
|
||||||
|
|
||||||
self.assertWeights(expected, num, request_spec, hosts)
|
|
||||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user