Use stevedore for scheduler driver

Avoid having to configure the full class path of scheduler driver,
change to load by stevedore driver plugin using entrypoints.

Change 'scheduler_driver' to use entrypoint with the namespace
'nova.scheduler.driver' in 'setup.cfg'. Meanwhile, still maintain the
compatibility for class path configuration until the next major release.

Change all related tests with flag 'scheduler_driver' to use stevedore
entrypoint.

UpgradeImpact - see the reno file attached.
Implements: blueprint scheduler-driver-use-stevedore
Change-Id: I8c169e12d9bfacdbdb1dadf68b8a1fa98c5ea5bc
This commit is contained in:
Yingxin 2015-12-08 13:47:32 +00:00
parent 158c6d64c2
commit 33d906a2e0
7 changed files with 120 additions and 16 deletions

View File

@ -270,20 +270,26 @@ and will be dropped in the N release.
""") """)
driver_opt = cfg.StrOpt("scheduler_driver", driver_opt = cfg.StrOpt("scheduler_driver",
default="nova.scheduler.filter_scheduler.FilterScheduler", default="filter_scheduler",
help=""" help="""
The class of the driver used by the scheduler. This should be the full Python The class of the driver used by the scheduler. This should be chosen from one
path to the class to be used. If nothing is specified in this option, the of the entrypoints under the namespace 'nova.scheduler.driver' of file
FilterScheduler is used. 'setup.cfg'. If nothing is specified in this option, the 'filter_scheduler' is
used.
This option also supports deprecated full Python path to the class to be used.
For example, "nova.scheduler.filter_scheduler.FilterScheduler". But note: this
support will be dropped in the N Release.
Other options are: Other options are:
* 'nova.scheduler.caching_scheduler.CachingScheduler' which aggressively * 'caching_scheduler' which aggressively caches the system state for better
caches the system state for better individual scheduler performance at individual scheduler performance at the risk of more retries when running
the risk of more retries when running multiple schedulers. multiple schedulers.
* 'nova.scheduler.chance.ChanceScheduler' which simply picks a host at * 'chance_scheduler' which simply picks a host at random.
random.
* 'fake_scheduler' which is used for testing.
* Services that use this: * Services that use this:

View File

@ -19,18 +19,23 @@
Scheduler Service Scheduler Service
""" """
from oslo_log import log as logging
import oslo_messaging as messaging import oslo_messaging as messaging
from oslo_serialization import jsonutils from oslo_serialization import jsonutils
from oslo_service import periodic_task from oslo_service import periodic_task
from oslo_utils import importutils from oslo_utils import importutils
from stevedore import driver
import nova.conf import nova.conf
from nova import exception from nova import exception
from nova.i18n import _, _LW
from nova import manager from nova import manager
from nova import objects from nova import objects
from nova import quota from nova import quota
LOG = logging.getLogger(__name__)
CONF = nova.conf.CONF CONF = nova.conf.CONF
QUOTAS = quota.QUOTAS QUOTAS = quota.QUOTAS
@ -46,7 +51,28 @@ class SchedulerManager(manager.Manager):
def __init__(self, scheduler_driver=None, *args, **kwargs): def __init__(self, scheduler_driver=None, *args, **kwargs):
if not scheduler_driver: if not scheduler_driver:
scheduler_driver = CONF.scheduler_driver scheduler_driver = CONF.scheduler_driver
self.driver = importutils.import_object(scheduler_driver) try:
self.driver = driver.DriverManager(
"nova.scheduler.driver",
scheduler_driver,
invoke_on_load=True).driver
# TODO(Yingxin): Change to catch stevedore.exceptions.NoMatches after
# stevedore v1.9.0
except RuntimeError:
# NOTE(Yingxin): Loading full class path is deprecated and should
# be removed in the N release.
try:
self.driver = importutils.import_object(scheduler_driver)
LOG.warning(_LW("DEPRECATED: scheduler_driver uses "
"classloader to load %(path)s. This legacy "
"loading style will be removed in the "
"N release."),
{'path': scheduler_driver})
except (ImportError, ValueError):
raise RuntimeError(
_("Cannot load scheduler driver from configuration "
"%(conf)s."),
{'conf': scheduler_driver})
super(SchedulerManager, self).__init__(service_name='scheduler', super(SchedulerManager, self).__init__(service_name='scheduler',
*args, **kwargs) *args, **kwargs)

View File

@ -92,8 +92,7 @@ class _IntegratedTestBase(test.TestCase):
return self.start_service('compute') return self.start_service('compute')
def _setup_scheduler_service(self): def _setup_scheduler_service(self):
self.flags(scheduler_driver='nova.scheduler.' self.flags(scheduler_driver='chance_scheduler')
'chance.ChanceScheduler')
return self.start_service('scheduler') return self.start_service('scheduler')
def _setup_services(self): def _setup_services(self):

View File

@ -79,8 +79,7 @@ class NUMAServersTest(ServersTestBase):
def _setup_scheduler_service(self): def _setup_scheduler_service(self):
self.flags(compute_driver='nova.virt.libvirt.LibvirtDriver') self.flags(compute_driver='nova.virt.libvirt.LibvirtDriver')
self.flags(scheduler_driver='nova.scheduler.' self.flags(scheduler_driver='filter_scheduler')
'filter_scheduler.FilterScheduler')
self.flags(scheduler_default_filters=CONF.scheduler_default_filters self.flags(scheduler_default_filters=CONF.scheduler_default_filters
+ ['NUMATopologyFilter']) + ['NUMATopologyFilter'])
return self.start_service('scheduler') return self.start_service('scheduler')

View File

@ -21,7 +21,10 @@ import mock
from nova import context from nova import context
from nova import objects from nova import objects
from nova.scheduler import caching_scheduler
from nova.scheduler import chance
from nova.scheduler import driver from nova.scheduler import driver
from nova.scheduler import filter_scheduler
from nova.scheduler import host_manager from nova.scheduler import host_manager
from nova.scheduler import ironic_host_manager from nova.scheduler import ironic_host_manager
from nova.scheduler import manager from nova.scheduler import manager
@ -31,18 +34,74 @@ from nova.tests.unit import fake_server_actions
from nova.tests.unit.scheduler import fakes from nova.tests.unit.scheduler import fakes
class SchedulerManagerInitTestCase(test.NoDBTestCase):
"""Test case for scheduler manager initiation."""
manager_cls = manager.SchedulerManager
@mock.patch.object(host_manager.HostManager, '_init_instance_info')
@mock.patch.object(host_manager.HostManager, '_init_aggregates')
def test_init_using_default_schedulerdriver(self,
mock_init_agg,
mock_init_inst):
driver = self.manager_cls().driver
self.assertIsInstance(driver, filter_scheduler.FilterScheduler)
@mock.patch.object(host_manager.HostManager, '_init_instance_info')
@mock.patch.object(host_manager.HostManager, '_init_aggregates')
def test_init_using_chance_schedulerdriver(self,
mock_init_agg,
mock_init_inst):
self.flags(scheduler_driver='chance_scheduler')
driver = self.manager_cls().driver
self.assertIsInstance(driver, chance.ChanceScheduler)
@mock.patch.object(host_manager.HostManager, '_init_instance_info')
@mock.patch.object(host_manager.HostManager, '_init_aggregates')
def test_init_using_caching_schedulerdriver(self,
mock_init_agg,
mock_init_inst):
self.flags(scheduler_driver='caching_scheduler')
driver = self.manager_cls().driver
self.assertIsInstance(driver, caching_scheduler.CachingScheduler)
@mock.patch.object(host_manager.HostManager, '_init_instance_info')
@mock.patch.object(host_manager.HostManager, '_init_aggregates')
def test_init_nonexist_schedulerdriver(self,
mock_init_agg,
mock_init_inst):
self.flags(scheduler_driver='nonexist_scheduler')
self.assertRaises(RuntimeError, self.manager_cls)
# NOTE(Yingxin): Loading full class path is deprecated and should be
# removed in the N release.
@mock.patch.object(manager.LOG, 'warning')
@mock.patch.object(host_manager.HostManager, '_init_instance_info')
@mock.patch.object(host_manager.HostManager, '_init_aggregates')
def test_init_using_classpath_to_schedulerdriver(self,
mock_init_agg,
mock_init_inst,
mock_warning):
self.flags(
scheduler_driver=
'nova.scheduler.chance.ChanceScheduler')
driver = self.manager_cls().driver
self.assertIsInstance(driver, chance.ChanceScheduler)
warn_args, kwargs = mock_warning.call_args
self.assertIn("DEPRECATED", warn_args[0])
class SchedulerManagerTestCase(test.NoDBTestCase): class SchedulerManagerTestCase(test.NoDBTestCase):
"""Test case for scheduler manager.""" """Test case for scheduler manager."""
manager_cls = manager.SchedulerManager manager_cls = manager.SchedulerManager
driver_cls = fakes.FakeScheduler driver_cls = fakes.FakeScheduler
driver_cls_name = 'nova.tests.unit.scheduler.fakes.FakeScheduler' driver_plugin_name = 'fake_scheduler'
@mock.patch.object(host_manager.HostManager, '_init_instance_info') @mock.patch.object(host_manager.HostManager, '_init_instance_info')
@mock.patch.object(host_manager.HostManager, '_init_aggregates') @mock.patch.object(host_manager.HostManager, '_init_aggregates')
def setUp(self, mock_init_agg, mock_init_inst): def setUp(self, mock_init_agg, mock_init_inst):
super(SchedulerManagerTestCase, self).setUp() super(SchedulerManagerTestCase, self).setUp()
self.flags(scheduler_driver=self.driver_cls_name) self.flags(scheduler_driver=self.driver_plugin_name)
with mock.patch.object(host_manager.HostManager, '_init_aggregates'): with mock.patch.object(host_manager.HostManager, '_init_aggregates'):
self.manager = self.manager_cls() self.manager = self.manager_cls()
self.context = context.RequestContext('fake_user', 'fake_project') self.context = context.RequestContext('fake_user', 'fake_project')

View File

@ -0,0 +1,9 @@
---
upgrade:
- |
The option ``scheduler_driver`` is now changed to use entrypoint instead of
full class path. Set one of the entrypoints under the namespace
'nova.scheduler.driver' in 'setup.cfg'. Its default value is
'host_manager'. The full class path style is still supported in current
release. But it is not recommended because class path can be changed and
this support will be dropped in the next major release.

View File

@ -191,6 +191,12 @@ nova.scheduler.host_manager =
host_manager = nova.scheduler.host_manager:HostManager host_manager = nova.scheduler.host_manager:HostManager
ironic_host_manager = nova.scheduler.ironic_host_manager:IronicHostManager ironic_host_manager = nova.scheduler.ironic_host_manager:IronicHostManager
nova.scheduler.driver =
filter_scheduler = nova.scheduler.filter_scheduler:FilterScheduler
caching_scheduler = nova.scheduler.caching_scheduler:CachingScheduler
chance_scheduler = nova.scheduler.chance:ChanceScheduler
fake_scheduler = nova.tests.unit.scheduler.fakes:FakeScheduler
[build_sphinx] [build_sphinx]
all_files = 1 all_files = 1
build-dir = doc/build build-dir = doc/build