From d807c89784360ce0a5335df5338b0e20666eca3b Mon Sep 17 00:00:00 2001 From: "yolanda.robla@canonical.com" <> Date: Tue, 25 Mar 2014 11:34:12 +0100 Subject: [PATCH] added postgresql support --- .../charmhelpers/contrib/openstack/context.py | 30 ++ hooks/cinder_hooks.py | 36 +- hooks/cinder_utils.py | 2 + hooks/postgresql-db-relation-broken | 320 ++++++++++++++++++ hooks/postgresql-db-relation-changed | 320 ++++++++++++++++++ hooks/postgresql-db-relation-joined | 320 ++++++++++++++++++ metadata.yaml | 2 + revision | 2 +- templates/cinder.conf | 2 +- templates/grizzly/cinder.conf | 2 +- templates/icehouse/cinder.conf | 2 +- 11 files changed, 1033 insertions(+), 5 deletions(-) create mode 100755 hooks/postgresql-db-relation-broken create mode 100755 hooks/postgresql-db-relation-changed create mode 100755 hooks/postgresql-db-relation-joined diff --git a/hooks/charmhelpers/contrib/openstack/context.py b/hooks/charmhelpers/contrib/openstack/context.py index a2d4636b..d547a4fb 100644 --- a/hooks/charmhelpers/contrib/openstack/context.py +++ b/hooks/charmhelpers/contrib/openstack/context.py @@ -146,6 +146,36 @@ class SharedDBContext(OSContextGenerator): 'database': self.database, 'database_user': self.user, 'database_password': passwd, + 'database_type': 'mysql', + } + if context_complete(ctxt): + return ctxt + return {} + + +class PostgresqlDBContext(OSContextGenerator): + interfaces = ['pgsql-db'] + + def __init__(self, database=None): + self.database = database + + def __call__(self): + self.database = self.database or config('database') + if self.database is None: + log('Could not generate postgresql_db context. ' + 'Missing required charm config options. ' + '(database name)') + raise OSContextError + ctxt = {} + + for rid in relation_ids(self.interfaces[0]): + for unit in related_units(rid): + ctxt = { + 'database_host': relation_get('host', rid=rid, unit=unit), + 'database': self.database, + 'database_user': relation_get('user', rid=rid, unit=unit), + 'database_password': relation_get('password', rid=rid, unit=unit), + 'database_type': 'postgresql', } if context_complete(ctxt): return ctxt diff --git a/hooks/cinder_hooks.py b/hooks/cinder_hooks.py index 9db80142..ebf71691 100755 --- a/hooks/cinder_hooks.py +++ b/hooks/cinder_hooks.py @@ -28,11 +28,14 @@ from charmhelpers.core.hookenv import ( Hooks, UnregisteredHookError, config, + is_relation_made, relation_get, relation_ids, relation_set, service_name, unit_get, + log, + ERROR ) from charmhelpers.fetch import apt_install, apt_update @@ -90,11 +93,29 @@ def config_changed(): @hooks.hook('shared-db-relation-joined') def db_joined(): + if is_relation_made('pgsql-db'): + # error, postgresql is used + e = ('Attempting to associate a mysql database when there is already ' + 'associated a postgresql one') + log(e, level=ERROR) + raise Exception(e) + conf = config() relation_set(database=conf['database'], username=conf['database-user'], hostname=unit_get('private-address')) +@hooks.hook('pgsql-db-relation-joined') +def pgsql_db_joined(): + if is_relation_made('shared-db'): + # raise error + e = ('Attempting to associate a postgresql database when there is already ' + 'associated a mysql one') + log(e, level=ERROR) + raise Exception(e) + relation_set(database=config('database')) + + @hooks.hook('shared-db-relation-changed') @restart_on_change(restart_map()) def db_changed(): @@ -107,6 +128,18 @@ def db_changed(): migrate_database() +@hooks.hook('pgsql-db-relation-changed') +@restart_on_change(restart_map()) +def db_changed(): + if 'pgsql-db' not in CONFIGS.complete_contexts(): + juju_log('pgsql-db relation incomplete. Peer not ready?') + return + CONFIGS.write(CINDER_CONF) + if eligible_leader(CLUSTER_RES): + juju_log('Cluster leader, performing db sync') + migrate_database() + + @hooks.hook('amqp-relation-joined') def amqp_joined(relation_id=None): conf = config() @@ -249,7 +282,8 @@ def image_service_changed(): 'ceph-relation-broken', 'identity-service-relation-broken', 'image-service-relation-broken', - 'shared-db-relation-broken') + 'shared-db-relation-broken', + 'pgsql-db-relation-broken') @restart_on_change(restart_map(), stopstart=True) def relation_broken(): CONFIGS.write_all() diff --git a/hooks/cinder_utils.py b/hooks/cinder_utils.py index b4d4c7b8..b85edee3 100644 --- a/hooks/cinder_utils.py +++ b/hooks/cinder_utils.py @@ -73,6 +73,7 @@ COMMON_PACKAGES = [ 'python-jinja2', 'python-keystoneclient', 'python-mysqldb', + 'python-psycopg2', 'qemu-utils', ] @@ -109,6 +110,7 @@ def ceph_config_file(): CONFIG_FILES = OrderedDict([ (CINDER_CONF, { 'hook_contexts': [context.SharedDBContext(), + context.PostgresqlDBContext(), context.AMQPContext(), context.ImageServiceContext(), context.OSConfigFlagContext(), diff --git a/hooks/postgresql-db-relation-broken b/hooks/postgresql-db-relation-broken new file mode 100755 index 00000000..ebf71691 --- /dev/null +++ b/hooks/postgresql-db-relation-broken @@ -0,0 +1,320 @@ +#!/usr/bin/python + +import os +import sys + +from subprocess import check_call + +from cinder_utils import ( + clean_storage, + determine_packages, + do_openstack_upgrade, + ensure_block_device, + ensure_ceph_pool, + juju_log, + migrate_database, + prepare_lvm_storage, + register_configs, + restart_map, + service_enabled, + set_ceph_env_variables, + CLUSTER_RES, + CINDER_CONF, + CINDER_API_CONF, + ceph_config_file +) + +from charmhelpers.core.hookenv import ( + Hooks, + UnregisteredHookError, + config, + is_relation_made, + relation_get, + relation_ids, + relation_set, + service_name, + unit_get, + log, + ERROR +) + +from charmhelpers.fetch import apt_install, apt_update +from charmhelpers.core.host import lsb_release, restart_on_change + +from charmhelpers.contrib.openstack.utils import ( + configure_installation_source, openstack_upgrade_available) + +from charmhelpers.contrib.storage.linux.ceph import ensure_ceph_keyring + +from charmhelpers.contrib.hahelpers.cluster import ( + canonical_url, + eligible_leader, + is_leader, + get_hacluster_config, +) + +from charmhelpers.payload.execd import execd_preinstall + +hooks = Hooks() + +CONFIGS = register_configs() + + +@hooks.hook('install') +def install(): + execd_preinstall() + conf = config() + src = conf['openstack-origin'] + if (lsb_release()['DISTRIB_CODENAME'] == 'precise' and + src == 'distro'): + src = 'cloud:precise-folsom' + configure_installation_source(src) + apt_update() + apt_install(determine_packages(), fatal=True) + + if (service_enabled('volume') and + conf['block-device'] not in [None, 'None', 'none']): + bdev = ensure_block_device(conf['block-device']) + juju_log('Located valid block device: %s' % bdev) + if conf['overwrite'] in ['true', 'True', True]: + juju_log('Ensuring block device is clean: %s' % bdev) + clean_storage(bdev) + prepare_lvm_storage(bdev, conf['volume-group']) + + +@hooks.hook('config-changed') +@restart_on_change(restart_map(), stopstart=True) +def config_changed(): + if openstack_upgrade_available('cinder-common'): + do_openstack_upgrade(configs=CONFIGS) + CONFIGS.write_all() + configure_https() + + +@hooks.hook('shared-db-relation-joined') +def db_joined(): + if is_relation_made('pgsql-db'): + # error, postgresql is used + e = ('Attempting to associate a mysql database when there is already ' + 'associated a postgresql one') + log(e, level=ERROR) + raise Exception(e) + + conf = config() + relation_set(database=conf['database'], username=conf['database-user'], + hostname=unit_get('private-address')) + + +@hooks.hook('pgsql-db-relation-joined') +def pgsql_db_joined(): + if is_relation_made('shared-db'): + # raise error + e = ('Attempting to associate a postgresql database when there is already ' + 'associated a mysql one') + log(e, level=ERROR) + raise Exception(e) + relation_set(database=config('database')) + + +@hooks.hook('shared-db-relation-changed') +@restart_on_change(restart_map()) +def db_changed(): + if 'shared-db' not in CONFIGS.complete_contexts(): + juju_log('shared-db relation incomplete. Peer not ready?') + return + CONFIGS.write(CINDER_CONF) + if eligible_leader(CLUSTER_RES): + juju_log('Cluster leader, performing db sync') + migrate_database() + + +@hooks.hook('pgsql-db-relation-changed') +@restart_on_change(restart_map()) +def db_changed(): + if 'pgsql-db' not in CONFIGS.complete_contexts(): + juju_log('pgsql-db relation incomplete. Peer not ready?') + return + CONFIGS.write(CINDER_CONF) + if eligible_leader(CLUSTER_RES): + juju_log('Cluster leader, performing db sync') + migrate_database() + + +@hooks.hook('amqp-relation-joined') +def amqp_joined(relation_id=None): + conf = config() + relation_set(relation_id=relation_id, + username=conf['rabbit-user'], vhost=conf['rabbit-vhost']) + + +@hooks.hook('amqp-relation-changed') +@restart_on_change(restart_map()) +def amqp_changed(): + if 'amqp' not in CONFIGS.complete_contexts(): + juju_log('amqp relation incomplete. Peer not ready?') + return + CONFIGS.write(CINDER_CONF) + +@hooks.hook('amqp-relation-departed') +@restart_on_change(restart_map()) +def amqp_departed(): + if 'amqp' not in CONFIGS.complete_contexts(): + juju_log('amqp relation incomplete. Peer not ready?') + return + CONFIGS.write(CINDER_CONF) + + +@hooks.hook('identity-service-relation-joined') +def identity_joined(rid=None): + if not eligible_leader(CLUSTER_RES): + return + + conf = config() + + port = conf['api-listening-port'] + url = canonical_url(CONFIGS) + ':%s/v1/$(tenant_id)s' % port + + settings = { + 'region': conf['region'], + 'service': 'cinder', + 'public_url': url, + 'internal_url': url, + 'admin_url': url, + } + relation_set(relation_id=rid, **settings) + + +@hooks.hook('identity-service-relation-changed') +@restart_on_change(restart_map()) +def identity_changed(): + if 'identity-service' not in CONFIGS.complete_contexts(): + juju_log('identity-service relation incomplete. Peer not ready?') + return + CONFIGS.write(CINDER_API_CONF) + configure_https() + + +@hooks.hook('ceph-relation-joined') +def ceph_joined(): + if not os.path.isdir('/etc/ceph'): + os.mkdir('/etc/ceph') + apt_install('ceph-common', fatal=True) + + +@hooks.hook('ceph-relation-changed') +@restart_on_change(restart_map()) +def ceph_changed(): + if 'ceph' not in CONFIGS.complete_contexts(): + juju_log('ceph relation incomplete. Peer not ready?') + return + svc = service_name() + if not ensure_ceph_keyring(service=svc, + user='cinder', group='cinder'): + juju_log('Could not create ceph keyring: peer not ready?') + return + CONFIGS.write(CINDER_CONF) + CONFIGS.write(ceph_config_file()) + set_ceph_env_variables(service=svc) + + if eligible_leader(CLUSTER_RES): + _config = config() + ensure_ceph_pool(service=svc, + replicas=_config['ceph-osd-replication-count']) + + +@hooks.hook('cluster-relation-changed', + 'cluster-relation-departed') +@restart_on_change(restart_map(), stopstart=True) +def cluster_changed(): + CONFIGS.write_all() + + +@hooks.hook('ha-relation-joined') +def ha_joined(): + config = get_hacluster_config() + resources = { + 'res_cinder_vip': 'ocf:heartbeat:IPaddr2', + 'res_cinder_haproxy': 'lsb:haproxy' + } + + vip_params = 'params ip="%s" cidr_netmask="%s" nic="%s"' % \ + (config['vip'], config['vip_cidr'], config['vip_iface']) + resource_params = { + 'res_cinder_vip': vip_params, + 'res_cinder_haproxy': 'op monitor interval="5s"' + } + init_services = { + 'res_cinder_haproxy': 'haproxy' + } + clones = { + 'cl_cinder_haproxy': 'res_cinder_haproxy' + } + relation_set(init_services=init_services, + corosync_bindiface=config['ha-bindiface'], + corosync_mcastport=config['ha-mcastport'], + resources=resources, + resource_params=resource_params, + clones=clones) + + +@hooks.hook('ha-relation-changed') +def ha_changed(): + clustered = relation_get('clustered') + if not clustered or clustered in [None, 'None', '']: + juju_log('ha_changed: hacluster subordinate not fully clustered.') + return + if not is_leader(CLUSTER_RES): + juju_log('ha_changed: hacluster complete but we are not leader.') + return + juju_log('Cluster configured, notifying other services and updating ' + 'keystone endpoint configuration') + for rid in relation_ids('identity-service'): + identity_joined(rid=rid) + + +@hooks.hook('image-service-relation-changed') +@restart_on_change(restart_map()) +def image_service_changed(): + CONFIGS.write(CINDER_CONF) + + +@hooks.hook('amqp-relation-broken', + 'ceph-relation-broken', + 'identity-service-relation-broken', + 'image-service-relation-broken', + 'shared-db-relation-broken', + 'pgsql-db-relation-broken') +@restart_on_change(restart_map(), stopstart=True) +def relation_broken(): + CONFIGS.write_all() + + +def configure_https(): + '''Enables SSL API Apache config if appropriate and kicks identity-service + with any required api updates. + ''' + # need to write all to ensure changes to the entire request pipeline + # propagate (c-api, haprxy, apache) + CONFIGS.write_all() + if 'https' in CONFIGS.complete_contexts(): + cmd = ['a2ensite', 'openstack_https_frontend'] + check_call(cmd) + else: + cmd = ['a2dissite', 'openstack_https_frontend'] + check_call(cmd) + + for rid in relation_ids('identity-service'): + identity_joined(rid=rid) + + +@hooks.hook('upgrade-charm') +def upgrade_charm(): + for rel_id in relation_ids('amqp'): + amqp_joined(relation_id=rel_id) + + +if __name__ == '__main__': + try: + hooks.execute(sys.argv) + except UnregisteredHookError as e: + juju_log('Unknown hook {} - skipping.'.format(e)) diff --git a/hooks/postgresql-db-relation-changed b/hooks/postgresql-db-relation-changed new file mode 100755 index 00000000..ebf71691 --- /dev/null +++ b/hooks/postgresql-db-relation-changed @@ -0,0 +1,320 @@ +#!/usr/bin/python + +import os +import sys + +from subprocess import check_call + +from cinder_utils import ( + clean_storage, + determine_packages, + do_openstack_upgrade, + ensure_block_device, + ensure_ceph_pool, + juju_log, + migrate_database, + prepare_lvm_storage, + register_configs, + restart_map, + service_enabled, + set_ceph_env_variables, + CLUSTER_RES, + CINDER_CONF, + CINDER_API_CONF, + ceph_config_file +) + +from charmhelpers.core.hookenv import ( + Hooks, + UnregisteredHookError, + config, + is_relation_made, + relation_get, + relation_ids, + relation_set, + service_name, + unit_get, + log, + ERROR +) + +from charmhelpers.fetch import apt_install, apt_update +from charmhelpers.core.host import lsb_release, restart_on_change + +from charmhelpers.contrib.openstack.utils import ( + configure_installation_source, openstack_upgrade_available) + +from charmhelpers.contrib.storage.linux.ceph import ensure_ceph_keyring + +from charmhelpers.contrib.hahelpers.cluster import ( + canonical_url, + eligible_leader, + is_leader, + get_hacluster_config, +) + +from charmhelpers.payload.execd import execd_preinstall + +hooks = Hooks() + +CONFIGS = register_configs() + + +@hooks.hook('install') +def install(): + execd_preinstall() + conf = config() + src = conf['openstack-origin'] + if (lsb_release()['DISTRIB_CODENAME'] == 'precise' and + src == 'distro'): + src = 'cloud:precise-folsom' + configure_installation_source(src) + apt_update() + apt_install(determine_packages(), fatal=True) + + if (service_enabled('volume') and + conf['block-device'] not in [None, 'None', 'none']): + bdev = ensure_block_device(conf['block-device']) + juju_log('Located valid block device: %s' % bdev) + if conf['overwrite'] in ['true', 'True', True]: + juju_log('Ensuring block device is clean: %s' % bdev) + clean_storage(bdev) + prepare_lvm_storage(bdev, conf['volume-group']) + + +@hooks.hook('config-changed') +@restart_on_change(restart_map(), stopstart=True) +def config_changed(): + if openstack_upgrade_available('cinder-common'): + do_openstack_upgrade(configs=CONFIGS) + CONFIGS.write_all() + configure_https() + + +@hooks.hook('shared-db-relation-joined') +def db_joined(): + if is_relation_made('pgsql-db'): + # error, postgresql is used + e = ('Attempting to associate a mysql database when there is already ' + 'associated a postgresql one') + log(e, level=ERROR) + raise Exception(e) + + conf = config() + relation_set(database=conf['database'], username=conf['database-user'], + hostname=unit_get('private-address')) + + +@hooks.hook('pgsql-db-relation-joined') +def pgsql_db_joined(): + if is_relation_made('shared-db'): + # raise error + e = ('Attempting to associate a postgresql database when there is already ' + 'associated a mysql one') + log(e, level=ERROR) + raise Exception(e) + relation_set(database=config('database')) + + +@hooks.hook('shared-db-relation-changed') +@restart_on_change(restart_map()) +def db_changed(): + if 'shared-db' not in CONFIGS.complete_contexts(): + juju_log('shared-db relation incomplete. Peer not ready?') + return + CONFIGS.write(CINDER_CONF) + if eligible_leader(CLUSTER_RES): + juju_log('Cluster leader, performing db sync') + migrate_database() + + +@hooks.hook('pgsql-db-relation-changed') +@restart_on_change(restart_map()) +def db_changed(): + if 'pgsql-db' not in CONFIGS.complete_contexts(): + juju_log('pgsql-db relation incomplete. Peer not ready?') + return + CONFIGS.write(CINDER_CONF) + if eligible_leader(CLUSTER_RES): + juju_log('Cluster leader, performing db sync') + migrate_database() + + +@hooks.hook('amqp-relation-joined') +def amqp_joined(relation_id=None): + conf = config() + relation_set(relation_id=relation_id, + username=conf['rabbit-user'], vhost=conf['rabbit-vhost']) + + +@hooks.hook('amqp-relation-changed') +@restart_on_change(restart_map()) +def amqp_changed(): + if 'amqp' not in CONFIGS.complete_contexts(): + juju_log('amqp relation incomplete. Peer not ready?') + return + CONFIGS.write(CINDER_CONF) + +@hooks.hook('amqp-relation-departed') +@restart_on_change(restart_map()) +def amqp_departed(): + if 'amqp' not in CONFIGS.complete_contexts(): + juju_log('amqp relation incomplete. Peer not ready?') + return + CONFIGS.write(CINDER_CONF) + + +@hooks.hook('identity-service-relation-joined') +def identity_joined(rid=None): + if not eligible_leader(CLUSTER_RES): + return + + conf = config() + + port = conf['api-listening-port'] + url = canonical_url(CONFIGS) + ':%s/v1/$(tenant_id)s' % port + + settings = { + 'region': conf['region'], + 'service': 'cinder', + 'public_url': url, + 'internal_url': url, + 'admin_url': url, + } + relation_set(relation_id=rid, **settings) + + +@hooks.hook('identity-service-relation-changed') +@restart_on_change(restart_map()) +def identity_changed(): + if 'identity-service' not in CONFIGS.complete_contexts(): + juju_log('identity-service relation incomplete. Peer not ready?') + return + CONFIGS.write(CINDER_API_CONF) + configure_https() + + +@hooks.hook('ceph-relation-joined') +def ceph_joined(): + if not os.path.isdir('/etc/ceph'): + os.mkdir('/etc/ceph') + apt_install('ceph-common', fatal=True) + + +@hooks.hook('ceph-relation-changed') +@restart_on_change(restart_map()) +def ceph_changed(): + if 'ceph' not in CONFIGS.complete_contexts(): + juju_log('ceph relation incomplete. Peer not ready?') + return + svc = service_name() + if not ensure_ceph_keyring(service=svc, + user='cinder', group='cinder'): + juju_log('Could not create ceph keyring: peer not ready?') + return + CONFIGS.write(CINDER_CONF) + CONFIGS.write(ceph_config_file()) + set_ceph_env_variables(service=svc) + + if eligible_leader(CLUSTER_RES): + _config = config() + ensure_ceph_pool(service=svc, + replicas=_config['ceph-osd-replication-count']) + + +@hooks.hook('cluster-relation-changed', + 'cluster-relation-departed') +@restart_on_change(restart_map(), stopstart=True) +def cluster_changed(): + CONFIGS.write_all() + + +@hooks.hook('ha-relation-joined') +def ha_joined(): + config = get_hacluster_config() + resources = { + 'res_cinder_vip': 'ocf:heartbeat:IPaddr2', + 'res_cinder_haproxy': 'lsb:haproxy' + } + + vip_params = 'params ip="%s" cidr_netmask="%s" nic="%s"' % \ + (config['vip'], config['vip_cidr'], config['vip_iface']) + resource_params = { + 'res_cinder_vip': vip_params, + 'res_cinder_haproxy': 'op monitor interval="5s"' + } + init_services = { + 'res_cinder_haproxy': 'haproxy' + } + clones = { + 'cl_cinder_haproxy': 'res_cinder_haproxy' + } + relation_set(init_services=init_services, + corosync_bindiface=config['ha-bindiface'], + corosync_mcastport=config['ha-mcastport'], + resources=resources, + resource_params=resource_params, + clones=clones) + + +@hooks.hook('ha-relation-changed') +def ha_changed(): + clustered = relation_get('clustered') + if not clustered or clustered in [None, 'None', '']: + juju_log('ha_changed: hacluster subordinate not fully clustered.') + return + if not is_leader(CLUSTER_RES): + juju_log('ha_changed: hacluster complete but we are not leader.') + return + juju_log('Cluster configured, notifying other services and updating ' + 'keystone endpoint configuration') + for rid in relation_ids('identity-service'): + identity_joined(rid=rid) + + +@hooks.hook('image-service-relation-changed') +@restart_on_change(restart_map()) +def image_service_changed(): + CONFIGS.write(CINDER_CONF) + + +@hooks.hook('amqp-relation-broken', + 'ceph-relation-broken', + 'identity-service-relation-broken', + 'image-service-relation-broken', + 'shared-db-relation-broken', + 'pgsql-db-relation-broken') +@restart_on_change(restart_map(), stopstart=True) +def relation_broken(): + CONFIGS.write_all() + + +def configure_https(): + '''Enables SSL API Apache config if appropriate and kicks identity-service + with any required api updates. + ''' + # need to write all to ensure changes to the entire request pipeline + # propagate (c-api, haprxy, apache) + CONFIGS.write_all() + if 'https' in CONFIGS.complete_contexts(): + cmd = ['a2ensite', 'openstack_https_frontend'] + check_call(cmd) + else: + cmd = ['a2dissite', 'openstack_https_frontend'] + check_call(cmd) + + for rid in relation_ids('identity-service'): + identity_joined(rid=rid) + + +@hooks.hook('upgrade-charm') +def upgrade_charm(): + for rel_id in relation_ids('amqp'): + amqp_joined(relation_id=rel_id) + + +if __name__ == '__main__': + try: + hooks.execute(sys.argv) + except UnregisteredHookError as e: + juju_log('Unknown hook {} - skipping.'.format(e)) diff --git a/hooks/postgresql-db-relation-joined b/hooks/postgresql-db-relation-joined new file mode 100755 index 00000000..ebf71691 --- /dev/null +++ b/hooks/postgresql-db-relation-joined @@ -0,0 +1,320 @@ +#!/usr/bin/python + +import os +import sys + +from subprocess import check_call + +from cinder_utils import ( + clean_storage, + determine_packages, + do_openstack_upgrade, + ensure_block_device, + ensure_ceph_pool, + juju_log, + migrate_database, + prepare_lvm_storage, + register_configs, + restart_map, + service_enabled, + set_ceph_env_variables, + CLUSTER_RES, + CINDER_CONF, + CINDER_API_CONF, + ceph_config_file +) + +from charmhelpers.core.hookenv import ( + Hooks, + UnregisteredHookError, + config, + is_relation_made, + relation_get, + relation_ids, + relation_set, + service_name, + unit_get, + log, + ERROR +) + +from charmhelpers.fetch import apt_install, apt_update +from charmhelpers.core.host import lsb_release, restart_on_change + +from charmhelpers.contrib.openstack.utils import ( + configure_installation_source, openstack_upgrade_available) + +from charmhelpers.contrib.storage.linux.ceph import ensure_ceph_keyring + +from charmhelpers.contrib.hahelpers.cluster import ( + canonical_url, + eligible_leader, + is_leader, + get_hacluster_config, +) + +from charmhelpers.payload.execd import execd_preinstall + +hooks = Hooks() + +CONFIGS = register_configs() + + +@hooks.hook('install') +def install(): + execd_preinstall() + conf = config() + src = conf['openstack-origin'] + if (lsb_release()['DISTRIB_CODENAME'] == 'precise' and + src == 'distro'): + src = 'cloud:precise-folsom' + configure_installation_source(src) + apt_update() + apt_install(determine_packages(), fatal=True) + + if (service_enabled('volume') and + conf['block-device'] not in [None, 'None', 'none']): + bdev = ensure_block_device(conf['block-device']) + juju_log('Located valid block device: %s' % bdev) + if conf['overwrite'] in ['true', 'True', True]: + juju_log('Ensuring block device is clean: %s' % bdev) + clean_storage(bdev) + prepare_lvm_storage(bdev, conf['volume-group']) + + +@hooks.hook('config-changed') +@restart_on_change(restart_map(), stopstart=True) +def config_changed(): + if openstack_upgrade_available('cinder-common'): + do_openstack_upgrade(configs=CONFIGS) + CONFIGS.write_all() + configure_https() + + +@hooks.hook('shared-db-relation-joined') +def db_joined(): + if is_relation_made('pgsql-db'): + # error, postgresql is used + e = ('Attempting to associate a mysql database when there is already ' + 'associated a postgresql one') + log(e, level=ERROR) + raise Exception(e) + + conf = config() + relation_set(database=conf['database'], username=conf['database-user'], + hostname=unit_get('private-address')) + + +@hooks.hook('pgsql-db-relation-joined') +def pgsql_db_joined(): + if is_relation_made('shared-db'): + # raise error + e = ('Attempting to associate a postgresql database when there is already ' + 'associated a mysql one') + log(e, level=ERROR) + raise Exception(e) + relation_set(database=config('database')) + + +@hooks.hook('shared-db-relation-changed') +@restart_on_change(restart_map()) +def db_changed(): + if 'shared-db' not in CONFIGS.complete_contexts(): + juju_log('shared-db relation incomplete. Peer not ready?') + return + CONFIGS.write(CINDER_CONF) + if eligible_leader(CLUSTER_RES): + juju_log('Cluster leader, performing db sync') + migrate_database() + + +@hooks.hook('pgsql-db-relation-changed') +@restart_on_change(restart_map()) +def db_changed(): + if 'pgsql-db' not in CONFIGS.complete_contexts(): + juju_log('pgsql-db relation incomplete. Peer not ready?') + return + CONFIGS.write(CINDER_CONF) + if eligible_leader(CLUSTER_RES): + juju_log('Cluster leader, performing db sync') + migrate_database() + + +@hooks.hook('amqp-relation-joined') +def amqp_joined(relation_id=None): + conf = config() + relation_set(relation_id=relation_id, + username=conf['rabbit-user'], vhost=conf['rabbit-vhost']) + + +@hooks.hook('amqp-relation-changed') +@restart_on_change(restart_map()) +def amqp_changed(): + if 'amqp' not in CONFIGS.complete_contexts(): + juju_log('amqp relation incomplete. Peer not ready?') + return + CONFIGS.write(CINDER_CONF) + +@hooks.hook('amqp-relation-departed') +@restart_on_change(restart_map()) +def amqp_departed(): + if 'amqp' not in CONFIGS.complete_contexts(): + juju_log('amqp relation incomplete. Peer not ready?') + return + CONFIGS.write(CINDER_CONF) + + +@hooks.hook('identity-service-relation-joined') +def identity_joined(rid=None): + if not eligible_leader(CLUSTER_RES): + return + + conf = config() + + port = conf['api-listening-port'] + url = canonical_url(CONFIGS) + ':%s/v1/$(tenant_id)s' % port + + settings = { + 'region': conf['region'], + 'service': 'cinder', + 'public_url': url, + 'internal_url': url, + 'admin_url': url, + } + relation_set(relation_id=rid, **settings) + + +@hooks.hook('identity-service-relation-changed') +@restart_on_change(restart_map()) +def identity_changed(): + if 'identity-service' not in CONFIGS.complete_contexts(): + juju_log('identity-service relation incomplete. Peer not ready?') + return + CONFIGS.write(CINDER_API_CONF) + configure_https() + + +@hooks.hook('ceph-relation-joined') +def ceph_joined(): + if not os.path.isdir('/etc/ceph'): + os.mkdir('/etc/ceph') + apt_install('ceph-common', fatal=True) + + +@hooks.hook('ceph-relation-changed') +@restart_on_change(restart_map()) +def ceph_changed(): + if 'ceph' not in CONFIGS.complete_contexts(): + juju_log('ceph relation incomplete. Peer not ready?') + return + svc = service_name() + if not ensure_ceph_keyring(service=svc, + user='cinder', group='cinder'): + juju_log('Could not create ceph keyring: peer not ready?') + return + CONFIGS.write(CINDER_CONF) + CONFIGS.write(ceph_config_file()) + set_ceph_env_variables(service=svc) + + if eligible_leader(CLUSTER_RES): + _config = config() + ensure_ceph_pool(service=svc, + replicas=_config['ceph-osd-replication-count']) + + +@hooks.hook('cluster-relation-changed', + 'cluster-relation-departed') +@restart_on_change(restart_map(), stopstart=True) +def cluster_changed(): + CONFIGS.write_all() + + +@hooks.hook('ha-relation-joined') +def ha_joined(): + config = get_hacluster_config() + resources = { + 'res_cinder_vip': 'ocf:heartbeat:IPaddr2', + 'res_cinder_haproxy': 'lsb:haproxy' + } + + vip_params = 'params ip="%s" cidr_netmask="%s" nic="%s"' % \ + (config['vip'], config['vip_cidr'], config['vip_iface']) + resource_params = { + 'res_cinder_vip': vip_params, + 'res_cinder_haproxy': 'op monitor interval="5s"' + } + init_services = { + 'res_cinder_haproxy': 'haproxy' + } + clones = { + 'cl_cinder_haproxy': 'res_cinder_haproxy' + } + relation_set(init_services=init_services, + corosync_bindiface=config['ha-bindiface'], + corosync_mcastport=config['ha-mcastport'], + resources=resources, + resource_params=resource_params, + clones=clones) + + +@hooks.hook('ha-relation-changed') +def ha_changed(): + clustered = relation_get('clustered') + if not clustered or clustered in [None, 'None', '']: + juju_log('ha_changed: hacluster subordinate not fully clustered.') + return + if not is_leader(CLUSTER_RES): + juju_log('ha_changed: hacluster complete but we are not leader.') + return + juju_log('Cluster configured, notifying other services and updating ' + 'keystone endpoint configuration') + for rid in relation_ids('identity-service'): + identity_joined(rid=rid) + + +@hooks.hook('image-service-relation-changed') +@restart_on_change(restart_map()) +def image_service_changed(): + CONFIGS.write(CINDER_CONF) + + +@hooks.hook('amqp-relation-broken', + 'ceph-relation-broken', + 'identity-service-relation-broken', + 'image-service-relation-broken', + 'shared-db-relation-broken', + 'pgsql-db-relation-broken') +@restart_on_change(restart_map(), stopstart=True) +def relation_broken(): + CONFIGS.write_all() + + +def configure_https(): + '''Enables SSL API Apache config if appropriate and kicks identity-service + with any required api updates. + ''' + # need to write all to ensure changes to the entire request pipeline + # propagate (c-api, haprxy, apache) + CONFIGS.write_all() + if 'https' in CONFIGS.complete_contexts(): + cmd = ['a2ensite', 'openstack_https_frontend'] + check_call(cmd) + else: + cmd = ['a2dissite', 'openstack_https_frontend'] + check_call(cmd) + + for rid in relation_ids('identity-service'): + identity_joined(rid=rid) + + +@hooks.hook('upgrade-charm') +def upgrade_charm(): + for rel_id in relation_ids('amqp'): + amqp_joined(relation_id=rel_id) + + +if __name__ == '__main__': + try: + hooks.execute(sys.argv) + except UnregisteredHookError as e: + juju_log('Unknown hook {} - skipping.'.format(e)) diff --git a/metadata.yaml b/metadata.yaml index 132f0a15..2aba29b6 100644 --- a/metadata.yaml +++ b/metadata.yaml @@ -11,6 +11,8 @@ provides: requires: shared-db: interface: mysql-shared + pgsql-db: + interface: pgsql amqp: interface: rabbitmq identity-service: diff --git a/revision b/revision index c8b255fc..7296f257 100644 --- a/revision +++ b/revision @@ -1 +1 @@ -135 +136 diff --git a/templates/cinder.conf b/templates/cinder.conf index 22cfa890..21e7a217 100644 --- a/templates/cinder.conf +++ b/templates/cinder.conf @@ -16,7 +16,7 @@ state_path = /var/lib/cinder lock_path = /var/lock/cinder volumes_dir = /var/lib/cinder/volumes {% if database_host -%} -sql_connection = mysql://{{ database_user }}:{{ database_password }}@{{ database_host }}/{{ database }} +sql_connection = {{ database_type }}://{{ database_user }}:{{ database_password }}@{{ database_host }}/{{ database }} {% endif -%} {% if rabbitmq_host -%} notification_driver = cinder.openstack.common.notifier.rabbit_notifier diff --git a/templates/grizzly/cinder.conf b/templates/grizzly/cinder.conf index 18b09da4..e303aba3 100644 --- a/templates/grizzly/cinder.conf +++ b/templates/grizzly/cinder.conf @@ -16,7 +16,7 @@ state_path = /var/lib/cinder lock_path = /var/lock/cinder volumes_dir = /var/lib/cinder/volumes {% if database_host -%} -sql_connection = mysql://{{ database_user }}:{{ database_password }}@{{ database_host }}/{{ database }} +sql_connection = {{ database_type }}://{{ database_user }}:{{ database_password }}@{{ database_host }}/{{ database }} {% endif -%} {% if rabbitmq_host or rabbitmq_hosts -%} notification_driver = cinder.openstack.common.notifier.rabbit_notifier diff --git a/templates/icehouse/cinder.conf b/templates/icehouse/cinder.conf index 17c95687..85f7198f 100644 --- a/templates/icehouse/cinder.conf +++ b/templates/icehouse/cinder.conf @@ -62,5 +62,5 @@ glance_api_version = {{ glance_api_version }} {% if database_host -%} [database] -connection = mysql://{{ database_user }}:{{ database_password }}@{{ database_host }}/{{ database }} +connection = {{ database_type }}://{{ database_user }}:{{ database_password }}@{{ database_host }}/{{ database }} {% endif -%}