diff --git a/glance_store/backend.py b/glance_store/backend.py index 50ac83eb..5a193b82 100644 --- a/glance_store/backend.py +++ b/glance_store/backend.py @@ -18,14 +18,17 @@ import sys from oslo.config import cfg from stevedore import driver +from stevedore import extension from glance_store import exceptions -from glance_store.i18n import _ +from glance_store import i18n from glance_store import location LOG = logging.getLogger(__name__) +_ = i18n._ + _DEPRECATED_STORE_OPTS = [ cfg.DeprecatedOpt('known_stores', group='DEFAULT'), cfg.DeprecatedOpt('default_store', group='DEFAULT') @@ -46,33 +49,38 @@ CONF = cfg.CONF _STORE_CFG_GROUP = 'glance_store' -def _oslo_config_options(): - return ((opt, _STORE_CFG_GROUP) for opt in _STORE_OPTS) +def _list_opts(): + driver_opts = [] + mgr = extension.ExtensionManager('glance_store.drivers') + # NOTE(zhiyan): Handle available drivers entry_points provided + drivers = [ext.name for ext in mgr] + handled_drivers = [] # Used to handle backwards-compatible entries + for store_entry in drivers: + driver_cls = _load_store(None, store_entry, False) + if driver_cls and driver_cls not in handled_drivers: + if getattr(driver_cls, 'OPTIONS', None) is not None: + # NOTE(flaper87): To be removed in k-2. This should + # give deployers enough time to migrate their systems + # and move configs under the new section. + for opt in driver_cls.OPTIONS: + opt.deprecated_opts = [cfg.DeprecatedOpt(opt.name, + group='DEFAULT')] + driver_opts.append(opt) + handled_drivers.append(driver_cls) + + # NOTE(zhiyan): This separated approach could list + # store options before all driver ones, which easier + # to read and configure by operator. + return ([(_STORE_CFG_GROUP, _STORE_OPTS)] + + [(_STORE_CFG_GROUP, driver_opts)]) def register_opts(conf): - for opt, group in _oslo_config_options(): - conf.register_opt(opt, group=group) - register_store_opts(conf) - - -def register_store_opts(conf): - for store_entry in set(conf.glance_store.stores): - LOG.debug("Registering options for %s" % store_entry) - store_cls = _load_store(conf, store_entry, False) - - if store_cls is None: - msg = _('Store %s not found') % store_entry - raise exceptions.GlanceStoreException(message=msg) - - if getattr(store_cls, 'OPTIONS', None) is not None: - # NOTE(flaper87): To be removed in k-2. This should - # give deployers enough time to migrate their systems - # and move configs under the new section. - for opt in store_cls.OPTIONS: - opt.deprecated_opts = [cfg.DeprecatedOpt(opt.name, - group='DEFAULT')] - conf.register_opt(opt, group=_STORE_CFG_GROUP) + opts = _list_opts() + for group, opt_list in opts: + LOG.debug("Registering options for group %s" % group) + for opt in opt_list: + conf.register_opt(opt, group=group) class Indexable(object): diff --git a/setup.cfg b/setup.cfg index d3185f9d..fa507353 100644 --- a/setup.cfg +++ b/setup.cfg @@ -48,6 +48,9 @@ glance_store.drivers = glance.store.gridfs.Store = glance_store._drivers.gridfs:Store glance.store.vmware_datastore.Store = glance_store._drivers.vmware_datastore:Store +oslo.config.opts = + glance.store = glance_store.backend:_list_opts + [build_sphinx] source-dir = doc/source build-dir = doc/build diff --git a/tests/unit/test_opts.py b/tests/unit/test_opts.py new file mode 100644 index 00000000..a13c0200 --- /dev/null +++ b/tests/unit/test_opts.py @@ -0,0 +1,112 @@ +# 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. + +import pkg_resources +from testtools import matchers + +from glance_store import backend +from glance_store.tests import base + + +class OptsTestCase(base.StoreBaseTest): + + def _check_opt_groups(self, opt_list, expected_opt_groups): + self.assertThat(opt_list, matchers.HasLength(len(expected_opt_groups))) + + groups = [g for (g, _l) in opt_list] + self.assertThat(groups, matchers.HasLength(len(expected_opt_groups))) + + for idx, group in enumerate(groups): + self.assertEqual(expected_opt_groups[idx], group) + + def _check_opt_names(self, opt_list, expected_opt_names): + opt_names = [o.name for (g, l) in opt_list for o in l] + self.assertThat(opt_names, matchers.HasLength(len(expected_opt_names))) + + for opt in opt_names: + self.assertIn(opt, expected_opt_names) + + def _test_entry_point(self, namespace, + expected_opt_groups, expected_opt_names): + opt_list = None + for ep in pkg_resources.iter_entry_points('oslo.config.opts'): + if ep.name == namespace: + list_fn = ep.load() + opt_list = list_fn() + break + + self.assertIsNotNone(opt_list) + + self._check_opt_groups(opt_list, expected_opt_groups) + self._check_opt_names(opt_list, expected_opt_names) + + def test_list_api_opts(self): + opt_list = backend._list_opts() + expected_opt_groups = ['glance_store', 'glance_store'] + expected_opt_names = [ + 'default_store', + 'stores', + 'cinder_api_insecure', + 'cinder_ca_certificates_file', + 'cinder_catalog_info', + 'cinder_endpoint_template', + 'cinder_http_retries', + 'filesystem_store_datadir', + 'filesystem_store_datadirs', + 'filesystem_store_file_perm', + 'filesystem_store_metadata_file', + 'mongodb_store_db', + 'mongodb_store_uri', + 'os_region_name', + 'rbd_store_ceph_conf', + 'rbd_store_chunk_size', + 'rbd_store_pool', + 'rbd_store_user', + 's3_store_access_key', + 's3_store_bucket', + 's3_store_bucket_url_format', + 's3_store_create_bucket_on_put', + 's3_store_host', + 's3_store_object_buffer_dir', + 's3_store_secret_key', + 'swift_enable_snet', + 'swift_store_admin_tenants', + 'swift_store_auth_insecure', + 'swift_store_auth_version', + 'swift_store_container', + 'swift_store_create_container_on_put', + 'swift_store_endpoint_type', + 'swift_store_large_object_chunk_size', + 'swift_store_large_object_size', + 'swift_store_multi_tenant', + 'swift_store_region', + 'swift_store_retry_get_count', + 'swift_store_service_type', + 'swift_store_ssl_compression', + 'vmware_api_insecure', + 'vmware_api_retry_count', + 'vmware_datacenter_path', + 'vmware_datastore_name', + 'vmware_server_host', + 'vmware_server_password', + 'vmware_server_username', + 'vmware_store_image_dir', + 'vmware_task_poll_interval' + ] + + self._check_opt_groups(opt_list, expected_opt_groups) + self._check_opt_names(opt_list, expected_opt_names) + self._test_entry_point('glance.store', + expected_opt_groups, expected_opt_names) diff --git a/tests/unit/test_vmware_store.py b/tests/unit/test_vmware_store.py index e23d59f7..b76c90fa 100644 --- a/tests/unit/test_vmware_store.py +++ b/tests/unit/test_vmware_store.py @@ -86,7 +86,7 @@ class TestStore(base.StoreBaseTest): vm_store.Store.CHUNKSIZE = 2 self.config(default_store='vmware', stores=['vmware']) - backend.register_store_opts(self.conf) + backend.register_opts(self.conf) self.config(group='glance_store', vmware_server_username='admin', vmware_server_password='admin',