]> review.fuel-infra Code Review - openstack-build/cinder-build.git/commitdiff
Add user defined extra capabilities
authorOllie Leahy <oliver.leahy@hp.com>
Thu, 9 Jan 2014 15:14:11 +0000 (15:14 +0000)
committerOllie Leahy <oliver.leahy@hp.com>
Tue, 4 Mar 2014 17:47:46 +0000 (17:47 +0000)
Provide a mechanism that will allow sys admins who are managing cinder
installations with multiple backends to assign key/value pairs to
backends. The key/value pairs can be used by the capabilities scheduler
to select between backends when requests specify volume types.

For example a sysadmin could specify the key 'service_level' with the
values 'high', 'medium' and 'low' for different backends. The sys admin
specifies the 'service_level' for each backend in cinder.conf as a json
string, for example:

extra_capabilities='{"service_level"="high"}'

or

extra_capabilities='{"service_level"="medium"}'

DocImpact
Implements: blueprint admin-defined-capabilities
Change-Id: I1ee9dcb22b3f097c3f3b2a70b0cb672930407cc9

cinder/tests/test_volume.py
cinder/volume/manager.py
etc/cinder/cinder.conf.sample

index 9c576c0086c49735e89665f5802f7e6866586989..a9c2ac8887a136f5e20d7405e9ed6ef7f8a5a2a3 100644 (file)
@@ -20,13 +20,13 @@ Tests for Volume Code.
 
 import contextlib
 import datetime
+import mock
 import os
 import shutil
 import socket
 import tempfile
 
 import eventlet
-import mock
 import mox
 from oslo.config import cfg
 from stevedore import extension
@@ -42,6 +42,7 @@ from cinder.image import image_utils
 from cinder import keymgr
 from cinder.openstack.common import fileutils
 from cinder.openstack.common import importutils
+from cinder.openstack.common import jsonutils
 from cinder.openstack.common.notifier import api as notifier_api
 from cinder.openstack.common.notifier import test_notifier
 from cinder.openstack.common import rpc
@@ -59,6 +60,7 @@ import cinder.volume
 from cinder.volume import configuration as conf
 from cinder.volume import driver
 from cinder.volume.drivers import lvm
+from cinder.volume.manager import VolumeManager
 from cinder.volume import rpcapi as volume_rpcapi
 from cinder.volume import utils as volutils
 from cinder.volume import volume_types
@@ -467,6 +469,27 @@ class VolumeTestCase(BaseVolumeTestCase):
                           self.context,
                           volume['id'])
 
+    def test_extra_capabilities(self):
+        # Test valid extra_capabilities.
+        fake_capabilities = {'key1': 1, 'key2': 2}
+
+        with mock.patch.object(jsonutils, 'loads') as mock_loads:
+            mock_loads.return_value = fake_capabilities
+            manager = VolumeManager()
+            manager.driver.set_initialized()
+            manager.publish_service_capabilities(self.context)
+            self.assertTrue(mock_loads.called)
+            volume_stats = manager.last_capabilities
+            self.assertEqual(volume_stats['key1'],
+                             fake_capabilities['key1'])
+            self.assertEqual(volume_stats['key2'],
+                             fake_capabilities['key2'])
+
+    def test_extra_capabilities_fail(self):
+        with mock.patch.object(jsonutils, 'loads') as mock_loads:
+            mock_loads.side_effect = exception.CinderException('test')
+            self.assertRaises(exception.CinderException, VolumeManager)
+
     def test_delete_busy_volume(self):
         """Test volume survives deletion if driver reports it as busy."""
         volume = tests_utils.create_volume(self.context, **self.volume_params)
index 9a2aa73495408aa8bb99d3af45d992a18e9e868f..645333f1e670490f184c98c05a0320393d151069 100644 (file)
@@ -47,6 +47,7 @@ from cinder.image import glance
 from cinder import manager
 from cinder.openstack.common import excutils
 from cinder.openstack.common import importutils
+from cinder.openstack.common import jsonutils
 from cinder.openstack.common import log as logging
 from cinder.openstack.common import periodic_task
 from cinder.openstack.common import timeutils
@@ -81,6 +82,10 @@ volume_manager_opts = [
     cfg.StrOpt('zoning_mode',
                default='none',
                help='FC Zoning mode configured'),
+    cfg.StrOpt('extra_capabilities',
+               default='{}',
+               help='User defined capabilities, a JSON formatted string '
+                    'specifying key/value pairs.'),
 ]
 
 CONF = cfg.CONF
@@ -199,6 +204,15 @@ class VolumeManager(manager.SchedulerDependentManager):
             host=self.host)
 
         self.zonemanager = None
+        try:
+            self.extra_capabilities = jsonutils.loads(
+                self.driver.configuration.extra_capabilities)
+        except AttributeError:
+            self.extra_capabilities = {}
+        except Exception:
+            with excutils.save_and_reraise_exception():
+                LOG.error("Invalid JSON: %s" %
+                          self.driver.configuration.extra_capabilities)
 
     def _add_to_threadpool(self, func, *args, **kwargs):
         self._tp.spawn_n(func, *args, **kwargs)
@@ -1069,6 +1083,8 @@ class VolumeManager(manager.SchedulerDependentManager):
                          'config_group': config_group})
         else:
             volume_stats = self.driver.get_volume_stats(refresh=True)
+            if self.extra_capabilities:
+                volume_stats.update(self.extra_capabilities)
             if volume_stats:
                 # Append volume stats with 'allocated_capacity_gb'
                 volume_stats.update(self.stats)
index 73de852424a8d38ae64f15e9ac42800b6587aee3..39c7820f2728ec3fb27f2fe9c6db7a399fe973c7 100644 (file)
 # FC Zoning mode configured (string value)
 #zoning_mode=none
 
+# User defined capabilities, a JSON formatted string
+# specifying key/value pairs. (string value)
+#extra_capabilities={}
+
 
 [fc-zone-manager]