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
This commit is contained in:
Andrey Pavlov 2016-08-22 15:37:38 +03:00 committed by James Page
parent ad8888b09b
commit fa1c1dda1c
9 changed files with 82 additions and 13 deletions

View File

@ -0,0 +1 @@
glance_relations.py

View File

@ -0,0 +1 @@
glance_relations.py

View File

@ -30,6 +30,10 @@ from charmhelpers.contrib.hahelpers.cluster import (
determine_api_port, determine_api_port,
) )
from charmhelpers.contrib.openstack.utils import (
os_release
)
class CephGlanceContext(OSContextGenerator): class CephGlanceContext(OSContextGenerator):
interfaces = ['ceph-glance'] 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): class MultiStoreContext(OSContextGenerator):
def __call__(self): def __call__(self):
@ -76,6 +95,9 @@ class MultiStoreContext(OSContextGenerator):
for store_relation, store_type in store_mapping.iteritems(): for store_relation, store_type in store_mapping.iteritems():
if relation_ids(store_relation): if relation_ids(store_relation):
stores.append(store_type) stores.append(store_type)
if (relation_ids('cinder-volume-service') and
os_release('glance-common') >= 'mitaka'):
stores.append('glance.store.cinder.Store')
return { return {
'known_stores': ','.join(stores) 'known_stores': ','.join(stores)
} }

View File

@ -88,6 +88,7 @@ from charmhelpers.contrib.openstack.utils import (
sync_db_with_multi_ipv6_addresses, sync_db_with_multi_ipv6_addresses,
pausable_restart_on_change as restart_on_change, pausable_restart_on_change as restart_on_change,
is_unit_paused_set, is_unit_paused_set,
os_requires_version,
) )
from charmhelpers.contrib.storage.linux.ceph import ( from charmhelpers.contrib.storage.linux.ceph import (
send_request_if_needed, send_request_if_needed,
@ -526,7 +527,8 @@ def ha_relation_changed():
@hooks.hook('identity-service-relation-broken', @hooks.hook('identity-service-relation-broken',
'object-store-relation-broken', 'object-store-relation-broken',
'shared-db-relation-broken', 'shared-db-relation-broken',
'pgsql-db-relation-broken') 'pgsql-db-relation-broken',
'cinder-volume-service-relation-broken')
def relation_broken(): def relation_broken():
CONFIGS.write_all() CONFIGS.write_all()
@ -590,6 +592,17 @@ def update_status():
juju_log('Updating 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__': if __name__ == '__main__':
try: try:
hooks.execute(sys.argv) hooks.execute(sys.argv)

View File

@ -142,8 +142,6 @@ HTTPS_APACHE_CONF = "/etc/apache2/sites-available/openstack_https_frontend"
HTTPS_APACHE_24_CONF = "/etc/apache2/sites-available/" \ HTTPS_APACHE_24_CONF = "/etc/apache2/sites-available/" \
"openstack_https_frontend.conf" "openstack_https_frontend.conf"
CONF_DIR = "/etc/glance"
TEMPLATES = 'templates/' TEMPLATES = 'templates/'
# The interface is said to be satisfied if anyone of the interfaces in the # The interface is said to be satisfied if anyone of the interfaces in the
@ -182,6 +180,7 @@ CONFIG_FILES = OrderedDict([
service_user='glance'), service_user='glance'),
glance_contexts.CephGlanceContext(), glance_contexts.CephGlanceContext(),
glance_contexts.ObjectStoreContext(), glance_contexts.ObjectStoreContext(),
glance_contexts.CinderStoreContext(),
glance_contexts.HAProxyContext(), glance_contexts.HAProxyContext(),
context.SyslogContext(), context.SyslogContext(),
glance_contexts.LoggingConfigContext(), 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') src_etc = os.path.join(git_src_dir(projects_yaml, 'glance'), 'etc')
configs = { configs = {
'src': src_etc, 'src': src_etc,
'dest': '/etc/glance', 'dest': GLANCE_CONF_DIR,
} }
if os.path.exists(configs['dest']): 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') bin_dir = os.path.join(git_pip_venv_dir(projects_yaml), 'bin')
# Use systemd init units/scripts from ubuntu wily onward # Use systemd init units/scripts from ubuntu wily onward
if lsb_release()['DISTRIB_RELEASE'] >= '15.10': 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'] daemons = ['glance-api', 'glance-glare', 'glance-registry']
for daemon in daemons: for daemon in daemons:
glance_context = { glance_context = {
@ -437,7 +436,7 @@ def git_post_install(projects_yaml):
'start_dir': '/var/lib/glance', 'start_dir': '/var/lib/glance',
'process_name': 'glance-api', 'process_name': 'glance-api',
'executable_name': os.path.join(bin_dir, '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', 'log_file': '/var/log/glance/api.log',
} }
@ -448,7 +447,7 @@ def git_post_install(projects_yaml):
'start_dir': '/var/lib/glance', 'start_dir': '/var/lib/glance',
'process_name': 'glance-registry', 'process_name': 'glance-registry',
'executable_name': os.path.join(bin_dir, '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', 'log_file': '/var/log/glance/registry.log',
} }
@ -477,8 +476,10 @@ def get_optional_interfaces():
if relation_ids('ha'): if relation_ids('ha'):
optional_interfaces['ha'] = ['cluster'] optional_interfaces['ha'] = ['cluster']
if relation_ids('ceph') or relation_ids('object-store'): if (relation_ids('ceph') or relation_ids('object-store') or
optional_interfaces['storage-backend'] = ['ceph', 'object-store'] relation_ids('cinder-volume-service')):
optional_interfaces['storage-backend'] = ['ceph', 'object-store',
'cinder']
if relation_ids('amqp'): if relation_ids('amqp'):
optional_interfaces['messaging'] = ['amqp'] optional_interfaces['messaging'] = ['amqp']

View File

@ -37,6 +37,8 @@ requires:
ha: ha:
interface: hacluster interface: hacluster
scope: container scope: container
cinder-volume-service:
interface: cinder
peers: peers:
cluster: cluster:
interface: glance-ha interface: glance-ha

View File

@ -51,6 +51,8 @@ stores = {{ known_stores }}
default_store = rbd default_store = rbd
{% elif swift_store -%} {% elif swift_store -%}
default_store = swift default_store = swift
{% elif cinder_store -%}
default_store = cinder
{% else -%} {% else -%}
default_store = file default_store = file
{% endif -%} {% endif -%}

View File

@ -26,6 +26,7 @@ TO_PATCH = [
'service_name', 'service_name',
'determine_apache_port', 'determine_apache_port',
'determine_api_port', 'determine_api_port',
'os_release',
] ]
@ -62,7 +63,8 @@ class TestGlanceContexts(CharmTestCase):
'expose_image_locations': True}) 'expose_image_locations': True})
self.config.assert_called_with('expose-image-locations') 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.relation_ids.return_value = ['random_rid']
self.assertEquals(contexts.MultiStoreContext()(), self.assertEquals(contexts.MultiStoreContext()(),
{'known_stores': "glance.store.filesystem.Store," {'known_stores': "glance.store.filesystem.Store,"
@ -70,6 +72,16 @@ class TestGlanceContexts(CharmTestCase):
"glance.store.rbd.Store," "glance.store.rbd.Store,"
"glance.store.swift.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): def test_multistore_defaults(self):
self.relation_ids.return_value = [] self.relation_ids.return_value = []
self.assertEquals(contexts.MultiStoreContext()(), self.assertEquals(contexts.MultiStoreContext()(),

View File

@ -35,9 +35,13 @@ utils.register_configs = MagicMock()
utils.restart_map = MagicMock() utils.restart_map = MagicMock()
with patch('hooks.charmhelpers.contrib.hardening.harden.harden') as mock_dec: with patch('hooks.charmhelpers.contrib.hardening.harden.harden') as mock_dec:
mock_dec.side_effect = (lambda *dargs, **dkwargs: lambda f: with patch('hooks.charmhelpers.contrib.openstack.'
lambda *args, **kwargs: f(*args, **kwargs)) 'utils.os_requires_version') as mock_os:
import hooks.glance_relations as relations 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 relations.hooks._config_save = False
@ -933,3 +937,14 @@ class GlanceRelationTests(CharmTestCase):
def test_relation_broken(self, configs): def test_relation_broken(self, configs):
relations.relation_broken() relations.relation_broken()
self.assertTrue(configs.write_all.called) 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
)