From: Zhiteng Huang Date: Thu, 2 Jan 2014 07:44:30 +0000 (+0800) Subject: Drop Chance/SimpleScheduler Implementation X-Git-Url: https://review.fuel-infra.org/gitweb?a=commitdiff_plain;h=cea7fe21cecfcd28ede3ba411b8ab8b36c9a7c15;p=openstack-build%2Fcinder-build.git Drop Chance/SimpleScheduler Implementation This patch removes the implementation of ChanceScheduler and SimpleScheduler as previous changes have made sure they are internally replaced by FilterScheduler. The "max_gigabytes" config option is deprecated and will leave it like that for one more release before we can remove it. DocImpact: "ChanceScheduler and SimpleScheduler have been deprecated and their implementation have been removed from Cinder." Implement bp: deprecate-chance-and-simple-schedulers Change-Id: Ifb1cb25e3bb4cdf26fa3283336b83fce5c97141e --- diff --git a/cinder/scheduler/chance.py b/cinder/scheduler/chance.py deleted file mode 100644 index 06b101aad..000000000 --- a/cinder/scheduler/chance.py +++ /dev/null @@ -1,148 +0,0 @@ -# Copyright (c) 2010 OpenStack Foundation -# Copyright 2010 United States Government as represented by the -# Administrator of the National Aeronautics and Space Administration. -# 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. - -""" -Chance (Random) Scheduler implementation -""" - -import random - -from oslo.config import cfg - -from cinder import exception -from cinder.scheduler import driver - - -CONF = cfg.CONF - - -class ChanceScheduler(driver.Scheduler): - """Implements Scheduler as a random node selector.""" - - def _filter_hosts(self, request_spec, hosts, **kwargs): - """Filter a list of hosts based on request_spec.""" - - filter_properties = kwargs.get('filter_properties', {}) - if not filter_properties: - filter_properties = {} - ignore_hosts = filter_properties.get('ignore_hosts', []) - hosts = [host for host in hosts if host not in ignore_hosts] - return hosts - - def _get_weighted_candidates(self, context, topic, request_spec, **kwargs): - """Returns a list of the available hosts.""" - - elevated = context.elevated() - hosts = self.hosts_up(elevated, topic) - if not hosts: - msg = _("Is the appropriate service running?") - raise exception.NoValidHost(reason=msg) - - return self._filter_hosts(request_spec, hosts, **kwargs) - - def _choose_host_from_list(self, hosts): - return hosts[int(random.random() * len(hosts))] - - def _schedule(self, context, topic, request_spec, **kwargs): - """Picks a host that is up at random.""" - hosts = self._get_weighted_candidates(context, topic, - request_spec, **kwargs) - if not hosts: - msg = _("Could not find another host") - raise exception.NoValidHost(reason=msg) - return self._choose_host_from_list(hosts) - - def schedule_create_volume(self, context, request_spec, filter_properties): - """Picks a host that is up at random.""" - topic = CONF.volume_topic - host = self._schedule(context, topic, request_spec, - filter_properties=filter_properties) - volume_id = request_spec['volume_id'] - snapshot_id = request_spec['snapshot_id'] - image_id = request_spec['image_id'] - - updated_volume = driver.volume_update_db(context, volume_id, host) - self.volume_rpcapi.create_volume(context, updated_volume, host, - request_spec, filter_properties, - snapshot_id=snapshot_id, - image_id=image_id) - - def host_passes_filters(self, context, host, request_spec, - filter_properties): - """Check if the specified host passes the filters.""" - weighed_hosts = self._get_weighted_candidates( - context, - CONF.volume_topic, - request_spec, - filter_properties=filter_properties) - - for weighed_host in weighed_hosts: - if weighed_host == host: - elevated = context.elevated() - host_states = self.host_manager.get_all_host_states(elevated) - for host_state in host_states: - if host_state.host == host: - return host_state - - msg = (_('cannot place volume %(id)s on %(host)s') - % {'id': request_spec['volume_id'], 'host': host}) - raise exception.NoValidHost(reason=msg) - - def find_retype_host(self, context, request_spec, filter_properties, - migration_policy='never'): - """Find a host that can accept the volume with its new type.""" - current_host = request_spec['volume_properties']['host'] - - # The volume already exists on this host, and so we shouldn't check if - # it can accept the volume again. - filter_properties['vol_exists_on'] = current_host - - weighed_hosts = self._get_weighted_candidates( - context, - CONF.volume_topic, - request_spec, - filter_properties=filter_properties) - if not weighed_hosts: - msg = (_('No valid hosts for volume %(id)s with type %(type)s') - % {'id': request_spec['volume_id'], - 'type': request_spec['volume_type']}) - raise exception.NoValidHost(reason=msg) - - target_host = None - for weighed_host in weighed_hosts: - if weighed_host == current_host: - target_host = current_host - - if migration_policy == 'never' and target_host is None: - msg = (_('Current host not valid for volume %(id)s with type ' - '%(type)s, migration not allowed') - % {'id': request_spec['volume_id'], - 'type': request_spec['volume_type']}) - raise exception.NoValidHost(reason=msg) - - if not target_host: - target_host = self._choose_host_from_list(weighed_hosts) - - elevated = context.elevated() - host_states = self.host_manager.get_all_host_states(elevated) - for host_state in host_states: - if host_state.host == target_host: - return (host_state, migration_policy) - - # NOTE(avishay):We should never get here, but raise just in case - msg = (_('No host_state for selected host %s') % target_host) - raise exception.NoValidHost(reason=msg) diff --git a/cinder/scheduler/driver.py b/cinder/scheduler/driver.py index 9eaf746fe..b64f77646 100644 --- a/cinder/scheduler/driver.py +++ b/cinder/scheduler/driver.py @@ -24,7 +24,6 @@ from oslo.config import cfg from cinder import db from cinder.openstack.common import importutils from cinder.openstack.common import timeutils -from cinder import utils from cinder.volume import rpcapi as volume_rpcapi @@ -65,14 +64,6 @@ class Scheduler(object): host, capabilities) - def hosts_up(self, context, topic): - """Return the list of hosts that have a running service for topic.""" - - services = db.service_get_all_by_topic(context, topic) - return [service['host'] - for service in services - if utils.service_is_up(service)] - def host_passes_filters(self, context, volume_id, host, filter_properties): """Check if the specified host passes the filters.""" raise NotImplementedError(_("Must implement host_passes_filters")) diff --git a/cinder/scheduler/simple.py b/cinder/scheduler/simple.py index 7ed512123..2dd59015e 100644 --- a/cinder/scheduler/simple.py +++ b/cinder/scheduler/simple.py @@ -16,68 +16,51 @@ # under the License. """ -Simple Scheduler -""" - -from oslo.config import cfg +Chance and Simple Scheduler are DEPRECATED. -from cinder import db -from cinder import exception -from cinder.scheduler import chance -from cinder import utils +Chance and Simple scheduler implementation have been deprecated, as their +functionality can be implemented using the FilterScheduler, here's how: +If one would like to have scheduler randomly picks available back-end +(like ChanceScheduler did), use FilterScheduler with following combination +of filters and weighers. -simple_scheduler_opts = [ - cfg.IntOpt("max_gigabytes", - default=10000, - help="maximum number of volume gigabytes to allow per host"), ] - -CONF = cfg.CONF -CONF.register_opts(simple_scheduler_opts) + scheduler_driver = cinder.scheduler.filter_scheduler.FilterScheduler + scheduler_default_filters = ['AvailabilityZoneFilter', 'CapacityFilter', + 'CapabilitiesFilter'] + scheduler_default_weighers = 'ChanceWeigher' +If one prefers the scheduler to pick up the back-end has most available +space that scheudler can see (like SimpleScheduler did), use following +combination of filters and weighers with FilterScheduler. -class SimpleScheduler(chance.ChanceScheduler): - """Implements Naive Scheduler that tries to find least loaded host.""" + scheduler_driver = cinder.scheduler.filter_scheduler.FilterScheduler + scheduler_default_filters = ['AvailabilityZoneFilter', 'CapacityFilter', + 'CapabilitiesFilter'] + scheduler_default_weighers = 'AllocatedCapacityWeigher' + allocated_capacity_weight_multiplier = -1.0 - def _get_weighted_candidates(self, context, topic, request_spec, **kwargs): - """Picks a host that is up and has the fewest volumes.""" - elevated = context.elevated() +Setting/leaving configure option +'scheduler_driver=cinder.scheduler.chance.ChanceScheduler' or +'scheduler_driver=cinder.scheduler.simple.SimpleScheduler' in cinder.conf +works exactly the same as described above since scheduler manager has been +updated to do the trick internally/transparently for users. - volume_id = request_spec.get('volume_id') - snapshot_id = request_spec.get('snapshot_id') - image_id = request_spec.get('image_id') - volume_properties = request_spec.get('volume_properties') - volume_size = volume_properties.get('size') - availability_zone = volume_properties.get('availability_zone') - filter_properties = kwargs.get('filter_properties', {}) - - zone, host = None, None - if availability_zone: - zone, _x, host = availability_zone.partition(':') - if host and context.is_admin: - service = db.service_get_by_args(elevated, host, topic) - if not utils.service_is_up(service): - raise exception.WillNotSchedule(host=host) - return [host] +With that, FilterScheduler behaves mostly the same as Chance/SimpleScheduler, +with additional benefits of supporting volume types, volume encryption, QoS. +""" - candidates = [] - results = db.service_get_all_volume_sorted(elevated) - if zone: - results = [(s, gigs) for (s, gigs) in results - if s['availability_zone'] == zone] - for result in results: - (service, volume_gigabytes) = result - no_skip = service['host'] != filter_properties.get('vol_exists_on') - if no_skip and volume_gigabytes + volume_size > CONF.max_gigabytes: - continue - if utils.service_is_up(service) and not service['disabled']: - candidates.append(service['host']) +from oslo.config import cfg - if candidates: - return candidates - else: - msg = _("No service with adequate space or no service running") - raise exception.NoValidHost(reason=msg) +simple_scheduler_opts = [ + cfg.IntOpt("max_gigabytes", + default=10000, + help="This configure option has been deprecated along with " + "the SimpleScheduler. New scheduler is able to gather " + "capacity information for each host, thus setting the " + "maximum number of volume gigabytes for host is no " + "longer needed. It's safe to remove this configure " + "from cinder.conf."), ] - def _choose_host_from_list(self, hosts): - return hosts[0] +CONF = cfg.CONF +CONF.register_opts(simple_scheduler_opts) diff --git a/cinder/tests/scheduler/test_scheduler.py b/cinder/tests/scheduler/test_scheduler.py index 33491e74b..42c7180ee 100644 --- a/cinder/tests/scheduler/test_scheduler.py +++ b/cinder/tests/scheduler/test_scheduler.py @@ -212,23 +212,6 @@ class SchedulerTestCase(test.TestCase): _mock_update_cap.assert_called_once_with(service_name, host, capabilities) - @mock.patch('cinder.db.service_get_all_by_topic') - @mock.patch('cinder.utils.service_is_up') - def test_hosts_up(self, _mock_serv_is_up, _mock_serv_get_all_by_topic): - service1 = {'host': 'host1', 'disabled': False} - service2 = {'host': 'host2', 'disabled': False} - services = [service1, service2] - - def fake_serv_is_up(service): - return service['host'] is 'host2' - - _mock_serv_get_all_by_topic.return_value = services - _mock_serv_is_up.side_effect = fake_serv_is_up - result = self.driver.hosts_up(self.context, self.topic) - self.assertEqual(result, ['host2']) - _mock_serv_get_all_by_topic.assert_called_once_with(self.context, - self.topic) - class SchedulerDriverBaseTestCase(SchedulerTestCase): """Test cases for base scheduler driver class methods diff --git a/doc/source/devref/scheduler.rst b/doc/source/devref/scheduler.rst index 6e531fb1a..e2b7e5f16 100644 --- a/doc/source/devref/scheduler.rst +++ b/doc/source/devref/scheduler.rst @@ -38,10 +38,10 @@ The :mod:`cinder.scheduler.driver` Module :show-inheritance: -The :mod:`cinder.scheduler.simple` Driver +The :mod:`cinder.scheduler.filter_scheduler` Driver ----------------------------------------- -.. automodule:: cinder.scheduler.simple +.. automodule:: cinder.scheduler.filter_scheduler :noindex: :members: :undoc-members: diff --git a/etc/cinder/cinder.conf.sample b/etc/cinder/cinder.conf.sample index 608133511..6ed923a1d 100644 --- a/etc/cinder/cinder.conf.sample +++ b/etc/cinder/cinder.conf.sample @@ -983,8 +983,11 @@ # Options defined in cinder.scheduler.simple # -# maximum number of volume gigabytes to allow per host -# (integer value) +# This configure option has been deprecated along with the +# SimpleScheduler. New scheduler is able to gather capacity +# information for each host, thus setting the maximum number +# of volume gigabytes for host is no longer needed. It's safe +# to remove this configure from cinder.conf. (integer value) #max_gigabytes=10000