From 289f63934459baccae1e7c547890cc5f4e9f26b3 Mon Sep 17 00:00:00 2001
From: Zhiteng Huang <zhithuang@ebaysf.com>
Date: Tue, 31 Dec 2013 15:00:52 +0800
Subject: [PATCH] Replace Simple/Chance Scheduler with FilterScheduler

This patch replaces SimpleScheduler and ChanceScheduler with
FilterScheduler. Scheduler manager is changed so that when it
detects 'scheduler_driver' is set to SimpleScheduler or
ChanceScheduler, it changes underlying scheduler driver to
FilterScheduler, and then host manager will use a pre-defined
combination of filters & weighers to keep scheduler behavior *MOSTLY*
the same as its old counterparts.

To simulate ChanceScheduler/SimpleScheduler, 'AvailabilityZoneFilter',
'CapacityFilter' and 'CapabilitiesFilter' are chosen to filter
out hosts that isn't in the target AZ, doesn't have sufficient
capacity and doesn't support needed capabilities. And newly
added 'ChanceWeigher' will randomly pick a host from hosts
that passes above filters - just like ChanceScheduler does;
for SimpleScheduler, 'AllocatedCapacityWeigher' will sort hosts
with their 'allocated_capacity' - the one allocates the least
is the winner by default.

So the two new weigher, 'ChanceWeigher' and 'AllocatedCapacityWeigher'
are the key FilterScheduler to act identical when choosing
back-ends to serve requests. The 3 filters on the other hand,
are essential to make sure this 'new' Simple/Chance Scheduler can
support volume types, encryption and QoS.

Partially implements bp: deprecate-chance-and-simple-schedulers

Change-Id: I0538b9692a23af6457ed463447fea8e564570848
---
 cinder/scheduler/host_manager.py         | 18 ++++++++++++
 cinder/scheduler/manager.py              | 10 +++++++
 cinder/tests/conf_fixture.py             |  3 ++
 cinder/tests/scheduler/test_scheduler.py | 37 ++++++++++++++++++++++++
 4 files changed, 68 insertions(+)

diff --git a/cinder/scheduler/host_manager.py b/cinder/scheduler/host_manager.py
index cfcbfcfa0..b676f45a6 100644
--- a/cinder/scheduler/host_manager.py
+++ b/cinder/scheduler/host_manager.py
@@ -48,6 +48,7 @@ host_manager_opts = [
 
 CONF = cfg.CONF
 CONF.register_opts(host_manager_opts)
+CONF.import_opt('scheduler_driver', 'cinder.scheduler.manager')
 
 LOG = logging.getLogger(__name__)
 
@@ -171,6 +172,23 @@ class HostManager(object):
                                                         'weights')
         self.weight_classes = self.weight_handler.get_all_classes()
 
+        default_filters = ['AvailabilityZoneFilter',
+                           'CapacityFilter',
+                           'CapabilitiesFilter']
+        chance = 'cinder.scheduler.chance.ChanceScheduler'
+        simple = 'cinder.scheduler.simple.SimpleScheduler'
+        if CONF.scheduler_driver == simple:
+            CONF.set_override('scheduler_default_filters', default_filters)
+            CONF.set_override('scheduler_default_weighers',
+                              ['AllocatedCapacityWeigher'])
+        elif CONF.scheduler_driver == chance:
+            CONF.set_override('scheduler_default_filters', default_filters)
+            CONF.set_override('scheduler_default_weighers',
+                              ['ChanceWeigher'])
+        else:
+            # Do nothing when some other scheduler is configured
+            pass
+
     def _choose_host_filters(self, filter_cls_names):
         """Since the caller may specify which filters to use we need
         to have an authoritative list of what is permissible. This
diff --git a/cinder/scheduler/manager.py b/cinder/scheduler/manager.py
index 515ef8c13..a64eaebee 100644
--- a/cinder/scheduler/manager.py
+++ b/cinder/scheduler/manager.py
@@ -55,6 +55,16 @@ class SchedulerManager(manager.Manager):
                  *args, **kwargs):
         if not scheduler_driver:
             scheduler_driver = CONF.scheduler_driver
+        if scheduler_driver in ['cinder.scheduler.chance.ChanceScheduler',
+                                'cinder.scheduler.simple.SimpleScheduler']:
+            scheduler_driver = ('cinder.scheduler.filter_scheduler.'
+                                'FilterScheduler')
+            LOG.deprecated(_('ChanceScheduler and SimpleScheduler have been '
+                             'deprecated due to lack of support for advanced '
+                             'features like: volume types, volume encryption,'
+                             ' QoS etc. These two schedulers can be fully '
+                             'replaced by FilterScheduler with certain '
+                             'combination of filters and weighers.'))
         self.driver = importutils.import_object(scheduler_driver)
         super(SchedulerManager, self).__init__(*args, **kwargs)
 
diff --git a/cinder/tests/conf_fixture.py b/cinder/tests/conf_fixture.py
index 841ec3791..bc01704a6 100644
--- a/cinder/tests/conf_fixture.py
+++ b/cinder/tests/conf_fixture.py
@@ -28,6 +28,7 @@ CONF.import_opt('volume_driver', 'cinder.volume.manager')
 CONF.import_opt('xiv_ds8k_proxy', 'cinder.volume.drivers.xiv_ds8k')
 CONF.import_opt('backup_driver', 'cinder.backup.manager')
 CONF.import_opt('fixed_key', 'cinder.keymgr.conf_key_mgr', group='keymgr')
+CONF.import_opt('scheduler_driver', 'cinder.scheduler.manager')
 
 def_vol_type = 'fake_vol_type'
 
@@ -50,3 +51,5 @@ def set_defaults(conf):
         'cinder.tests.test_xiv_ds8k.XIVDS8KFakeProxyDriver')
     conf.set_default('backup_driver', 'cinder.tests.backup.fake_service')
     conf.set_default('fixed_key', default='0' * 64, group='keymgr')
+    conf.set_default('scheduler_driver',
+                     'cinder.scheduler.filter_scheduler.FilterScheduler')
diff --git a/cinder/tests/scheduler/test_scheduler.py b/cinder/tests/scheduler/test_scheduler.py
index 0b3ade1fb..4b2e28cd8 100644
--- a/cinder/tests/scheduler/test_scheduler.py
+++ b/cinder/tests/scheduler/test_scheduler.py
@@ -20,14 +20,19 @@ Tests For Scheduler
 """
 
 import mock
+from oslo.config import cfg
 
 from cinder import context
 from cinder import exception
 from cinder.scheduler import driver
+from cinder.scheduler import filter_scheduler
 from cinder.scheduler import manager
 from cinder import test
 
 
+CONF = cfg.CONF
+
+
 class SchedulerManagerTestCase(test.TestCase):
     """Test case for scheduler manager."""
 
@@ -119,6 +124,38 @@ class SchedulerManagerTestCase(test.TestCase):
         _mock_host_passes.assert_called_once_with(self.context, 'host',
                                                   request_spec, {})
 
+    def test_chance_simple_scheduler_mocked(self):
+        # Test FilterScheduler is loaded and predefined combination
+        # of filters and weighers overrides the default value of config option
+        # scheduler_default_filters and scheduler_default_weighers when
+        # ChanceScheduler or SimpleScheduler is configured as scheduler_driver.
+        chance = 'cinder.scheduler.chance.ChanceScheduler'
+        simple = 'cinder.scheduler.simple.SimpleScheduler'
+        default_filters = ['AvailabilityZoneFilter',
+                           'CapacityFilter',
+                           'CapabilitiesFilter']
+        self.flags(scheduler_driver=chance,
+                   scheduler_default_filters=['CapacityFilter'],
+                   scheduler_default_weighers=['CapacityWeigher'])
+        self.manager = self.manager_cls()
+        self.assertTrue(isinstance(self.manager.driver,
+                                   filter_scheduler.FilterScheduler))
+        self.assertEqual(CONF.scheduler_default_filters,
+                         default_filters)
+        self.assertEqual(CONF.scheduler_default_weighers,
+                         ['ChanceWeigher'])
+
+        self.flags(scheduler_driver=simple,
+                   scheduler_default_filters=['CapacityFilter'],
+                   scheduler_default_weighers=['CapacityWeigher'])
+        self.manager = self.manager_cls()
+        self.assertTrue(isinstance(self.manager.driver,
+                                   filter_scheduler.FilterScheduler))
+        self.assertEqual(CONF.scheduler_default_filters,
+                         default_filters)
+        self.assertEqual(CONF.scheduler_default_weighers,
+                         ['AllocatedCapacityWeigher'])
+
 
 class SchedulerTestCase(test.TestCase):
     """Test case for base scheduler driver class."""
-- 
2.45.2