From fa1c1dda1c0f50c30a57ac05a5893a45b2d77c98 Mon Sep 17 00:00:00 2001 From: Andrey Pavlov Date: Mon, 22 Aug 2016 15:37:38 +0300 Subject: [PATCH] Add support for storage of images in Cinder Glance charm allows to store images in file, ceph, swift. This changeset adds support for storage of images in Cinder for OpenStack Mitaka or later. Required dependencies are installed on relation to Cinder (inline with Ceph integration). This feature is dependent on resolution of some packaging issues in the glance-store package (see Related-Bug). Related-Bug: 1609733 Change-Id: Ib9d9f28e040b7b2eebb3f5d0ee9ff0773292bdcc --- hooks/cinder-volume-service-relation-broken | 1 + hooks/cinder-volume-service-relation-joined | 1 + hooks/glance_contexts.py | 22 +++++++++++++++++++++ hooks/glance_relations.py | 15 +++++++++++++- hooks/glance_utils.py | 17 ++++++++-------- metadata.yaml | 2 ++ templates/mitaka/glance-api.conf | 2 ++ unit_tests/test_glance_contexts.py | 14 ++++++++++++- unit_tests/test_glance_relations.py | 21 +++++++++++++++++--- 9 files changed, 82 insertions(+), 13 deletions(-) create mode 120000 hooks/cinder-volume-service-relation-broken create mode 120000 hooks/cinder-volume-service-relation-joined diff --git a/hooks/cinder-volume-service-relation-broken b/hooks/cinder-volume-service-relation-broken new file mode 120000 index 00000000..e955ca43 --- /dev/null +++ b/hooks/cinder-volume-service-relation-broken @@ -0,0 +1 @@ +glance_relations.py \ No newline at end of file diff --git a/hooks/cinder-volume-service-relation-joined b/hooks/cinder-volume-service-relation-joined new file mode 120000 index 00000000..e955ca43 --- /dev/null +++ b/hooks/cinder-volume-service-relation-joined @@ -0,0 +1 @@ +glance_relations.py \ No newline at end of file diff --git a/hooks/glance_contexts.py b/hooks/glance_contexts.py index 6181616a..45a614a8 100644 --- a/hooks/glance_contexts.py +++ b/hooks/glance_contexts.py @@ -30,6 +30,10 @@ from charmhelpers.contrib.hahelpers.cluster import ( determine_api_port, ) +from charmhelpers.contrib.openstack.utils import ( + os_release +) + class CephGlanceContext(OSContextGenerator): interfaces = ['ceph-glance'] @@ -65,6 +69,21 @@ class ObjectStoreContext(OSContextGenerator): } +class CinderStoreContext(OSContextGenerator): + interfaces = ['cinder-volume-service'] + + def __call__(self): + """Cinder store config. + Used to generate template context to be added to glance-api.conf in + the presence of a 'cinder-volume-service' relation. + """ + if not relation_ids('cinder-volume-service'): + return {} + return { + 'cinder_store': True, + } + + class MultiStoreContext(OSContextGenerator): def __call__(self): @@ -76,6 +95,9 @@ class MultiStoreContext(OSContextGenerator): for store_relation, store_type in store_mapping.iteritems(): if relation_ids(store_relation): stores.append(store_type) + if (relation_ids('cinder-volume-service') and + os_release('glance-common') >= 'mitaka'): + stores.append('glance.store.cinder.Store') return { 'known_stores': ','.join(stores) } diff --git a/hooks/glance_relations.py b/hooks/glance_relations.py index 5b17a2c8..74f86444 100755 --- a/hooks/glance_relations.py +++ b/hooks/glance_relations.py @@ -88,6 +88,7 @@ from charmhelpers.contrib.openstack.utils import ( sync_db_with_multi_ipv6_addresses, pausable_restart_on_change as restart_on_change, is_unit_paused_set, + os_requires_version, ) from charmhelpers.contrib.storage.linux.ceph import ( send_request_if_needed, @@ -526,7 +527,8 @@ def ha_relation_changed(): @hooks.hook('identity-service-relation-broken', 'object-store-relation-broken', 'shared-db-relation-broken', - 'pgsql-db-relation-broken') + 'pgsql-db-relation-broken', + 'cinder-volume-service-relation-broken') def relation_broken(): CONFIGS.write_all() @@ -590,6 +592,17 @@ def update_status(): juju_log('Updating status.') +@hooks.hook('cinder-volume-service-relation-joined') +@os_requires_version('mitaka', 'glance-common') +@restart_on_change(restart_map(), stopstart=True) +def cinder_volume_service_relation_joined(relid=None): + optional_packages = ["python-cinderclient", + "python-os-brick", + "python-oslo.rootwrap"] + apt_install(filter_installed_packages(optional_packages), fatal=True) + CONFIGS.write_all() + + if __name__ == '__main__': try: hooks.execute(sys.argv) diff --git a/hooks/glance_utils.py b/hooks/glance_utils.py index 559e3c78..a8e091ea 100644 --- a/hooks/glance_utils.py +++ b/hooks/glance_utils.py @@ -142,8 +142,6 @@ HTTPS_APACHE_CONF = "/etc/apache2/sites-available/openstack_https_frontend" HTTPS_APACHE_24_CONF = "/etc/apache2/sites-available/" \ "openstack_https_frontend.conf" -CONF_DIR = "/etc/glance" - TEMPLATES = 'templates/' # The interface is said to be satisfied if anyone of the interfaces in the @@ -182,6 +180,7 @@ CONFIG_FILES = OrderedDict([ service_user='glance'), glance_contexts.CephGlanceContext(), glance_contexts.ObjectStoreContext(), + glance_contexts.CinderStoreContext(), glance_contexts.HAProxyContext(), context.SyslogContext(), glance_contexts.LoggingConfigContext(), @@ -396,7 +395,7 @@ def git_post_install(projects_yaml): src_etc = os.path.join(git_src_dir(projects_yaml, 'glance'), 'etc') configs = { 'src': src_etc, - 'dest': '/etc/glance', + 'dest': GLANCE_CONF_DIR, } if os.path.exists(configs['dest']): @@ -418,7 +417,7 @@ def git_post_install(projects_yaml): bin_dir = os.path.join(git_pip_venv_dir(projects_yaml), 'bin') # Use systemd init units/scripts from ubuntu wily onward if lsb_release()['DISTRIB_RELEASE'] >= '15.10': - templates_dir = os.path.join(charm_dir(), 'templates/git') + templates_dir = os.path.join(charm_dir(), TEMPLATES, 'git') daemons = ['glance-api', 'glance-glare', 'glance-registry'] for daemon in daemons: glance_context = { @@ -437,7 +436,7 @@ def git_post_install(projects_yaml): 'start_dir': '/var/lib/glance', 'process_name': 'glance-api', 'executable_name': os.path.join(bin_dir, 'glance-api'), - 'config_files': ['/etc/glance/glance-api.conf'], + 'config_files': [GLANCE_API_CONF], 'log_file': '/var/log/glance/api.log', } @@ -448,7 +447,7 @@ def git_post_install(projects_yaml): 'start_dir': '/var/lib/glance', 'process_name': 'glance-registry', 'executable_name': os.path.join(bin_dir, 'glance-registry'), - 'config_files': ['/etc/glance/glance-registry.conf'], + 'config_files': [GLANCE_REGISTRY_CONF], 'log_file': '/var/log/glance/registry.log', } @@ -477,8 +476,10 @@ def get_optional_interfaces(): if relation_ids('ha'): optional_interfaces['ha'] = ['cluster'] - if relation_ids('ceph') or relation_ids('object-store'): - optional_interfaces['storage-backend'] = ['ceph', 'object-store'] + if (relation_ids('ceph') or relation_ids('object-store') or + relation_ids('cinder-volume-service')): + optional_interfaces['storage-backend'] = ['ceph', 'object-store', + 'cinder'] if relation_ids('amqp'): optional_interfaces['messaging'] = ['amqp'] diff --git a/metadata.yaml b/metadata.yaml index b439f486..dc78fc17 100644 --- a/metadata.yaml +++ b/metadata.yaml @@ -37,6 +37,8 @@ requires: ha: interface: hacluster scope: container + cinder-volume-service: + interface: cinder peers: cluster: interface: glance-ha diff --git a/templates/mitaka/glance-api.conf b/templates/mitaka/glance-api.conf index d5a0595d..b964608a 100644 --- a/templates/mitaka/glance-api.conf +++ b/templates/mitaka/glance-api.conf @@ -51,6 +51,8 @@ stores = {{ known_stores }} default_store = rbd {% elif swift_store -%} default_store = swift +{% elif cinder_store -%} +default_store = cinder {% else -%} default_store = file {% endif -%} diff --git a/unit_tests/test_glance_contexts.py b/unit_tests/test_glance_contexts.py index ca66f08d..1ee813fa 100644 --- a/unit_tests/test_glance_contexts.py +++ b/unit_tests/test_glance_contexts.py @@ -26,6 +26,7 @@ TO_PATCH = [ 'service_name', 'determine_apache_port', 'determine_api_port', + 'os_release', ] @@ -62,7 +63,8 @@ class TestGlanceContexts(CharmTestCase): 'expose_image_locations': True}) self.config.assert_called_with('expose-image-locations') - def test_multistore(self): + def test_multistore_below_mitaka(self): + self.os_release.return_value = 'liberty' self.relation_ids.return_value = ['random_rid'] self.assertEquals(contexts.MultiStoreContext()(), {'known_stores': "glance.store.filesystem.Store," @@ -70,6 +72,16 @@ class TestGlanceContexts(CharmTestCase): "glance.store.rbd.Store," "glance.store.swift.Store"}) + def test_multistore_for_mitaka_and_upper(self): + self.os_release.return_value = 'mitaka' + self.relation_ids.return_value = ['random_rid'] + self.assertEquals(contexts.MultiStoreContext()(), + {'known_stores': "glance.store.filesystem.Store," + "glance.store.http.Store," + "glance.store.rbd.Store," + "glance.store.swift.Store," + "glance.store.cinder.Store"}) + def test_multistore_defaults(self): self.relation_ids.return_value = [] self.assertEquals(contexts.MultiStoreContext()(), diff --git a/unit_tests/test_glance_relations.py b/unit_tests/test_glance_relations.py index ac827065..734aab49 100644 --- a/unit_tests/test_glance_relations.py +++ b/unit_tests/test_glance_relations.py @@ -35,9 +35,13 @@ utils.register_configs = MagicMock() utils.restart_map = MagicMock() with patch('hooks.charmhelpers.contrib.hardening.harden.harden') as mock_dec: - mock_dec.side_effect = (lambda *dargs, **dkwargs: lambda f: - lambda *args, **kwargs: f(*args, **kwargs)) - import hooks.glance_relations as relations + with patch('hooks.charmhelpers.contrib.openstack.' + 'utils.os_requires_version') as mock_os: + mock_dec.side_effect = (lambda *dargs, **dkwargs: lambda f: + lambda *args, **kwargs: f(*args, **kwargs)) + mock_os.side_effect = (lambda *dargs, **dkwargs: lambda f: + lambda *args, **kwargs: f(*args, **kwargs)) + import hooks.glance_relations as relations relations.hooks._config_save = False @@ -933,3 +937,14 @@ class GlanceRelationTests(CharmTestCase): def test_relation_broken(self, configs): relations.relation_broken() self.assertTrue(configs.write_all.called) + + @patch.object(relations, 'CONFIGS') + def test_cinder_volume_joined(self, configs): + self.filter_installed_packages.side_effect = lambda pkgs: pkgs + relations.cinder_volume_service_relation_joined() + self.assertTrue(configs.write_all.called) + self.apt_install.assert_called_with( + ["python-cinderclient", + "python-os-brick", + "python-oslo.rootwrap"], fatal=True + )