From fd94dbb2d54a8ec745fd4044dd1560d9486ecf69 Mon Sep 17 00:00:00 2001 From: Sandy Walsh Date: Thu, 5 May 2011 18:09:11 -0700 Subject: [PATCH] terminology: no more plug-ins or queries. They are host filters and drivers. --- nova/scheduler/{query.py => host_filter.py} | 76 +++++++++++-------- .../{test_query.py => test_host_filter.py} | 44 ++++++----- 2 files changed, 66 insertions(+), 54 deletions(-) rename nova/scheduler/{query.py => host_filter.py} (76%) rename nova/tests/{test_query.py => test_host_filter.py} (83%) diff --git a/nova/scheduler/query.py b/nova/scheduler/host_filter.py similarity index 76% rename from nova/scheduler/query.py rename to nova/scheduler/host_filter.py index 1e294b59..aa6101c9 100644 --- a/nova/scheduler/query.py +++ b/nova/scheduler/host_filter.py @@ -14,14 +14,23 @@ # under the License. """ -Query is a plug-in mechanism for requesting instance resources. -Three plug-ins are included: AllHosts, Flavor & JSON. AllHosts just +Host Filter is a driver mechanism for requesting instance resources. +Three drivers 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 -query grammar. +filter grammar. -Note: These are hard filters. All capabilities used must be present -or the host will excluded. If you want soft filters use the weighting +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. + +Note: These are hard filters. All capabilities used must be present +or the host will be excluded. If you want soft filters use the weighting mechanism which is intended for the more touchy-feely capabilities. """ @@ -32,36 +41,36 @@ from nova import flags from nova import log as logging from nova import utils -LOG = logging.getLogger('nova.scheduler.query') +LOG = logging.getLogger('nova.scheduler.host_filter') FLAGS = flags.FLAGS -flags.DEFINE_string('default_query_engine', - 'nova.scheduler.query.AllHostsQuery', - 'Which query engine to use for filtering hosts.') +flags.DEFINE_string('default_host_filter_driver', + 'nova.scheduler.host_filter.AllHostsFilter', + 'Which driver to use for filtering hosts.') -class Query(object): - """Base class for query plug-ins.""" +class HostFilter(object): + """Base class for host filter drivers.""" - def instance_type_to_query(self, instance_type): - """Convert instance_type into a query for most common use-case.""" + def instance_type_to_filter(self, instance_type): + """Convert instance_type into a filter for most common use-case.""" raise NotImplementedError() def filter_hosts(self, zone_manager, query): - """Return a list of hosts that fulfill the query.""" + """Return a list of hosts that fulfill the filter.""" raise NotImplementedError() def _full_name(self): - """module.classname of the Query object""" + """module.classname of the filter driver""" return "%s.%s" % (self.__module__, self.__class__.__name__) -class AllHostsQuery(Query): - """NOP query plug-in. Returns all hosts in ZoneManager. +class AllHostsFilter(HostFilter): + """NOP host filter driver. Returns all hosts in ZoneManager. This essentially does what the old Scheduler+Chance used to give us.""" - def instance_type_to_query(self, instance_type): + def instance_type_to_filter(self, instance_type): """Return anything to prevent base-class from raising exception.""" return (self._full_name(), instance_type) @@ -72,10 +81,10 @@ class AllHostsQuery(Query): for host, services in zone_manager.service_states.iteritems()] -class FlavorQuery(Query): - """Query plug-in hard-coded to work with flavors.""" +class FlavorFilter(HostFilter): + """HostFilter driver hard-coded to work with flavors.""" - def instance_type_to_query(self, instance_type): + def instance_type_to_filter(self, instance_type): """Use instance_type to filter hosts.""" return (self._full_name(), instance_type) @@ -119,8 +128,9 @@ class FlavorQuery(Query): #rxtx_cap = Column(Integer, nullable=False, default=0) -class JsonQuery(Query): - """Query plug-in to allow simple JSON-based grammar for selecting hosts.""" +class JsonFilter(HostFilter): + """Host Filter driver to allow simple JSON-based grammar for + selecting hosts.""" def _equals(self, args): """First term is == all the other terms.""" @@ -204,8 +214,8 @@ class JsonQuery(Query): 'and': _and, } - def instance_type_to_query(self, instance_type): - """Convert instance_type into JSON query object.""" + def instance_type_to_filter(self, instance_type): + """Convert instance_type into JSON filter object.""" required_ram = instance_type['memory_mb'] required_disk = instance_type['local_gb'] query = ['and', @@ -229,7 +239,7 @@ class JsonQuery(Query): return None return services - def _process_query(self, zone_manager, query, host, services): + def _process_filter(self, zone_manager, query, host, services): """Recursively parse the query structure.""" if len(query) == 0: return True @@ -238,7 +248,7 @@ class JsonQuery(Query): cooked_args = [] for arg in query[1:]: if isinstance(arg, list): - arg = self._process_query(zone_manager, arg, host, services) + arg = self._process_filter(zone_manager, arg, host, services) elif isinstance(arg, basestring): arg = self._parse_string(arg, host, services) if arg != None: @@ -247,11 +257,11 @@ class JsonQuery(Query): return result def filter_hosts(self, zone_manager, query): - """Return a list of hosts that can fulfill query.""" + """Return a list of hosts that can fulfill filter.""" expanded = json.loads(query) hosts = [] for host, services in zone_manager.service_states.iteritems(): - r = self._process_query(zone_manager, expanded, host, services) + r = self._process_filter(zone_manager, expanded, host, services) if isinstance(r, list): r = True in r if r: @@ -259,7 +269,7 @@ class JsonQuery(Query): return hosts -DRIVERS = [AllHostsQuery, FlavorQuery, JsonQuery] +DRIVERS = [AllHostsFilter, FlavorFilter, JsonFilter] def choose_driver(driver_name=None): @@ -267,10 +277,10 @@ def choose_driver(driver_name=None): to have an authoritative list of what is permissible. This function checks the driver name against a predefined set of acceptable drivers.""" - + if not driver_name: - driver_name = FLAGS.default_query_engine + driver_name = FLAGS.default_host_filter_driver for driver in DRIVERS: if "%s.%s" % (driver.__module__, driver.__name__) == driver_name: return driver() - raise exception.SchedulerQueryDriverNotFound(driver_name=driver_name) + raise exception.SchedulerHostFilterDriverNotFound(driver_name=driver_name) diff --git a/nova/tests/test_query.py b/nova/tests/test_host_filter.py similarity index 83% rename from nova/tests/test_query.py rename to nova/tests/test_host_filter.py index 9497a8c9..31e40ae1 100644 --- a/nova/tests/test_query.py +++ b/nova/tests/test_host_filter.py @@ -13,7 +13,7 @@ # License for the specific language governing permissions and limitations # under the License. """ -Tests For Scheduler Query Drivers +Tests For Scheduler Host Filter Drivers. """ import json @@ -21,7 +21,7 @@ import json from nova import exception from nova import flags from nova import test -from nova.scheduler import query +from nova.scheduler import host_filter FLAGS = flags.FLAGS @@ -30,8 +30,8 @@ class FakeZoneManager: pass -class QueryTestCase(test.TestCase): - """Test case for query drivers.""" +class HostFilterTestCase(test.TestCase): + """Test case for host filter drivers.""" def _host_caps(self, multiplier): # Returns host capabilities in the following way: @@ -57,8 +57,9 @@ class QueryTestCase(test.TestCase): 'host_name-label': 'xs-%s' % multiplier} def setUp(self): - self.old_flag = FLAGS.default_query_engine - FLAGS.default_query_engine = 'nova.scheduler.query.AllHostsQuery' + 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, @@ -75,37 +76,38 @@ class QueryTestCase(test.TestCase): self.zone_manager.service_states = states def tearDown(self): - FLAGS.default_query_engine = self.old_flag + FLAGS.default_host_filter_driver = self.old_flag def test_choose_driver(self): # Test default driver ... - driver = query.choose_driver() + driver = host_filter.choose_driver() self.assertEquals(driver._full_name(), - 'nova.scheduler.query.AllHostsQuery') + 'nova.scheduler.host_filter.AllHostsFilter') # Test valid driver ... - driver = query.choose_driver('nova.scheduler.query.FlavorQuery') + driver = host_filter.choose_driver( + 'nova.scheduler.host_filter.FlavorFilter') self.assertEquals(driver._full_name(), - 'nova.scheduler.query.FlavorQuery') + 'nova.scheduler.host_filter.FlavorFilter') # Test invalid driver ... try: - query.choose_driver('does not exist') + host_filter.choose_driver('does not exist') self.fail("Should not find driver") - except exception.SchedulerQueryDriverNotFound: + except exception.SchedulerHostFilterDriverNotFound: pass def test_all_host_driver(self): - driver = query.AllHostsQuery() - cooked = driver.instance_type_to_query(self.instance_type) + 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_flavor_driver(self): - driver = query.FlavorQuery() + driver = host_filter.FlavorFilter() # filter all hosts that can support 50 ram and 500 disk - name, cooked = driver.instance_type_to_query(self.instance_type) - self.assertEquals('nova.scheduler.query.FlavorQuery', name) + name, cooked = driver.instance_type_to_filter(self.instance_type) + self.assertEquals('nova.scheduler.host_filter.FlavorFilter', name) hosts = driver.filter_hosts(self.zone_manager, cooked) self.assertEquals(6, len(hosts)) just_hosts = [host for host, caps in hosts] @@ -114,10 +116,10 @@ class QueryTestCase(test.TestCase): self.assertEquals('host10', just_hosts[5]) def test_json_driver(self): - driver = query.JsonQuery() + driver = host_filter.JsonFilter() # filter all hosts that can support 50 ram and 500 disk - name, cooked = driver.instance_type_to_query(self.instance_type) - self.assertEquals('nova.scheduler.query.JsonQuery', name) + 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]