# 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 Filters. """ import json import nova from nova import exception from nova import test from nova.scheduler import distributed_scheduler as dist from nova.scheduler import zone_manager from nova.tests.scheduler import fake_zone_manager as ds_fakes class HostFilterTestCase(test.TestCase): """Test case for host filters.""" 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, 'enabled': True} def setUp(self): super(HostFilterTestCase, self).setUp() default_host_filters = ['AllHostsFilter'] self.flags(default_host_filters=default_host_filters) self.instance_type = dict(name='tiny', memory_mb=30, vcpus=10, local_gb=300, flavorid=1, swap=500, rxtx_quota=30000, rxtx_cap=200, extra_specs={}) self.gpu_instance_type = dict(name='tiny.gpu', memory_mb=30, vcpus=10, local_gb=300, flavorid=2, swap=500, rxtx_quota=30000, rxtx_cap=200, extra_specs={'xpu_arch': 'fermi', 'xpu_info': 'Tesla 2050'}) self.zone_manager = ds_fakes.FakeZoneManager() states = {} for x in xrange(4): states['host%d' % (x + 1)] = {'compute': self._host_caps(x)} self.zone_manager.service_states = states # Add some extra capabilities to some hosts host4 = self.zone_manager.service_states['host4']['compute'] host4['xpu_arch'] = 'fermi' host4['xpu_info'] = 'Tesla 2050' host2 = self.zone_manager.service_states['host2']['compute'] host2['xpu_arch'] = 'radeon' host3 = self.zone_manager.service_states['host3']['compute'] host3['xpu_arch'] = 'fermi' host3['xpu_info'] = 'Tesla 2150' def _get_all_hosts(self): return self.zone_manager.get_all_host_data(None).items() def test_choose_filter(self): # Test default filter ... sched = dist.DistributedScheduler() hfs = sched._choose_host_filters() hf = hfs[0] self.assertEquals(hf._full_name().split(".")[-1], 'AllHostsFilter') # Test valid filter ... hfs = sched._choose_host_filters('InstanceTypeFilter') hf = hfs[0] self.assertEquals(hf._full_name().split(".")[-1], 'InstanceTypeFilter') # Test invalid filter ... try: sched._choose_host_filters('does not exist') self.fail("Should not find host filter.") except exception.SchedulerHostFilterNotFound: pass def test_all_host_filter(self): sched = dist.DistributedScheduler() hfs = sched._choose_host_filters('AllHostsFilter') hf = hfs[0] all_hosts = self._get_all_hosts() cooked = hf.instance_type_to_filter(self.instance_type) hosts = hf.filter_hosts(all_hosts, cooked) self.assertEquals(4, len(hosts)) for host, capabilities in hosts: self.assertTrue(host.startswith('host')) def test_instance_type_filter(self): hf = nova.scheduler.filters.InstanceTypeFilter() # filter all hosts that can support 30 ram and 300 disk cooked = hf.instance_type_to_filter(self.instance_type) all_hosts = self._get_all_hosts() hosts = hf.filter_hosts(all_hosts, cooked) self.assertEquals(3, len(hosts)) just_hosts = [host for host, hostinfo in hosts] just_hosts.sort() self.assertEquals('host4', just_hosts[2]) self.assertEquals('host3', just_hosts[1]) self.assertEquals('host2', just_hosts[0]) def test_instance_type_filter_extra_specs(self): hf = nova.scheduler.filters.InstanceTypeFilter() # filter all hosts that can support 30 ram and 300 disk cooked = hf.instance_type_to_filter(self.gpu_instance_type) all_hosts = self._get_all_hosts() hosts = hf.filter_hosts(all_hosts, cooked) self.assertEquals(1, len(hosts)) just_hosts = [host for host, caps in hosts] self.assertEquals('host4', just_hosts[0]) def test_json_filter(self): hf = nova.scheduler.filters.JsonFilter() # filter all hosts that can support 30 ram and 300 disk cooked = hf.instance_type_to_filter(self.instance_type) all_hosts = self._get_all_hosts() hosts = hf.filter_hosts(all_hosts, cooked) self.assertEquals(2, len(hosts)) just_hosts = [host for host, caps in hosts] just_hosts.sort() self.assertEquals('host3', just_hosts[0]) self.assertEquals('host4', just_hosts[1]) # Try some custom queries raw = ['or', ['and', ['<', '$compute.host_memory_free', 30], ['<', '$compute.disk_available', 300], ], ['and', ['>', '$compute.host_memory_free', 30], ['>', '$compute.disk_available', 300], ] ] cooked = json.dumps(raw) hosts = hf.filter_hosts(all_hosts, cooked) self.assertEquals(3, len(hosts)) just_hosts = [host for host, caps in hosts] just_hosts.sort() for index, host in zip([1, 2, 4], just_hosts): self.assertEquals('host%d' % index, host) raw = ['not', ['=', '$compute.host_memory_free', 30], ] cooked = json.dumps(raw) hosts = hf.filter_hosts(all_hosts, cooked) self.assertEquals(3, len(hosts)) just_hosts = [host for host, caps in hosts] just_hosts.sort() for index, host in zip([1, 2, 4], just_hosts): self.assertEquals('host%d' % index, host) raw = ['in', '$compute.host_memory_free', 20, 40, 60, 80, 100] cooked = json.dumps(raw) hosts = hf.filter_hosts(all_hosts, cooked) self.assertEquals(2, len(hosts)) just_hosts = [host for host, caps in hosts] just_hosts.sort() for index, host in zip([2, 4], just_hosts): self.assertEquals('host%d' % index, host) # Try some bogus input ... raw = ['unknown command', ] cooked = json.dumps(raw) try: hf.filter_hosts(all_hosts, cooked) self.fail("Should give KeyError") except KeyError, e: pass self.assertTrue(hf.filter_hosts(all_hosts, json.dumps([]))) self.assertTrue(hf.filter_hosts(all_hosts, json.dumps({}))) self.assertTrue(hf.filter_hosts(all_hosts, json.dumps( ['not', True, False, True, False], ))) try: hf.filter_hosts(all_hosts, json.dumps( 'not', True, False, True, False, )) self.fail("Should give KeyError") except KeyError, e: pass self.assertFalse(hf.filter_hosts(all_hosts, json.dumps(['=', '$foo', 100]))) self.assertFalse(hf.filter_hosts(all_hosts, json.dumps(['=', '$.....', 100]))) self.assertFalse(hf.filter_hosts(all_hosts, json.dumps( ['>', ['and', ['or', ['not', ['<', ['>=', ['<=', ['in', ]]]]]]]]))) self.assertFalse(hf.filter_hosts(all_hosts, json.dumps(['=', {}, ['>', '$missing....foo']])))