]> review.fuel-infra Code Review - openstack-build/cinder-build.git/commitdiff
Support Volume Num Weighter
authorling-yun <zengyunling@huawei.com>
Wed, 25 Jun 2014 02:40:38 +0000 (10:40 +0800)
committerling-yun <zengyunling@huawei.com>
Thu, 26 Jun 2014 08:13:18 +0000 (16:13 +0800)
Currently cinder support choosing volume backend according to
free_capacity and allocated_capacity. Volume Num Weighter is
that scheduler could choose volume backend based on volume number
in volume backend, which could provide another mean to help
improve volume-backends' IO balance and volumes' IO performance,
see details in ref [1] and [2].

blueprint support-volume-num-weighter

Ref:
[1]https://blueprints.launchpad.net/cinder/+spec/support-volume-num-weighter
[2]https://review.openstack.org/#/c/99683/

Change-Id: Id9275ed954f1b35ee8d7bee0f0b61cc3a7c48f63

cinder/db/api.py
cinder/db/sqlalchemy/api.py
cinder/scheduler/weights/volume_number.py [new file with mode: 0644]
cinder/tests/scheduler/test_volume_number_weigher.py [new file with mode: 0644]
etc/cinder/cinder.conf.sample
setup.cfg

index de2664b7edbd6eef50f579301c57e8b31c4a5f33..1d4ac6279b7ab10e78de241f19cbc2a61a2c811d 100644 (file)
@@ -215,10 +215,11 @@ def volume_create(context, values):
     return IMPL.volume_create(context, values)
 
 
-def volume_data_get_for_host(context, host):
+def volume_data_get_for_host(context, host, count_only=False):
     """Get (volume_count, gigabytes) for project."""
     return IMPL.volume_data_get_for_host(context,
-                                         host)
+                                         host,
+                                         count_only)
 
 
 def volume_data_get_for_project(context, project_id):
index fd6e74bc508a5e5691947b69b7d6d04b40a41c92..38a5aaaf7bba096a32f93c0312800e75b18eccfe 100644 (file)
@@ -1004,16 +1004,23 @@ def volume_create(context, values):
 
 
 @require_admin_context
-def volume_data_get_for_host(context, host):
-    result = model_query(context,
-                         func.count(models.Volume.id),
-                         func.sum(models.Volume.size),
-                         read_deleted="no").\
-        filter_by(host=host).\
-        first()
-
-    # NOTE(vish): convert None to 0
-    return (result[0] or 0, result[1] or 0)
+def volume_data_get_for_host(context, host, count_only=False):
+    if count_only:
+        result = model_query(context,
+                             func.count(models.Volume.id),
+                             read_deleted="no").\
+            filter_by(host=host).\
+            first()
+        return result[0] or 0
+    else:
+        result = model_query(context,
+                             func.count(models.Volume.id),
+                             func.sum(models.Volume.size),
+                             read_deleted="no").\
+            filter_by(host=host).\
+            first()
+        # NOTE(vish): convert None to 0
+        return (result[0] or 0, result[1] or 0)
 
 
 @require_admin_context
diff --git a/cinder/scheduler/weights/volume_number.py b/cinder/scheduler/weights/volume_number.py
new file mode 100644 (file)
index 0000000..8720362
--- /dev/null
@@ -0,0 +1,60 @@
+# Copyright (c) 2014 OpenStack Foundation
+# 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.
+"""
+Weighers that weigh hosts by volume number in backends:
+
+1. Volume Number Weigher.  Weigh hosts by their volume number.
+
+The default is to spread volumes across all hosts evenly.  If you prefer
+stacking, you can set the 'volume_number_multiplier' option to a positive
+number and the weighing has the opposite effect of the default.
+"""
+
+
+from oslo.config import cfg
+
+from cinder import db
+from cinder.openstack.common import log as logging
+from cinder.openstack.common.scheduler import weights
+
+
+LOG = logging.getLogger(__name__)
+
+
+volume_number_weight_opts = [
+    cfg.FloatOpt('volume_number_multiplier',
+                 default=-1.0,
+                 help='Multiplier used for weighing volume number. '
+                      'Negative numbers mean to spread vs stack.'),
+]
+
+CONF = cfg.CONF
+CONF.register_opts(volume_number_weight_opts)
+
+
+class VolumeNumberWeigher(weights.BaseHostWeigher):
+    def _weight_multiplier(self):
+        """Override the weight multiplier."""
+        return CONF.volume_number_multiplier
+
+    def _weigh_object(self, host_state, weight_properties):
+        """Less volume number weights win.
+        We want spreading to be the default.
+        """
+        context = weight_properties['context']
+        volume_number = db.volume_data_get_for_host(context=context,
+                                                    host=host_state.host,
+                                                    count_only=True)
+        return volume_number
diff --git a/cinder/tests/scheduler/test_volume_number_weigher.py b/cinder/tests/scheduler/test_volume_number_weigher.py
new file mode 100644 (file)
index 0000000..ca596cf
--- /dev/null
@@ -0,0 +1,98 @@
+# Copyright 2014 OpenStack Foundation
+# 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.
+"""
+Tests For Volume Number Weigher.
+"""
+
+import mock
+
+from oslo.config import cfg
+
+from cinder import context
+from cinder.db.sqlalchemy import api
+from cinder.openstack.common.scheduler.weights import HostWeightHandler
+from cinder.scheduler.weights.volume_number import VolumeNumberWeigher
+from cinder import test
+from cinder.tests.scheduler import fakes
+
+CONF = cfg.CONF
+
+
+def fake_volume_data_get_for_host(context, host, count_only=False):
+    if host == 'host1':
+        return 1
+    elif host == 'host2':
+        return 2
+    elif host == 'host3':
+        return 3
+    elif host == 'host4':
+        return 4
+    else:
+        return 1
+
+
+class VolumeNumberWeigherTestCase(test.TestCase):
+    def setUp(self):
+        super(VolumeNumberWeigherTestCase, self).setUp()
+        self.context = context.get_admin_context()
+        self.host_manager = fakes.FakeHostManager()
+        self.weight_handler = HostWeightHandler('cinder.scheduler.weights')
+
+    def _get_weighed_host(self, hosts, weight_properties=None):
+        if weight_properties is None:
+            weight_properties = {'context': self.context}
+        return self.weight_handler.get_weighed_objects([VolumeNumberWeigher],
+                                                       hosts,
+                                                       weight_properties)[0]
+
+    @mock.patch('cinder.db.sqlalchemy.api.service_get_all_by_topic')
+    def _get_all_hosts(self, _mock_service_get_all_by_topic, disabled=False):
+        ctxt = context.get_admin_context()
+        fakes.mock_host_manager_db_calls(_mock_service_get_all_by_topic,
+                                         disabled=disabled)
+        host_states = self.host_manager.get_all_host_states(ctxt)
+        _mock_service_get_all_by_topic.assert_called_once_with(
+            ctxt, CONF.volume_topic, disabled=disabled)
+        return host_states
+
+    def test_volume_number_weight_multiplier1(self):
+        self.flags(volume_number_multiplier=-1.0)
+        hostinfo_list = self._get_all_hosts()
+
+        # host1: 1 volume
+        # host2: 2 volumes
+        # host3: 3 volumes
+        # host4: 4 volumes
+        # so, host1 should win:
+        with mock.patch.object(api, 'volume_data_get_for_host',
+                               fake_volume_data_get_for_host):
+            weighed_host = self._get_weighed_host(hostinfo_list)
+            self.assertEqual(weighed_host.weight, -1.0)
+            self.assertEqual(weighed_host.obj.host, 'host1')
+
+    def test_volume_number_weight_multiplier2(self):
+        self.flags(volume_number_multiplier=1.0)
+        hostinfo_list = self._get_all_hosts()
+
+        # host1: 1 volume
+        # host2: 2 volumes
+        # host3: 3 volumes
+        # host4: 4 volumes
+        # so, host4 should win:
+        with mock.patch.object(api, 'volume_data_get_for_host',
+                               fake_volume_data_get_for_host):
+            weighed_host = self._get_weighed_host(hostinfo_list)
+            self.assertEqual(weighed_host.weight, 4.0)
+            self.assertEqual(weighed_host.obj.host, 'host4')
index f5a8cc0058315b9a22ab230a8304ac135ecca796..a14d45edc58ac74bd78669282b085a2de943c60d 100644 (file)
 #allocated_capacity_weight_multiplier=-1.0
 
 
+#
+# Options defined in cinder.scheduler.weights.volume_number
+#
+
+# Multiplier used for weighing volume number. Negative numbers
+# mean to spread vs stack. (floating point value)
+#volume_number_multiplier=-1.0
+
+
 #
 # Options defined in cinder.transfer.api
 #
index e967d0d377a23e79a6d8da4643faf3159d99e6ae..3d47126b1d8b9b3b330009bcb2813b89288a6f2d 100644 (file)
--- a/setup.cfg
+++ b/setup.cfg
@@ -48,6 +48,7 @@ cinder.scheduler.weights =
     AllocatedCapacityWeigher = cinder.scheduler.weights.capacity:AllocatedCapacityWeigher
     CapacityWeigher = cinder.scheduler.weights.capacity:CapacityWeigher
     ChanceWeigher = cinder.scheduler.weights.chance:ChanceWeigher
+    VolumeNumberWeigher = cinder.scheduler.weights.volume_number:VolumeNumberWeigher
 console_scripts =
     cinder-rootwrap = oslo.rootwrap.cmd:main
 # These are for backwards compat with Havana notification_driver configuration values