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 + )