diff --git a/manila/opts.py b/manila/opts.py index f19541484f..43229efb8b 100644 --- a/manila/opts.py +++ b/manila/opts.py @@ -101,6 +101,7 @@ _global_opt_lists = [ manila.scheduler.simple.simple_scheduler_opts, manila.scheduler.weights.capacity.capacity_weight_opts, manila.scheduler.weights.capacity.capacity_weight_opts, + manila.scheduler.weights.pool.pool_weight_opts, manila.service.service_opts, manila.share.api.share_api_opts, manila.share.driver.ganesha_opts, diff --git a/manila/scheduler/host_manager.py b/manila/scheduler/host_manager.py index 4833dfe70d..a5c84c8f1e 100644 --- a/manila/scheduler/host_manager.py +++ b/manila/scheduler/host_manager.py @@ -403,6 +403,10 @@ class HostManager(object): weigher_class_names=None): """Weigh the hosts.""" weigher_classes = self._choose_host_weighers(weigher_class_names) + weight_properties['server_pools_mapping'] = {} + for backend, info in self.service_states.items(): + weight_properties['server_pools_mapping'].update( + info.get('server_pools_mapping', {})) return self.weight_handler.get_weighed_objects(weigher_classes, hosts, weight_properties) diff --git a/manila/scheduler/weights/pool.py b/manila/scheduler/weights/pool.py new file mode 100644 index 0000000000..ad5f27ec63 --- /dev/null +++ b/manila/scheduler/weights/pool.py @@ -0,0 +1,54 @@ +# Copyright 2015 Mirantis Inc. +# 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. + +from oslo_config import cfg + +from manila import context +from manila.db import api as db_api +from manila.openstack.common.scheduler import weights +from manila.share import utils + +pool_weight_opts = [ + cfg.FloatOpt('pool_weight_multiplier', + default=1.0, + help='Multiplier used for weighing pools which have ' + 'existing share servers. Negative numbers mean to spread' + ' vs stack.'), +] + +CONF = cfg.CONF +CONF.register_opts(pool_weight_opts) + + +class PoolWeigher(weights.BaseHostWeigher): + def weight_multiplier(self): + """Override the weight multiplier.""" + return CONF.pool_weight_multiplier + + def _weigh_object(self, host_state, weight_properties): + """Pools with existing share server win.""" + pool_mapping = weight_properties.get('server_pools_mapping', {}) + if not pool_mapping: + return 0 + + ctx = context.get_admin_context() + host = utils.extract_host(host_state.host, 'backend') + servers = db_api.share_server_get_all_by_host(ctx, host) + pool = utils.extract_host(host_state.host, 'pool') + for server in servers: + if any(pool == p['pool_name'] for p in pool_mapping.get( + server['id'], [])): + return 1 + return 0 diff --git a/manila/tests/scheduler/weights/__init__.py b/manila/tests/scheduler/weights/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/manila/tests/scheduler/weights/test_pool.py b/manila/tests/scheduler/weights/test_pool.py new file mode 100644 index 0000000000..d1c8346247 --- /dev/null +++ b/manila/tests/scheduler/weights/test_pool.py @@ -0,0 +1,177 @@ +# Copyright 2015 Mirantis Inc. +# 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. + +import mock +from oslo_config import cfg +from oslo_utils import timeutils + +from manila import context +from manila.db import api as db_api +from manila.openstack.common.scheduler import weights +from manila.scheduler.weights import pool +from manila.share import utils +from manila import test +from manila.tests.scheduler import fakes + +CONF = cfg.CONF + + +class PoolWeigherTestCase(test.TestCase): + def setUp(self): + super(PoolWeigherTestCase, self).setUp() + self.host_manager = fakes.FakeHostManager() + self.weight_handler = weights.HostWeightHandler( + 'manila.scheduler.weights') + share_servers = [ + {'id': 'fake_server_id0'}, + {'id': 'fake_server_id1'}, + {'id': 'fake_server_id2'}, + {'id': 'fake_server_id3'}, + {'id': 'fake_server_id4'}, + ] + self.host_manager.service_states = ( + fakes.SHARE_SERVICE_STATES_WITH_POOLS) + self.mock_object(db_api, 'share_server_get_all_by_host', + mock.Mock(return_value=share_servers)) + + def _get_weighed_host(self, hosts, weight_properties=None): + if weight_properties is None: + weight_properties = { + 'server_pools_mapping': { + 'fake_server_id2': [{'pool_name': 'pool2'}, ], + }, + } + return self.weight_handler.get_weighed_objects( + [pool.PoolWeigher], + hosts, + weight_properties)[0] + + @mock.patch('manila.db.sqlalchemy.api.service_get_all_by_topic') + def _get_all_hosts(self, _mock_service_get_all_by_topic): + ctxt = context.get_admin_context() + _mock_service_get_all_by_topic.return_value = [ + dict(id=1, host='host1@AAA', topic='share', disabled=False, + availability_zone='zone1', updated_at=timeutils.utcnow()), + dict(id=2, host='host2@BBB', topic='share', disabled=False, + availability_zone='zone1', updated_at=timeutils.utcnow()), + dict(id=3, host='host3@CCC', topic='share', disabled=False, + availability_zone='zone2', updated_at=timeutils.utcnow()), + dict(id=4, host='host@DDD', topic='share', disabled=False, + availability_zone='zone3', updated_at=timeutils.utcnow()), + dict(id=5, host='host5@EEE', topic='share', disabled=False, + availability_zone='zone3', updated_at=timeutils.utcnow()), + ] + + host_states = self.host_manager.get_all_host_states_share(ctxt) + _mock_service_get_all_by_topic.assert_called_once_with( + ctxt, CONF.share_topic) + return host_states + + def test_no_server_pool_mapping(self): + weight_properties = { + 'server_pools_mapping': {}, + } + weighed_host = self._get_weighed_host(self._get_all_hosts(), + weight_properties) + self.assertEqual(0.0, weighed_host.weight) + + def test_choose_pool_with_existing_share_server(self): + # host1: weight = 0*(1.0) + # host2: weight = 1*(1.0) + # host3: weight = 0*(1.0) + # host4: weight = 0*(1.0) + # host5: weight = 0*(1.0) + + # so, host2 should win: + + weighed_host = self._get_weighed_host(self._get_all_hosts()) + self.assertEqual(1.0, weighed_host.weight) + self.assertEqual( + 'host2@BBB', utils.extract_host(weighed_host.obj.host)) + + def test_pool_weight_multiplier_positive(self): + self.flags(pool_weight_multiplier=2.0) + + # host1: weight = 0*(2.0) + # host2: weight = 1*(2.0) + # host3: weight = 0*(2.0) + # host4: weight = 0*(2.0) + # host5: weight = 0*(2.0) + + # so, host2 should win: + + weighed_host = self._get_weighed_host(self._get_all_hosts()) + self.assertEqual(2.0, weighed_host.weight) + self.assertEqual( + 'host2@BBB', utils.extract_host(weighed_host.obj.host)) + + def test_pool_weight_multiplier_negative(self): + self.flags(pool_weight_multiplier=-1.0) + weight_properties = { + 'server_pools_mapping': { + 'fake_server_id0': [{'pool_name': 'pool1'}], + 'fake_server_id2': [{'pool_name': 'pool3'}], + 'fake_server_id3': [ + {'pool_name': 'pool4a'}, + {'pool_name': 'pool4b'}, + ], + 'fake_server_id4': [ + {'pool_name': 'pool5a'}, + {'pool_name': 'pool5b'}, + ], + }, + } + + # host1: weight = 1*(-1.0) + # host2: weight = 0*(-1.0) + # host3: weight = 1*(-1.0) + # host4: weight = 1*(-1.0) + # host5: weight = 1*(-1.0) + + # so, host2 should win: + weighed_host = self._get_weighed_host(self._get_all_hosts(), + weight_properties) + self.assertEqual(0.0, weighed_host.weight) + self.assertEqual( + 'host2@BBB', utils.extract_host(weighed_host.obj.host)) + + def test_pool_weigher_all_pools_with_share_servers(self): + weight_properties = { + 'server_pools_mapping': { + 'fake_server_id0': [{'pool_name': 'pool1'}], + 'fake_server_id1': [{'pool_name': 'pool2'}], + 'fake_server_id2': [{'pool_name': 'pool3'}], + 'fake_server_id3': [ + {'pool_name': 'pool4a'}, + {'pool_name': 'pool4b'}, + ], + 'fake_server_id4': [ + {'pool_name': 'pool5a'}, + {'pool_name': 'pool5b'}, + ], + }, + } + + # host1: weight = 1*(1.0) + # host2: weight = 1*(1.0) + # host3: weight = 1*(1.0) + # host4: weight = 1*(1.0) + # host5: weight = 1*(1.0) + + # But after normalization all weights will be 0 + + weighed_host = self._get_weighed_host(self._get_all_hosts(), + weight_properties) + self.assertEqual(0.0, weighed_host.weight) diff --git a/setup.cfg b/setup.cfg index 7dd687298b..728bfb4971 100644 --- a/setup.cfg +++ b/setup.cfg @@ -40,6 +40,7 @@ manila.scheduler.filters = RetryFilter = manila.scheduler.filters.retry_filter:RetryFilter manila.scheduler.weights = CapacityWeigher = manila.scheduler.weights.capacity:CapacityWeigher + PoolWeigher = manila.scheduler.weights.pool:PoolWeigher # These are for backwards compat with Havana notification_driver configuration values oslo_messaging.notify.drivers = manila.openstack.common.notifier.log_notifier = oslo_messaging.notify._impl_log:LogDriver