From: Ollie Leahy Date: Thu, 9 Jan 2014 15:14:11 +0000 (+0000) Subject: Add user defined extra capabilities X-Git-Url: https://review.fuel-infra.org/gitweb?a=commitdiff_plain;h=9bcd7285b575cdcf0fe521bf01609bd21c48fd1c;p=openstack-build%2Fcinder-build.git Add user defined extra capabilities 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 --- diff --git a/cinder/tests/test_volume.py b/cinder/tests/test_volume.py index 9c576c008..a9c2ac888 100644 --- a/cinder/tests/test_volume.py +++ b/cinder/tests/test_volume.py @@ -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) diff --git a/cinder/volume/manager.py b/cinder/volume/manager.py index 9a2aa7349..645333f1e 100644 --- a/cinder/volume/manager.py +++ b/cinder/volume/manager.py @@ -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) diff --git a/etc/cinder/cinder.conf.sample b/etc/cinder/cinder.conf.sample index 73de85242..39c7820f2 100644 --- a/etc/cinder/cinder.conf.sample +++ b/etc/cinder/cinder.conf.sample @@ -1790,6 +1790,10 @@ # 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]