end of day
This commit is contained in:
@@ -13,6 +13,7 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from abstract_filter import AbstractHostFilter
|
||||
from all_hosts_filter import AllHostsFilter
|
||||
from instance_type_filter import InstanceTypeFilter
|
||||
from json_filter import JsonFilter
|
||||
|
||||
@@ -13,44 +13,15 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""
|
||||
The Host Filter classes are a way to ensure that only hosts that are
|
||||
appropriate are considered when creating a new instance. Hosts that are
|
||||
either incompatible or insufficient to accept a newly-requested instance
|
||||
are removed by Host Filter classes from consideration. Those that pass
|
||||
the filter are then passed on for weighting or other process for ordering.
|
||||
|
||||
Three filters are included: AllHosts, Flavor & JSON. AllHosts just
|
||||
returns the full, unfiltered list of hosts. Flavor is a hard coded
|
||||
matching mechanism based on flavor criteria and JSON is an ad-hoc
|
||||
filter grammar.
|
||||
|
||||
Why JSON? The requests for instances may come in through the
|
||||
REST interface from a user or a parent Zone.
|
||||
Currently Flavors and/or InstanceTypes are used for
|
||||
specifing the type of instance desired. Specific Nova users have
|
||||
noted a need for a more expressive way of specifying instances.
|
||||
Since we don't want to get into building full DSL this is a simple
|
||||
form as an example of how this could be done. In reality, most
|
||||
consumers will use the more rigid filters such as FlavorFilter.
|
||||
"""
|
||||
|
||||
import json
|
||||
|
||||
from nova import exception
|
||||
from nova import flags
|
||||
from nova import log as logging
|
||||
|
||||
import nova.scheduler
|
||||
from nova import flags
|
||||
|
||||
|
||||
LOG = logging.getLogger('nova.scheduler.host_filter')
|
||||
FLAGS = flags.FLAGS
|
||||
flags.DEFINE_string('default_host_filter',
|
||||
'nova.scheduler.host_filter.AllHostsFilter',
|
||||
'nova.scheduler.filters.AllHostsFilter',
|
||||
'Which filter to use for filtering hosts')
|
||||
|
||||
|
||||
class AbstractHostFilter(object):
|
||||
"""Base class for host filters."""
|
||||
def instance_type_to_filter(self, instance_type):
|
||||
@@ -64,24 +35,3 @@ class AbstractHostFilter(object):
|
||||
def _full_name(self):
|
||||
"""module.classname of the filter."""
|
||||
return "%s.%s" % (self.__module__, self.__class__.__name__)
|
||||
|
||||
|
||||
def _get_filters():
|
||||
from nova.scheduler import filters
|
||||
return [itm for itm in dir(filters)
|
||||
if issubclass(itm, AbstractHostFilter)]
|
||||
|
||||
|
||||
def choose_host_filter(filter_name=None):
|
||||
"""Since the caller may specify which filter to use we need
|
||||
to have an authoritative list of what is permissible. This
|
||||
function checks the filter name against a predefined set
|
||||
of acceptable filters.
|
||||
"""
|
||||
if not filter_name:
|
||||
filter_name = FLAGS.default_host_filter
|
||||
for filter_class in _get_filters():
|
||||
host_match = "%s.%s" % (filter_class.__module__, filter_class.__name__)
|
||||
if host_match == filter_name:
|
||||
return filter_class()
|
||||
raise exception.SchedulerHostFilterNotFound(filter_name=filter_name)
|
||||
|
||||
@@ -15,9 +15,10 @@
|
||||
|
||||
|
||||
import nova.scheduler
|
||||
from nova.scheduler.filters import abstract_filter
|
||||
|
||||
|
||||
class AllHostsFilter(nova.scheduler.host_filter.AbstractHostFilter):
|
||||
class AllHostsFilter(abstract_filter.AbstractHostFilter):
|
||||
"""NOP host filter. Returns all hosts in ZoneManager."""
|
||||
def instance_type_to_filter(self, instance_type):
|
||||
"""Return anything to prevent base-class from raising
|
||||
|
||||
@@ -14,10 +14,11 @@
|
||||
# under the License.
|
||||
|
||||
|
||||
from nova.scheduler import host_filter
|
||||
import nova.scheduler
|
||||
from nova.scheduler.filters import abstract_filter
|
||||
|
||||
|
||||
class InstanceTypeFilter(host_filter.AbstractHostFilter):
|
||||
class InstanceTypeFilter(abstract_filter.AbstractHostFilter):
|
||||
"""HostFilter hard-coded to work with InstanceType records."""
|
||||
def instance_type_to_filter(self, instance_type):
|
||||
"""Use instance_type to filter hosts."""
|
||||
|
||||
@@ -14,49 +14,64 @@
|
||||
# under the License.
|
||||
|
||||
|
||||
import json
|
||||
import operator
|
||||
|
||||
from nova.scheduler import host_filter
|
||||
import nova.scheduler
|
||||
from nova.scheduler.filters import abstract_filter
|
||||
|
||||
def debug(*args):
|
||||
with file("/tmp/debug", "a") as dbg:
|
||||
msg = " ".join([str(arg) for arg in args])
|
||||
dbg.write("%s\n" % msg)
|
||||
|
||||
|
||||
class JsonFilter(host_filter.AbstractHostFilter):
|
||||
class JsonFilter(abstract_filter.AbstractHostFilter):
|
||||
"""Host Filter to allow simple JSON-based grammar for
|
||||
selecting hosts.
|
||||
"""
|
||||
def _op_comp(self, args, op):
|
||||
def _op_compare(self, args, op):
|
||||
"""Returns True if the specified operator can successfully
|
||||
compare the first item in the args with all the rest. Will
|
||||
return False if only one item is in the list.
|
||||
"""
|
||||
if len(args) < 2:
|
||||
return False
|
||||
if op is operator.contains:
|
||||
debug("ARGS", type(args), args)
|
||||
debug("op", op)
|
||||
debug("REVERSED!!!")
|
||||
# operator.contains reverses the param order.
|
||||
bad = [arg for arg in args[1:]
|
||||
if not op(args, args[0])]
|
||||
else:
|
||||
bad = [arg for arg in args[1:]
|
||||
if not op(args[0], arg)]
|
||||
return not bool(bad)
|
||||
|
||||
def _equals(self, args):
|
||||
"""First term is == all the other terms."""
|
||||
return self._op_comp(args, operator.eq)
|
||||
return self._op_compare(args, operator.eq)
|
||||
|
||||
def _less_than(self, args):
|
||||
"""First term is < all the other terms."""
|
||||
return self._op_comp(args, operator.lt)
|
||||
return self._op_compare(args, operator.lt)
|
||||
|
||||
def _greater_than(self, args):
|
||||
"""First term is > all the other terms."""
|
||||
return self._op_comp(args, operator.gt)
|
||||
return self._op_compare(args, operator.gt)
|
||||
|
||||
def _in(self, args):
|
||||
"""First term is in set of remaining terms"""
|
||||
return self._op_comp(args, operator.contains)
|
||||
return self._op_compare(args, operator.contains)
|
||||
|
||||
def _less_than_equal(self, args):
|
||||
"""First term is <= all the other terms."""
|
||||
return self._op_comp(args, operator.le)
|
||||
return self._op_compare(args, operator.le)
|
||||
|
||||
def _greater_than_equal(self, args):
|
||||
"""First term is >= all the other terms."""
|
||||
return self._op_comp(args, operator.ge)
|
||||
return self._op_compare(args, operator.ge)
|
||||
|
||||
def _not(self, args):
|
||||
"""Flip each of the arguments."""
|
||||
@@ -129,6 +144,8 @@ class JsonFilter(host_filter.AbstractHostFilter):
|
||||
specified in the query.
|
||||
"""
|
||||
expanded = json.loads(query)
|
||||
|
||||
debug("expanded", type(expanded), expanded)
|
||||
filtered_hosts = []
|
||||
for host, services in zone_manager.service_states.iteritems():
|
||||
result = self._process_filter(zone_manager, expanded, host,
|
||||
|
||||
75
nova/scheduler/host_filter.py
Normal file
75
nova/scheduler/host_filter.py
Normal file
@@ -0,0 +1,75 @@
|
||||
# Copyright (c) 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.
|
||||
|
||||
"""
|
||||
The Host Filter classes are a way to ensure that only hosts that are
|
||||
appropriate are considered when creating a new instance. Hosts that are
|
||||
either incompatible or insufficient to accept a newly-requested instance
|
||||
are removed by Host Filter classes from consideration. Those that pass
|
||||
the filter are then passed on for weighting or other process for ordering.
|
||||
|
||||
Three filters are included: AllHosts, Flavor & JSON. AllHosts just
|
||||
returns the full, unfiltered list of hosts. Flavor is a hard coded
|
||||
matching mechanism based on flavor criteria and JSON is an ad-hoc
|
||||
filter grammar.
|
||||
|
||||
Why JSON? The requests for instances may come in through the
|
||||
REST interface from a user or a parent Zone.
|
||||
Currently Flavors and/or InstanceTypes are used for
|
||||
specifing the type of instance desired. Specific Nova users have
|
||||
noted a need for a more expressive way of specifying instances.
|
||||
Since we don't want to get into building full DSL this is a simple
|
||||
form as an example of how this could be done. In reality, most
|
||||
consumers will use the more rigid filters such as FlavorFilter.
|
||||
"""
|
||||
|
||||
import json
|
||||
import types
|
||||
|
||||
from nova import exception
|
||||
from nova import flags
|
||||
from nova import log as logging
|
||||
|
||||
import nova.scheduler
|
||||
|
||||
|
||||
LOG = logging.getLogger('nova.scheduler.host_filter')
|
||||
FLAGS = flags.FLAGS
|
||||
|
||||
|
||||
def _get_filters():
|
||||
from nova.scheduler import filters
|
||||
def get_itm(nm):
|
||||
return getattr(filters, nm)
|
||||
|
||||
return [get_itm(itm) for itm in dir(filters)
|
||||
if (type(get_itm(itm)) is types.TypeType)
|
||||
and issubclass(get_itm(itm), filters.AbstractHostFilter)]
|
||||
|
||||
|
||||
def choose_host_filter(filter_name=None):
|
||||
"""Since the caller may specify which filter to use we need
|
||||
to have an authoritative list of what is permissible. This
|
||||
function checks the filter name against a predefined set
|
||||
of acceptable filters.
|
||||
"""
|
||||
if not filter_name:
|
||||
filter_name = FLAGS.default_host_filter
|
||||
for filter_class in _get_filters():
|
||||
host_match = "%s.%s" % (filter_class.__module__, filter_class.__name__)
|
||||
if (host_match.startswith("nova.scheduler.filters") and
|
||||
(host_match.split(".")[-1] == filter_name)):
|
||||
return filter_class()
|
||||
raise exception.SchedulerHostFilterNotFound(filter_name=filter_name)
|
||||
@@ -20,6 +20,7 @@ import json
|
||||
|
||||
from nova import exception
|
||||
from nova import test
|
||||
from nova.scheduler import host_filter
|
||||
from nova.scheduler import filters
|
||||
|
||||
|
||||
@@ -55,7 +56,7 @@ class HostFilterTestCase(test.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(HostFilterTestCase, self).setUp()
|
||||
default_host_filter = 'nova.scheduler.filteris.AllHostsFilter'
|
||||
default_host_filter = 'AllHostsFilter'
|
||||
self.flags(default_host_filter=default_host_filter)
|
||||
self.instance_type = dict(name='tiny',
|
||||
memory_mb=50,
|
||||
@@ -98,13 +99,10 @@ class HostFilterTestCase(test.TestCase):
|
||||
def test_choose_filter(self):
|
||||
# Test default filter ...
|
||||
hf = host_filter.choose_host_filter()
|
||||
self.assertEquals(hf._full_name(),
|
||||
'nova.scheduler.host_filter.AllHostsFilter')
|
||||
self.assertEquals(hf._full_name().split(".")[-1], 'AllHostsFilter')
|
||||
# Test valid filter ...
|
||||
hf = host_filter.choose_host_filter(
|
||||
'nova.scheduler.host_filter.InstanceTypeFilter')
|
||||
self.assertEquals(hf._full_name(),
|
||||
'nova.scheduler.host_filter.InstanceTypeFilter')
|
||||
hf = host_filter.choose_host_filter('InstanceTypeFilter')
|
||||
self.assertEquals(hf._full_name().split(".")[-1], 'InstanceTypeFilter')
|
||||
# Test invalid filter ...
|
||||
try:
|
||||
host_filter.choose_host_filter('does not exist')
|
||||
@@ -113,7 +111,7 @@ class HostFilterTestCase(test.TestCase):
|
||||
pass
|
||||
|
||||
def test_all_host_filter(self):
|
||||
hf = host_filter.AllHostsFilter()
|
||||
hf = filters.AllHostsFilter()
|
||||
cooked = hf.instance_type_to_filter(self.instance_type)
|
||||
hosts = hf.filter_hosts(self.zone_manager, cooked)
|
||||
self.assertEquals(10, len(hosts))
|
||||
@@ -121,11 +119,10 @@ class HostFilterTestCase(test.TestCase):
|
||||
self.assertTrue(host.startswith('host'))
|
||||
|
||||
def test_instance_type_filter(self):
|
||||
hf = host_filter.InstanceTypeFilter()
|
||||
hf = filters.InstanceTypeFilter()
|
||||
# filter all hosts that can support 50 ram and 500 disk
|
||||
name, cooked = hf.instance_type_to_filter(self.instance_type)
|
||||
self.assertEquals('nova.scheduler.host_filter.InstanceTypeFilter',
|
||||
name)
|
||||
self.assertEquals(name.split(".")[-1], 'InstanceTypeFilter')
|
||||
hosts = hf.filter_hosts(self.zone_manager, cooked)
|
||||
self.assertEquals(6, len(hosts))
|
||||
just_hosts = [host for host, caps in hosts]
|
||||
@@ -134,21 +131,20 @@ class HostFilterTestCase(test.TestCase):
|
||||
self.assertEquals('host10', just_hosts[5])
|
||||
|
||||
def test_instance_type_filter_extra_specs(self):
|
||||
hf = host_filter.InstanceTypeFilter()
|
||||
hf = filters.InstanceTypeFilter()
|
||||
# filter all hosts that can support 50 ram and 500 disk
|
||||
name, cooked = hf.instance_type_to_filter(self.gpu_instance_type)
|
||||
self.assertEquals('nova.scheduler.host_filter.InstanceTypeFilter',
|
||||
name)
|
||||
self.assertEquals(name.split(".")[-1], 'InstanceTypeFilter')
|
||||
hosts = hf.filter_hosts(self.zone_manager, cooked)
|
||||
self.assertEquals(1, len(hosts))
|
||||
just_hosts = [host for host, caps in hosts]
|
||||
self.assertEquals('host07', just_hosts[0])
|
||||
|
||||
def test_json_filter(self):
|
||||
hf = host_filter.JsonFilter()
|
||||
hf = filters.JsonFilter()
|
||||
# filter all hosts that can support 50 ram and 500 disk
|
||||
name, cooked = hf.instance_type_to_filter(self.instance_type)
|
||||
self.assertEquals('nova.scheduler.host_filter.JsonFilter', name)
|
||||
self.assertEquals(name.split(".")[-1], 'JsonFilter')
|
||||
hosts = hf.filter_hosts(self.zone_manager, cooked)
|
||||
self.assertEquals(6, len(hosts))
|
||||
just_hosts = [host for host, caps in hosts]
|
||||
@@ -191,6 +187,12 @@ class HostFilterTestCase(test.TestCase):
|
||||
|
||||
raw = ['in', '$compute.host_memory_free', 20, 40, 60, 80, 100]
|
||||
cooked = json.dumps(raw)
|
||||
def debug(*args):
|
||||
with file("/tmp/debug", "a") as dbg:
|
||||
msg = " ".join([str(arg) for arg in args])
|
||||
dbg.write("%s\n" % msg)
|
||||
|
||||
debug("cooked", cooked, type(cooked))
|
||||
hosts = hf.filter_hosts(self.zone_manager, cooked)
|
||||
|
||||
self.assertEquals(5, len(hosts))
|
||||
|
||||
Reference in New Issue
Block a user