From 41db63fe691ea178dd07e33c76cfcd118bb47670 Mon Sep 17 00:00:00 2001 From: James Page Date: Tue, 12 Mar 2013 11:17:32 +0000 Subject: [PATCH] Refactoring to split up utils, use common lib --- config.yaml | 16 +- hooks/lib/apache_utils.py | 197 ++++++ hooks/lib/cluster_utils.py | 127 ++++ hooks/lib/haproxy_utils.py | 52 ++ hooks/lib/openstack_common.py | 42 +- hooks/lib/utils.py | 260 ++++++++ hooks/swift_hooks.py | 19 +- hooks/swift_utils.py | 83 +-- hooks/utils.py | 578 ------------------ .../templates => templates}/apache2_site.tmpl | 0 .../essex/memcached.conf | 0 .../essex/proxy-server.conf | 0 .../templates => templates}/essex/swift-rings | 0 .../templates => templates}/essex/swift.conf | 0 .../folsom/memcached.conf | 0 .../folsom/proxy-server.conf | 0 .../folsom/swift-rings | 0 .../templates => templates}/folsom/swift.conf | 0 .../grizzly/memcached.conf | 0 .../grizzly/proxy-server.conf | 0 .../grizzly/swift-rings | 0 .../grizzly/swift.conf | 0 {hooks/templates => templates}/haproxy.cfg | 0 23 files changed, 720 insertions(+), 654 deletions(-) create mode 100644 hooks/lib/apache_utils.py create mode 100644 hooks/lib/cluster_utils.py create mode 100644 hooks/lib/haproxy_utils.py create mode 100644 hooks/lib/utils.py delete mode 100644 hooks/utils.py rename {hooks/templates => templates}/apache2_site.tmpl (100%) rename {hooks/templates => templates}/essex/memcached.conf (100%) rename {hooks/templates => templates}/essex/proxy-server.conf (100%) rename {hooks/templates => templates}/essex/swift-rings (100%) rename {hooks/templates => templates}/essex/swift.conf (100%) rename {hooks/templates => templates}/folsom/memcached.conf (100%) rename {hooks/templates => templates}/folsom/proxy-server.conf (100%) rename {hooks/templates => templates}/folsom/swift-rings (100%) rename {hooks/templates => templates}/folsom/swift.conf (100%) rename {hooks/templates => templates}/grizzly/memcached.conf (100%) rename {hooks/templates => templates}/grizzly/proxy-server.conf (100%) rename {hooks/templates => templates}/grizzly/swift-rings (100%) rename {hooks/templates => templates}/grizzly/swift.conf (100%) rename {hooks/templates => templates}/haproxy.cfg (100%) diff --git a/config.yaml b/config.yaml index c8635a5..6b47d2f 100644 --- a/config.yaml +++ b/config.yaml @@ -50,13 +50,19 @@ options: ssl_cert: type: string description: | - SSL certificate to install and use for API ports. Setting this value - and ssl_key will enable reverse proxying, point Swifts's entry in the - Keystone catalog to use https, and override any certficiate and key - issued by Keystone (if it is configured to do so). + Base64 encoded SSL certificate to install and use for API ports. + . + juju set swift-proxy ssl_cert="$(cat cert | base64)" \ + ssl_key="$(cat key | base64)" + . + Setting this value (and ssl_key) will enable reverse proxying, point + Swifts's entry in the Keystone catalog to use https, and override + any certficiate and key issued by Keystone (if it is configured to + do so). ssl_key: type: string - description: SSL key to use with certificate specified as ssl_cert. + description: | + Base64 encoded SSL key to use with certificate specified as ssl_cert. # Locally generated CA Cert info (only use without keystone) # These options are deprecated and will be removed sometime use-https: diff --git a/hooks/lib/apache_utils.py b/hooks/lib/apache_utils.py new file mode 100644 index 0000000..504cba5 --- /dev/null +++ b/hooks/lib/apache_utils.py @@ -0,0 +1,197 @@ +# +# Copyright 2012 Canonical Ltd. +# +# Authors: +# James Page +# + +from lib.utils import ( + relation_ids, + relation_list, + relation_get, + render_template, + juju_log, + config_get, + install, + get_host_ip, + restart + ) +from lib.cluster_utils import https + +import os +import subprocess +from base64 import b64decode, b64encode + +APACHE_SITE_DIR = "/etc/apache2/sites-available" +SITE_TEMPLATE = "apache2_site.tmpl" +RELOAD_CHECK = "To activate the new configuration" + + +def enable_https(port_maps, namespace): + ''' + For a given number of port mappings, configures apache2 + HTTPs local reverse proxying using certficates and keys provided in + either configuration data (preferred) or relation data. Assumes ports + are not in use (calling charm should ensure that). + + port_maps: dict: external to internal port mappings + namespace: str: name of charm + ''' + juju_log('INFO', "Enabling HTTPS for port mappings: {}".format(port_maps)) + http_restart = False + # allow overriding of keystone provided certs with those set manually + # in config. + cert = config_get('ssl_cert') + key = config_get('ssl_key') + ca_cert = None + if not (cert and key): + juju_log('INFO', + "Inspecting identity-service relations for SSL certificate.") + cert = key = ca_cert = None + for r_id in relation_ids('identity-service'): + for unit in relation_list(r_id): + if not cert: + cert = relation_get('ssl_cert', rid=r_id, unit=unit) + if not key: + key = relation_get('ssl_key', rid=r_id, unit=unit) + if not ca_cert: + ca_cert = relation_get('ca_cert', rid=r_id, unit=unit) + if (not (cert and key and ca_cert) and + config_get('use-https')): + juju_log('INFO', + "Using self-signed SSL certificate.") + (cert, key) = generate_cert() + else: + juju_log('INFO', + "Using SSL certificate provided in service config.") + + if cert: + cert = b64decode(cert) + if key: + key = b64decode(key) + if ca_cert: + ca_cert = b64decode(ca_cert) + # TODO: Implement check tosee if certs have changed + + if not cert and not key: + juju_log('ERROR', + "Expected but could not find SSL certificate data, not " + "configuring HTTPS!") + return False + + install('apache2') + if RELOAD_CHECK in subprocess.check_output(['a2enmod', 'ssl', + 'proxy', 'proxy_http']): + http_restart = True + + ssl_dir = os.path.join('/etc/apache2/ssl', namespace) + if not os.path.exists(ssl_dir): + os.makedirs(ssl_dir) + with open(os.path.join(ssl_dir, 'cert'), 'w') as fcert: + fcert.write(cert) + with open(os.path.join(ssl_dir, 'key'), 'w') as fkey: + fkey.write(key) + os.chmod(os.path.join(ssl_dir, 'key'), 0600) + if ca_cert: + with open('/usr/local/share/ca-certificates/keystone_juju_ca_cert.crt', + 'w') as crt: + crt.write(ca_cert) + subprocess.check_call(['update-ca-certificates', '--fresh']) + + sites_dir = '/etc/apache2/sites-available' + for ext_port, int_port in port_maps.items(): + juju_log('INFO', + 'Creating apache2 reverse proxy vhost' + ' for {}:{}'.format(ext_port, + int_port)) + site = "{}_{}".format(namespace, ext_port) + site_path = os.path.join(sites_dir, site) + with open(site_path, 'w') as fsite: + context = { + "ext": ext_port, + "int": int_port, + "namespace": namespace, + "private_address": get_host_ip() + } + fsite.write(render_template(SITE_TEMPLATE, + context)) + + if RELOAD_CHECK in subprocess.check_output(['a2ensite', site]): + http_restart = True + + if http_restart: + restart('apache2') + + return True + + +def disable_https(port_maps, namespace): + ''' + Ensure HTTPS reverse proxying is disables for given port mappings + + port_maps: dict: of ext -> int port mappings + namespace: str: name of chamr + ''' + juju_log('INFO', 'Ensuring HTTPS disabled for {}'.format(port_maps)) + + if (not os.path.exists('/etc/apache2') or + not os.path.exists(os.path.join('/etc/apache2/ssl', namespace))): + return + + http_restart = False + for ext_port in port_maps.keys(): + if os.path.exists(os.path.join(APACHE_SITE_DIR, + "{}_{}".format(namespace, + ext_port))): + juju_log('INFO', + "Disabling HTTPS reverse proxy" + " for {} {}.".format(namespace, + ext_port)) + if (RELOAD_CHECK in + subprocess.check_output(['a2dissite', + '{}_{}'.format(namespace, + ext_port)])): + http_restart = True + + if http_restart: + restart(['apache2']) + + +def setup_https(port_maps, namespace): + ''' + Ensures HTTPS is either enabled or disabled for given port + mapping. + + port_maps: dict: of ext -> int port mappings + namespace: str: name of charm + ''' + if not https: + disable_https(port_maps, namespace) + else: + enable_https(port_maps, namespace) + + +def generate_cert(): + ''' + Generates a self signed certificate and key using the + provided charm configuration data. + + returns: tuple of (cert, key) + ''' + CERT = '/etc/swift/ssl.cert' + KEY = '/etc/swift/ssl.key' + if (not os.path.exists(CERT) and + not os.path.exists(KEY)): + subj = '/C=%s/ST=%s/L=%s/CN=%s' %\ + (config_get('country'), config_get('state'), + config_get('locale'), config_get('common-name')) + cmd = ['openssl', 'req', '-new', '-x509', '-nodes', + '-out', CERT, '-keyout', KEY, + '-subj', subj] + subprocess.check_call(cmd) + # Slurp as base64 encoded - makes handling easier up the stack + with open(CERT, 'r') as cfile: + ssl_cert = b64encode(cfile.read()) + with open(KEY, 'r') as kfile: + ssl_key = b64encode(kfile.read()) + return (ssl_cert, ssl_key) diff --git a/hooks/lib/cluster_utils.py b/hooks/lib/cluster_utils.py new file mode 100644 index 0000000..1a6a753 --- /dev/null +++ b/hooks/lib/cluster_utils.py @@ -0,0 +1,127 @@ +# +# Copyright 2012 Canonical Ltd. +# +# Authors: +# James Page +# + +from lib.utils import ( + juju_log, + relation_ids, + relation_list, + relation_get, + get_unit_hostname, + config_get + ) +import subprocess +import os + + +def is_clustered(): + for r_id in (relation_ids('ha') or []): + for unit in (relation_list(r_id) or []): + clustered = relation_get('clustered', + rid=r_id, + unit=unit) + if clustered: + return True + return False + + +def is_leader(resource): + cmd = [ + "crm", "resource", + "show", resource + ] + try: + status = subprocess.check_output(cmd) + except subprocess.CalledProcessError: + return False + else: + if get_unit_hostname() in status: + return True + else: + return False + + +def peer_units(): + peers = [] + for r_id in (relation_ids('cluster') or []): + for unit in (relation_list(r_id) or []): + peers.append(unit) + return peers + + +def oldest_peer(peers): + local_unit_no = os.getenv('JUJU_UNIT_NAME').split('/')[1] + for peer in peers: + remote_unit_no = peer.split('/')[1] + if remote_unit_no < local_unit_no: + return False + return True + + +def eligible_leader(resource): + if is_clustered(): + if not is_leader(resource): + juju_log('INFO', 'Deferring action to CRM leader.') + return False + else: + peers = peer_units() + if peers and not oldest_peer(peers): + juju_log('INFO', 'Deferring action to oldest service unit.') + return False + return True + + +def https(): + ''' + Determines whether enough data has been provided in configuration + or relation data to configure HTTPS + . + returns: boolean + ''' + if config_get('use-https'): + return True + if config_get('ssl_cert') and config_get('ssl_key'): + return True + for r_id in relation_ids('identity-service'): + for unit in relation_list(r_id): + if (relation_get('https_keystone', rid=r_id, unit=unit) and + relation_get('ssl_cert', rid=r_id, unit=unit) and + relation_get('ssl_key', rid=r_id, unit=unit) and + relation_get('ca_cert', rid=r_id, unit=unit)): + return True + return False + + +def determine_api_port(public_port): + ''' + Determine correct API server listening port based on + existence of HTTPS reverse proxy and/or haproxy. + + public_port: int: standard public port for given service + + returns: int: the correct listening port for the API service + ''' + i = 0 + if len(peer_units()) > 0 or is_clustered(): + i += 1 + if https(): + i += 1 + return public_port - (i * 10) + + +def determine_haproxy_port(public_port): + ''' + Description: Determine correct proxy listening port based on public IP + + existence of HTTPS reverse proxy. + + public_port: int: standard public port for given service + + returns: int: the correct listening port for the HAProxy service + ''' + i = 0 + if https(): + i += 1 + return public_port - (i * 10) diff --git a/hooks/lib/haproxy_utils.py b/hooks/lib/haproxy_utils.py new file mode 100644 index 0000000..3236628 --- /dev/null +++ b/hooks/lib/haproxy_utils.py @@ -0,0 +1,52 @@ +# +# Copyright 2012 Canonical Ltd. +# +# Authors: +# James Page +# + +from lib.utils import ( + relation_ids, + relation_list, + relation_get, + unit_get, + reload, + render_template + ) +import os + +HAPROXY_CONF = '/etc/haproxy/haproxy.cfg' +HAPROXY_DEFAULT = '/etc/default/haproxy' + + +def configure_haproxy(service_ports): + ''' + Configure HAProxy based on the current peers in the service + cluster using the provided port map: + + "swift": [ 8080, 8070 ] + + HAproxy will also be reloaded/started if required + + service_ports: dict: dict of lists of [ frontend, backend ] + ''' + cluster_hosts = {} + cluster_hosts[os.getenv('JUJU_UNIT_NAME').replace('/', '-')] = \ + unit_get('private-address') + for r_id in relation_ids('cluster'): + for unit in relation_list(r_id): + cluster_hosts[unit.replace('/', '-')] = \ + relation_get(attribute='private-address', + rid=r_id, + unit=unit) + context = { + 'units': cluster_hosts, + 'service_ports': service_ports + } + with open(HAPROXY_CONF, 'w') as f: + f.write(render_template(os.path.basename(HAPROXY_CONF), + context)) + with open(HAPROXY_DEFAULT, 'w') as f: + f.write('ENABLED=1') + + reload('haproxy') diff --git a/hooks/lib/openstack_common.py b/hooks/lib/openstack_common.py index dc01c78..321443c 100644 --- a/hooks/lib/openstack_common.py +++ b/hooks/lib/openstack_common.py @@ -12,7 +12,7 @@ ubuntu_openstack_release = { 'oneiric': 'diablo', 'precise': 'essex', 'quantal': 'folsom', - 'raring' : 'grizzly' + 'raring': 'grizzly' } @@ -20,7 +20,8 @@ openstack_codenames = { '2011.2': 'diablo', '2012.1': 'essex', '2012.2': 'folsom', - '2013.1': 'grizzly' + '2013.1': 'grizzly', + '2013.2': 'havana' } # The ugly duckling @@ -32,6 +33,7 @@ swift_codenames = { '1.7.7': 'grizzly' } + def juju_log(msg): subprocess.check_call(['juju-log', msg]) @@ -76,6 +78,7 @@ def get_os_codename_install_source(src): if v in src: return v + def get_os_codename_version(vers): '''Determine OpenStack codename from version number.''' try: @@ -115,7 +118,7 @@ def get_os_codename_package(pkg): return clean vers = None - for l in output.split('\n'): + for l in str(output).split('\n'): if l.startswith('ii'): l = _clean(l) if l[1] == pkg: @@ -153,16 +156,17 @@ def get_os_version_package(pkg): e = "Could not determine OpenStack version for package: %s" % pkg error_out(e) + def configure_installation_source(rel): '''Configure apt installation source.''' - def _import_key(id): + def _import_key(keyid): cmd = "apt-key adv --keyserver keyserver.ubuntu.com " \ - "--recv-keys %s" % id + "--recv-keys %s" % keyid try: subprocess.check_call(cmd.split(' ')) - except: - error_out("Error importing repo key %s" % id) + except subprocess.CalledProcessError: + error_out("Error importing repo key %s" % keyid) if rel == 'distro': return @@ -171,7 +175,7 @@ def configure_installation_source(rel): subprocess.check_call(["add-apt-repository", "-y", src]) elif rel[:3] == "deb": l = len(rel.split('|')) - if l == 2: + if l == 2: src, key = rel.split('|') juju_log("Importing PPA key from keyserver for %s" % src) _import_key(key) @@ -225,26 +229,6 @@ def configure_installation_source(rel): else: error_out("Invalid openstack-release specified: %s" % rel) -HAPROXY_CONF = '/etc/haproxy/haproxy.cfg' -HAPROXY_DEFAULT = '/etc/default/haproxy' - -def configure_haproxy(units, service_ports, template_dir=None): - template_dir = template_dir or 'templates' - import jinja2 - context = { - 'units': units, - 'service_ports': service_ports - } - templates = jinja2.Environment( - loader=jinja2.FileSystemLoader(template_dir) - ) - template = templates.get_template( - os.path.basename(HAPROXY_CONF) - ) - with open(HAPROXY_CONF, 'w') as f: - f.write(template.render(context)) - with open(HAPROXY_DEFAULT, 'w') as f: - f.write('ENABLED=1') def save_script_rc(script_path="scripts/scriptrc", **env_vars): """ @@ -255,7 +239,7 @@ def save_script_rc(script_path="scripts/scriptrc", **env_vars): service changes. """ unit_name = os.getenv('JUJU_UNIT_NAME').replace('/', '-') - juju_rc_path="/var/lib/juju/units/%s/charm/%s" % (unit_name, script_path) + juju_rc_path = "/var/lib/juju/units/%s/charm/%s" % (unit_name, script_path) with open(juju_rc_path, 'wb') as rc_script: rc_script.write( "#!/bin/bash\n") diff --git a/hooks/lib/utils.py b/hooks/lib/utils.py new file mode 100644 index 0000000..7225daa --- /dev/null +++ b/hooks/lib/utils.py @@ -0,0 +1,260 @@ + +# +# Copyright 2012 Canonical Ltd. +# +# Authors: +# James Page +# Paul Collins +# + +import json +import os +import subprocess +import socket +import sys + + +def do_hooks(hooks): + hook = os.path.basename(sys.argv[0]) + + try: + hook_func = hooks[hook] + except KeyError: + juju_log('INFO', + "This charm doesn't know how to handle '{}'.".format(hook)) + else: + hook_func() + + +def install(*pkgs): + cmd = [ + 'apt-get', + '-y', + 'install' + ] + for pkg in pkgs: + cmd.append(pkg) + subprocess.check_call(cmd) + +TEMPLATES_DIR = 'templates' + +try: + import jinja2 +except ImportError: + install('python-jinja2') + import jinja2 + +try: + import dns.resolver +except ImportError: + install('python-dnspython') + import dns.resolver + + +def render_template(template_name, context, template_dir=TEMPLATES_DIR): + templates = jinja2.Environment( + loader=jinja2.FileSystemLoader(template_dir) + ) + template = templates.get_template(template_name) + return template.render(context) + +CLOUD_ARCHIVE = \ +""" # Ubuntu Cloud Archive +deb http://ubuntu-cloud.archive.canonical.com/ubuntu {} main +""" + +CLOUD_ARCHIVE_POCKETS = { + 'folsom': 'precise-updates/folsom', + 'folsom/updates': 'precise-updates/folsom', + 'folsom/proposed': 'precise-proposed/folsom', + 'grizzly': 'precise-updates/grizzly', + 'grizzly/updates': 'precise-updates/grizzly', + 'grizzly/proposed': 'precise-proposed/grizzly' + } + + +def configure_source(): + source = str(config_get('openstack-origin')) + if not source: + return + if source.startswith('ppa:'): + cmd = [ + 'add-apt-repository', + source + ] + subprocess.check_call(cmd) + if source.startswith('cloud:'): + install('ubuntu-cloud-keyring') + pocket = source.split(':')[1] + with open('/etc/apt/sources.list.d/cloud-archive.list', 'w') as apt: + apt.write(CLOUD_ARCHIVE.format(CLOUD_ARCHIVE_POCKETS[pocket])) + if source.startswith('deb'): + l = len(source.split('|')) + if l == 2: + (apt_line, key) = source.split('|') + cmd = [ + 'apt-key', + 'adv', '--keyserver keyserver.ubuntu.com', + '--recv-keys', key + ] + subprocess.check_call(cmd) + elif l == 1: + apt_line = source + + with open('/etc/apt/sources.list.d/quantum.list', 'w') as apt: + apt.write(apt_line + "\n") + cmd = [ + 'apt-get', + 'update' + ] + subprocess.check_call(cmd) + +# Protocols +TCP = 'TCP' +UDP = 'UDP' + + +def expose(port, protocol='TCP'): + cmd = [ + 'open-port', + '{}/{}'.format(port, protocol) + ] + subprocess.check_call(cmd) + + +def juju_log(severity, message): + cmd = [ + 'juju-log', + '--log-level', severity, + message + ] + subprocess.check_call(cmd) + + +def relation_ids(relation): + cmd = [ + 'relation-ids', + relation + ] + result = str(subprocess.check_output(cmd)).split() + if result == "": + return None + else: + return result + + +def relation_list(rid): + cmd = [ + 'relation-list', + '-r', rid, + ] + result = str(subprocess.check_output(cmd)).split() + if result == "": + return None + else: + return result + + +def relation_get(attribute, unit=None, rid=None): + cmd = [ + 'relation-get', + ] + if rid: + cmd.append('-r') + cmd.append(rid) + cmd.append(attribute) + if unit: + cmd.append(unit) + value = subprocess.check_output(cmd).strip() # IGNORE:E1103 + if value == "": + return None + else: + return value + + +def relation_set(**kwargs): + cmd = [ + 'relation-set' + ] + args = [] + for k, v in kwargs.items(): + if k == 'rid': + if v: + cmd.append('-r') + cmd.append(v) + else: + args.append('{}={}'.format(k, v)) + cmd += args + subprocess.check_call(cmd) + + +def unit_get(attribute): + cmd = [ + 'unit-get', + attribute + ] + value = subprocess.check_output(cmd).strip() # IGNORE:E1103 + if value == "": + return None + else: + return value + + +def config_get(attribute): + cmd = [ + 'config-get', + '--format', + 'json', + ] + out = subprocess.check_output(cmd).strip() # IGNORE:E1103 + cfg = json.loads(out) + + try: + return cfg[attribute] + except KeyError: + return None + + +def get_unit_hostname(): + return socket.gethostname() + + +def get_host_ip(hostname=unit_get('private-address')): + try: + # Test to see if already an IPv4 address + socket.inet_aton(hostname) + return hostname + except socket.error: + answers = dns.resolver.query(hostname, 'A') + if answers: + return answers[0].address + return None + + +def _svc_control(service, action): + subprocess.check_call(['service', service, action]) + + +def restart(*services): + for service in services: + _svc_control(service, 'restart') + + +def stop(*services): + for service in services: + _svc_control(service, 'stop') + + +def start(*services): + for service in services: + _svc_control(service, 'start') + + +def reload(*services): + for service in services: + try: + _svc_control(service, 'reload') + except subprocess.CalledProcessError: + # Reload failed - either service does not support reload + # or it was not running - restart will fixup most things + _svc_control(service, 'restart') diff --git a/hooks/swift_hooks.py b/hooks/swift_hooks.py index e59e422..3ff52f0 100755 --- a/hooks/swift_hooks.py +++ b/hooks/swift_hooks.py @@ -1,13 +1,14 @@ #!/usr/bin/python import os -import utils import sys import shutil import uuid from subprocess import check_call import lib.openstack_common as openstack +import lib.utils as utils +import lib.cluster_utils as cluster import swift_utils as swift extra_pkgs = [ @@ -15,6 +16,7 @@ extra_pkgs = [ "python-jinja2" ] + def install(): src = utils.config_get('openstack-origin') if src != 'distro': @@ -40,7 +42,7 @@ def install(): swift.write_proxy_config() # memcached.conf - ctxt = { 'proxy_ip': utils.get_host_ip() } + ctxt = {'proxy_ip': utils.get_host_ip()} with open(swift.MEMCACHED_CONF, 'w') as conf: conf.write(swift.render_config(swift.MEMCACHED_CONF, ctxt)) @@ -61,14 +63,14 @@ def install(): def keystone_joined(relid=None): - if not utils.eligible_leader(): + if not cluster.eligible_leader(swift.SWIFT_HA_RES): return - if utils.is_clustered(): + if cluster.is_clustered(): hostname = utils.config_get('vip') else: hostname = utils.unit_get('private-address') port = utils.config_get('bind-port') - if utils.https(): + if cluster.https(): proto = 'https' else: proto = 'http' @@ -105,7 +107,7 @@ def balance_rings(): shutil.copyfile(os.path.join(swift.SWIFT_CONF_DIR, f), os.path.join(swift.WWW_DIR, f)) - if utils.eligible_leader(): + if cluster.eligible_leader(swift.SWIFT_HA_RES): msg = 'Broadcasting notification to all storage nodes that new '\ 'ring is ready for consumption.' utils.juju_log('INFO', msg) @@ -119,6 +121,7 @@ def balance_rings(): swift.proxy_control('restart') + def storage_changed(): zone = swift.get_zone(utils.config_get('zone-assignment')) node_settings = { @@ -149,9 +152,11 @@ def storage_changed(): if swift.should_balance([r for r in swift.SWIFT_RINGS.itervalues()]): balance_rings() + def storage_broken(): swift.write_apache_config() + def config_changed(): relids = utils.relation_ids('identity-service') if relids: @@ -167,7 +172,7 @@ def cluster_changed(): def ha_relation_changed(): clustered = utils.relation_get('clustered') - if clustered and utils.is_leader(): + if clustered and cluster.is_leader(swift.SWIFT_HA_RES): utils.juju_log('INFO', 'Cluster configured, notifying other services and' 'updating keystone endpoint configuration') diff --git a/hooks/swift_utils.py b/hooks/swift_utils.py index 108dd05..4c72192 100644 --- a/hooks/swift_utils.py +++ b/hooks/swift_utils.py @@ -2,11 +2,14 @@ import os import pwd import subprocess import lib.openstack_common as openstack -import utils +import lib.utils as utils +import lib.haproxy_utils as haproxy +import lib.apache_utils as apache +import lib.cluster_utils as cluster import sys # Various config files that are managed via templating. -SWIFT_HASH_FILE='/var/lib/juju/swift-hash-path.conf' +SWIFT_HASH_FILE = '/var/lib/juju/swift-hash-path.conf' SWIFT_CONF = '/etc/swift/swift.conf' SWIFT_PROXY_CONF = '/etc/swift/proxy-server.conf' SWIFT_CONF_DIR = os.path.dirname(SWIFT_CONF) @@ -33,9 +36,12 @@ BASE_PACKAGES = [ 'python-keystone', ] +SWIFT_HA_RES = 'res_swift_vip' + # Folsom-specific packages FOLSOM_PACKAGES = BASE_PACKAGES + ['swift-plugin-s3'] + def proxy_control(action): '''utility to work around swift-init's bad RCs.''' def _cmd(action): @@ -50,8 +56,9 @@ def proxy_control(action): elif status == 0: return subprocess.check_call(_cmd('stop')) - # the proxy will not start unless there are balanced rings, gzip'd in /etc/swift - missing=False + # the proxy will not start unless there are balanced rings + # gzip'd in /etc/swift + missing = False for k in SWIFT_RINGS.keys(): if not os.path.exists(os.path.join(SWIFT_CONF_DIR, '%s.ring.gz' % k)): missing = True @@ -70,8 +77,9 @@ def proxy_control(action): elif status == 1: return subprocess.check_call(_cmd('start')) + def swift_user(username='swift'): - user = pwd.getpwnam('swift') + user = pwd.getpwnam(username) return (user.pw_uid, user.pw_gid) @@ -153,11 +161,16 @@ def get_keystone_auth(): 'keystone_host': utils.relation_get('auth_host', unit, relid), 'auth_port': utils.relation_get('auth_port', unit, relid), - 'service_user': utils.relation_get('service_username', unit, relid), - 'service_password': utils.relation_get('service_password', unit, relid), - 'service_tenant': utils.relation_get('service_tenant', unit, relid), - 'service_port': utils.relation_get('service_port', unit, relid), - 'admin_token': utils.relation_get('admin_token', unit, relid), + 'service_user': utils.relation_get('service_username', + unit, relid), + 'service_password': utils.relation_get('service_password', + unit, relid), + 'service_tenant': utils.relation_get('service_tenant', + unit, relid), + 'service_port': utils.relation_get('service_port', + unit, relid), + 'admin_token': utils.relation_get('admin_token', + unit, relid), } if None not in ks_auth.itervalues(): return ks_auth @@ -179,7 +192,7 @@ def write_proxy_config(): ctxt = { 'proxy_ip': utils.get_host_ip(), - 'bind_port': utils.determine_api_port(bind_port), + 'bind_port': cluster.determine_api_port(bind_port), 'workers': workers, 'operator_roles': utils.config_get('operator-roles') } @@ -201,7 +214,7 @@ def write_proxy_config(): def _load_builder(path): # lifted straight from /usr/bin/swift-ring-builder - from swift.common.ring import RingBuilder, Ring + from swift.common.ring import RingBuilder import cPickle as pickle try: builder = pickle.load(open(path, 'rb')) @@ -210,10 +223,8 @@ def _load_builder(path): builder = RingBuilder(1, 1, 1) builder.copy_from(builder_dict) except ImportError: # Happens with really old builder pickles - modules['swift.ring_builder'] = \ - modules['swift.common.ring.builder'] builder = RingBuilder(1, 1, 1) - builder.copy_from(pickle.load(open(argv[1], 'rb'))) + builder.copy_from(pickle.load(open(path, 'rb'))) for dev in builder.devs: if dev and 'meta' not in dev: dev['meta'] = '' @@ -225,8 +236,6 @@ def _write_ring(ring, ring_path): pickle.dump(ring.to_dict(), open(ring_path, 'wb'), protocol=2) - - def ring_port(ring_path, node): '''determine correct port from relation settings for a given ring file.''' for name in ['account', 'object', 'container']: @@ -240,8 +249,8 @@ def initialize_ring(path, part_power, replicas, min_hours): ring = RingBuilder(part_power, replicas, min_hours) _write_ring(ring, path) + def exists_in_ring(ring_path, node): - from swift.common.ring import RingBuilder, Ring ring = _load_builder(ring_path).to_dict() node['port'] = ring_port(ring_path, node) @@ -258,7 +267,6 @@ def exists_in_ring(ring_path, node): def add_to_ring(ring_path, node): - from swift.common.ring import RingBuilder, Ring ring = _load_builder(ring_path) port = ring_port(ring_path, node) @@ -278,8 +286,9 @@ def add_to_ring(ring_path, node): } ring.add_dev(new_dev) _write_ring(ring, ring_path) - msg = 'Added new device to ring %s: %s' % (ring_path, - [k for k in new_dev.iteritems()]) + msg = 'Added new device to ring %s: %s' %\ + (ring_path, + [k for k in new_dev.iteritems()]) utils.juju_log('INFO', msg) @@ -324,7 +333,7 @@ def get_zone(assignment_policy): potential_zones.append(_get_zone(builder)) return set(potential_zones).pop() else: - utils.juju_log('Invalid zone assignment policy: %s' %\ + utils.juju_log('ERROR', 'Invalid zone assignment policy: %s' %\ assignment_policy) sys.exit(1) @@ -343,9 +352,10 @@ def balance_ring(ring_path): # swift-ring-builder returns 1 on WARNING (ring didn't require balance) return False else: - utils.juju_log('balance_ring: %s returned %s' % (cmd, rc)) + utils.juju_log('ERROR', 'balance_ring: %s returned %s' % (cmd, rc)) sys.exit(1) + def should_balance(rings): '''Based on zones vs min. replicas, determine whether or not the rings should be balanaced during initial configuration.''' @@ -371,35 +381,38 @@ def write_apache_config(): host = utils.relation_get('private-address', unit, relid) allowed_hosts.append(utils.get_host_ip(host)) - ctxt = { 'www_dir': WWW_DIR, 'allowed_hosts': allowed_hosts } + ctxt = { + 'www_dir': WWW_DIR, + 'allowed_hosts': allowed_hosts + } with open(APACHE_CONF, 'w') as conf: conf.write(render_config(APACHE_CONF, ctxt)) - subprocess.check_call(['service', 'apache2', 'reload']) + utils.reload('apache2') def configure_haproxy(): api_port = utils.config_get('bind-port') service_ports = { "swift": [ - utils.determine_haproxy_port(api_port), - utils.determine_api_port(api_port) + cluster.determine_haproxy_port(api_port), + cluster.determine_api_port(api_port) ] } write_proxy_config() - utils.configure_haproxy(service_ports) + haproxy.configure_haproxy(service_ports) def configure_https(): - if utils.https(): + if cluster.https(): api_port = utils.config_get('bind-port') - if (len(utils.peer_units()) > 0 or - utils.is_clustered()): - target_port = utils.determine_haproxy_port(api_port) + if (len(cluster.peer_units()) > 0 or + cluster.is_clustered()): + target_port = cluster.determine_haproxy_port(api_port) configure_haproxy() else: - target_port = utils.determine_api_port(api_port) + target_port = cluster.determine_api_port(api_port) write_proxy_config() - utils.setup_https(namespace="swift", - port_maps={api_port: target_port}) + apache.setup_https(namespace="swift", + port_maps={api_port: target_port}) else: return False diff --git a/hooks/utils.py b/hooks/utils.py deleted file mode 100644 index e8bfb3b..0000000 --- a/hooks/utils.py +++ /dev/null @@ -1,578 +0,0 @@ - -# -# Copyright 2012 Canonical Ltd. -# -# Authors: -# James Page -# Paul Collins -# - -import json -import os -import subprocess -import socket -import sys -import base64 -import tempfile - - -def do_hooks(hooks): - hook = os.path.basename(sys.argv[0]) - - try: - hook_func = hooks[hook] - except KeyError: - juju_log('INFO', - "This charm doesn't know how to handle '{}'.".format(hook)) - else: - hook_func() - - -def install(*pkgs): - cmd = [ - 'apt-get', - '-y', - 'install' - ] - for pkg in pkgs: - cmd.append(pkg) - subprocess.check_call(cmd) - -TEMPLATES_DIR = 'hooks/templates' - -try: - import jinja2 -except ImportError: - install('python-jinja2') - import jinja2 - -try: - import dns.resolver - import dns.ipv4 -except ImportError: - install('python-dnspython') - import dns.resolver - import dns.ipv4 - - -def render_template(template_name, context, template_dir=TEMPLATES_DIR): - templates = jinja2.Environment( - loader=jinja2.FileSystemLoader(template_dir) - ) - template = templates.get_template(template_name) - return template.render(context) - -CLOUD_ARCHIVE = \ -""" # Ubuntu Cloud Archive -deb http://ubuntu-cloud.archive.canonical.com/ubuntu {} main -""" - -CLOUD_ARCHIVE_POCKETS = { - 'folsom': 'precise-updates/folsom', - 'folsom/updates': 'precise-updates/folsom', - 'folsom/proposed': 'precise-proposed/folsom' - } - - -def configure_source(): - source = str(config_get('openstack-origin')) - if not source: - return - if source.startswith('ppa:'): - cmd = [ - 'add-apt-repository', - source - ] - subprocess.check_call(cmd) - if source.startswith('cloud:'): - install('ubuntu-cloud-keyring') - pocket = source.split(':')[1] - with open('/etc/apt/sources.list.d/cloud-archive.list', 'w') as apt: - apt.write(CLOUD_ARCHIVE.format(CLOUD_ARCHIVE_POCKETS[pocket])) - if source.startswith('deb'): - l = len(source.split('|')) - if l == 2: - (apt_line, key) = source.split('|') - cmd = [ - 'apt-key', - 'adv', '--keyserver keyserver.ubuntu.com', - '--recv-keys', key - ] - subprocess.check_call(cmd) - elif l == 1: - apt_line = source - - with open('/etc/apt/sources.list.d/quantum.list', 'w') as apt: - apt.write(apt_line + "\n") - cmd = [ - 'apt-get', - 'update' - ] - subprocess.check_call(cmd) - -# Protocols -TCP = 'TCP' -UDP = 'UDP' - - -def expose(port, protocol='TCP'): - cmd = [ - 'open-port', - '{}/{}'.format(port, protocol) - ] - subprocess.check_call(cmd) - - -def juju_log(severity, message): - cmd = [ - 'juju-log', - '--log-level', severity, - message - ] - subprocess.check_call(cmd) - - -def relation_ids(relation): - cmd = [ - 'relation-ids', - relation - ] - result = str(subprocess.check_output(cmd)).split() - if result == "": - return None - else: - return result - - -def relation_list(rid): - cmd = [ - 'relation-list', - '-r', rid, - ] - result = str(subprocess.check_output(cmd)).split() - if result == "": - return None - else: - return result - - -def relation_get(attribute, unit=None, rid=None): - cmd = [ - 'relation-get', - ] - if rid: - cmd.append('-r') - cmd.append(rid) - cmd.append(attribute) - if unit: - cmd.append(unit) - value = subprocess.check_output(cmd).strip() # IGNORE:E1103 - if value == "": - return None - else: - return value - - -def relation_set(**kwargs): - cmd = [ - 'relation-set' - ] - args = [] - for k, v in kwargs.items(): - if k == 'rid': - if v: - cmd.append('-r') - cmd.append(v) - else: - args.append('{}={}'.format(k, v)) - cmd += args - subprocess.check_call(cmd) - - -def unit_get(attribute): - cmd = [ - 'unit-get', - attribute - ] - value = subprocess.check_output(cmd).strip() # IGNORE:E1103 - if value == "": - return None - else: - return value - - -def config_get(attribute): - cmd = [ - 'config-get', - '--format', - 'json', - ] - out = subprocess.check_output(cmd).strip() # IGNORE:E1103 - cfg = json.loads(out) - - try: - return cfg[attribute] - except KeyError: - return None - - -def get_unit_hostname(): - return socket.gethostname() - - -def get_host_ip(hostname=unit_get('private-address')): - try: - # Test to see if already an IPv4 address - socket.inet_aton(hostname) - return hostname - except socket.error: - answers = dns.resolver.query(hostname, 'A') - if answers: - return answers[0].address - return None - - -def restart(*services): - for service in services: - subprocess.check_call(['service', service, 'restart']) - - -def stop(*services): - for service in services: - subprocess.check_call(['service', service, 'stop']) - - -def start(*services): - for service in services: - subprocess.check_call(['service', service, 'start']) - - -def reload(*services): - for service in services: - try: - subprocess.check_call(['service', service, 'reload']) - except subprocess.CalledProcessError: - # Reload failed - either service does not support reload - # or it was not running - restart will fixup most things - subprocess.check_call(['service', service, 'restart']) - - -def is_clustered(): - for r_id in (relation_ids('ha') or []): - for unit in (relation_list(r_id) or []): - clustered = relation_get('clustered', - rid=r_id, - unit=unit) - if clustered: - return True - return False - - -def is_leader(): - cmd = [ - "crm", "resource", - "show", "res_swift_vip" - ] - try: - status = subprocess.check_output(cmd) - except subprocess.CalledProcessError: - return False - else: - if get_unit_hostname() in status: - return True - else: - return False - - -def peer_units(): - peers = [] - for r_id in (relation_ids('cluster') or []): - for unit in (relation_list(r_id) or []): - peers.append(unit) - return peers - - -def oldest_peer(peers): - local_unit_no = os.getenv('JUJU_UNIT_NAME').split('/')[1] - for peer in peers: - remote_unit_no = peer.split('/')[1] - if remote_unit_no < local_unit_no: - return False - return True - - -def eligible_leader(): - if is_clustered(): - if not is_leader(): - juju_log('INFO', 'Deferring action to CRM leader.') - return False - else: - peers = peer_units() - if peers and not oldest_peer(peers): - juju_log('INFO', 'Deferring action to oldest service unit.') - return False - return True - - -def https(): - ''' - Determines whether enough data has been provided in configuration - or relation data to configure HTTPS - . - returns: boolean - ''' - if config_get('use-https'): - return True - if config_get('ssl_cert') and config_get('ssl_key'): - return True - for r_id in relation_ids('identity-service'): - for unit in relation_list(r_id): - if (relation_get('https_keystone', rid=r_id, unit=unit) and - relation_get('ssl_cert', rid=r_id, unit=unit) and - relation_get('ssl_key', rid=r_id, unit=unit) and - relation_get('ca_cert', rid=r_id, unit=unit)): - return True - return False - - -APACHE_SITE_DIR = "/etc/apache2/sites-available" -SITE_TEMPLATE = "apache2_site.tmpl" -RELOAD_CHECK = "To activate the new configuration" - - -def enable_https(port_maps, namespace): - ''' - For a given number of port mappings, configures apache2 - HTTPs local reverse proxying using certficates and keys provided in - either configuration data (preferred) or relation data. Assumes ports - are not in use (calling charm should ensure that). - - port_maps: dict: external to internal port mappings - namespace: str: name of charm - ''' - juju_log('INFO', "Enabling HTTPS for port mappings: {}".format(port_maps)) - http_restart = False - # allow overriding of keystone provided certs with those set manually - # in config. - cert = config_get('ssl_cert') - key = config_get('ssl_key') - ca_cert = None - if not (cert and key): - juju_log('INFO', - "Inspecting identity-service relations for SSL certificate.") - cert = key = ca_cert = None - for r_id in relation_ids('identity-service'): - for unit in relation_list(r_id): - if not cert: - cert = relation_get('ssl_cert', rid=r_id, unit=unit) - if not key: - key = relation_get('ssl_key', rid=r_id, unit=unit) - if not ca_cert: - ca_cert = relation_get('ca_cert', rid=r_id, unit=unit) - if (not (cert and key and ca_cert) and - config_get('use-https')): - juju_log('INFO', - "Using self-signed SSL certificate.") - (cert, key) = generate_cert() - else: - juju_log('INFO', - "Using SSL certificate provided in service config.") - - if cert: - cert = base64.b64decode(cert) - if key: - key = base64.b64decode(key) - if ca_cert: - ca_cert = base64.b64decode(ca_cert) - - if not cert and not key: - juju_log('ERROR', - "Expected but could not find SSL certificate data, not " - "configuring HTTPS!") - return False - - install('apache2') - if RELOAD_CHECK in subprocess.check_output(['a2enmod', 'ssl', - 'proxy', 'proxy_http']): - http_restart = True - - ssl_dir = os.path.join('/etc/apache2/ssl', namespace) - if not os.path.exists(ssl_dir): - os.makedirs(ssl_dir) - with open(os.path.join(ssl_dir, 'cert'), 'w') as fcert: - fcert.write(cert) - with open(os.path.join(ssl_dir, 'key'), 'w') as fkey: - fkey.write(key) - os.chmod(os.path.join(ssl_dir, 'key'), 0600) - if ca_cert: - with open('/usr/local/share/ca-certificates/keystone_juju_ca_cert.crt', - 'w') as crt: - crt.write(ca_cert) - subprocess.check_call(['update-ca-certificates', '--fresh']) - - sites_dir = '/etc/apache2/sites-available' - for ext_port, int_port in port_maps.items(): - juju_log('INFO', - 'Creating apache2 reverse proxy vhost' - ' for {}:{}'.format(ext_port, - int_port)) - site = "{}_{}".format(namespace, ext_port) - site_path = os.path.join(sites_dir, site) - with open(site_path, 'w') as fsite: - context = { - "ext": ext_port, - "int": int_port, - "namespace": namespace, - "private_address": get_host_ip() - } - fsite.write(render_template(SITE_TEMPLATE, - context)) - - if RELOAD_CHECK in subprocess.check_output(['a2ensite', site]): - http_restart = True - - if http_restart: - restart('apache2') - - return True - - -def disable_https(port_maps, namespace): - ''' - Ensure HTTPS reverse proxying is disables for given port mappings - - port_maps: dict: of ext -> int port mappings - namespace: str: name of chamr - ''' - juju_log('INFO', 'Ensuring HTTPS disabled for {}'.format(port_maps)) - - if (not os.path.exists('/etc/apache2') or - not os.path.exists(os.path.join('/etc/apache2/ssl', namespace))): - return - - http_restart = False - for ext_port in port_maps.keys(): - if os.path.exists(os.path.join(APACHE_SITE_DIR, - "{}_{}".format(namespace, - ext_port))): - juju_log('INFO', - "Disabling HTTPS reverse proxy" - " for {} {}.".format(namespace, - ext_port)) - if (RELOAD_CHECK in - subprocess.check_output(['a2dissite', - '{}_{}'.format(namespace, - ext_port)])): - http_restart = True - - if http_restart: - restart(['apache2']) - - -def setup_https(port_maps, namespace): - ''' - Ensures HTTPS is either enabled or disabled for given port - mapping. - - port_maps: dict: of ext -> int port mappings - namespace: str: name of charm - ''' - if not https: - disable_https(port_maps, namespace) - else: - enable_https(port_maps, namespace) - - -def generate_cert(): - ''' - Generates a self signed certificate and key using the - provided charm configuration data. - - returns: tuple of (cert, key) - ''' - CERT = '/etc/swift/ssl.cert' - KEY = '/etc/swift/ssl.key' - if (not os.path.exists(CERT) and - not os.path.exists(KEY)): - subj = '/C=%s/ST=%s/L=%s/CN=%s' %\ - (config_get('country'), config_get('state'), - config_get('locale'), config_get('common-name')) - cmd = ['openssl', 'req', '-new', '-x509', '-nodes', - '-out', CERT, '-keyout', KEY, - '-subj', subj] - subprocess.check_call(cmd) - # Slurp as base64 encoded - makes handling easier up the stack - with open(CERT, 'r') as cfile: - ssl_cert = base64.b64encode(cfile.read()) - with open(KEY, 'r') as kfile: - ssl_key = base64.b64encode(kfile.read()) - return (ssl_cert, ssl_key) - - -def determine_api_port(public_port): - ''' - Determine correct API server listening port based on - existence of HTTPS reverse proxy and/or haproxy. - - public_port: int: standard public port for given service - - returns: int: the correct listening port for the API service - ''' - i = 0 - if len(peer_units()) > 0 or is_clustered(): - i += 1 - if https(): - i += 1 - return public_port - (i * 10) - - -def determine_haproxy_port(public_port): - ''' - Description: Determine correct proxy listening port based on public IP + - existence of HTTPS reverse proxy. - - public_port: int: standard public port for given service - - returns: int: the correct listening port for the HAProxy service - ''' - i = 0 - if https(): - i += 1 - return public_port - (i * 10) - - -HAPROXY_CONF = '/etc/haproxy/haproxy.cfg' -HAPROXY_DEFAULT = '/etc/default/haproxy' - - -def configure_haproxy(service_ports): - ''' - Configure HAProxy based on the current peers in the service - cluster using the provided port map: - - "swift": [ 8080, 8070 ] - - HAproxy will also be reloaded/started if required - - service_ports: dict: dict of lists of [ frontend, backend ] - ''' - cluster_hosts = {} - cluster_hosts[os.getenv('JUJU_UNIT_NAME').replace('/', '-')] = \ - unit_get('private-address') - for r_id in relation_ids('cluster'): - for unit in relation_list(r_id): - cluster_hosts[unit.replace('/', '-')] = \ - relation_get(attribute='private-address', - rid=r_id, - unit=unit) - context = { - 'units': cluster_hosts, - 'service_ports': service_ports - } - with open(HAPROXY_CONF, 'w') as f: - f.write(render_template(os.path.basename(HAPROXY_CONF), - context)) - with open(HAPROXY_DEFAULT, 'w') as f: - f.write('ENABLED=1') - - reload('haproxy') diff --git a/hooks/templates/apache2_site.tmpl b/templates/apache2_site.tmpl similarity index 100% rename from hooks/templates/apache2_site.tmpl rename to templates/apache2_site.tmpl diff --git a/hooks/templates/essex/memcached.conf b/templates/essex/memcached.conf similarity index 100% rename from hooks/templates/essex/memcached.conf rename to templates/essex/memcached.conf diff --git a/hooks/templates/essex/proxy-server.conf b/templates/essex/proxy-server.conf similarity index 100% rename from hooks/templates/essex/proxy-server.conf rename to templates/essex/proxy-server.conf diff --git a/hooks/templates/essex/swift-rings b/templates/essex/swift-rings similarity index 100% rename from hooks/templates/essex/swift-rings rename to templates/essex/swift-rings diff --git a/hooks/templates/essex/swift.conf b/templates/essex/swift.conf similarity index 100% rename from hooks/templates/essex/swift.conf rename to templates/essex/swift.conf diff --git a/hooks/templates/folsom/memcached.conf b/templates/folsom/memcached.conf similarity index 100% rename from hooks/templates/folsom/memcached.conf rename to templates/folsom/memcached.conf diff --git a/hooks/templates/folsom/proxy-server.conf b/templates/folsom/proxy-server.conf similarity index 100% rename from hooks/templates/folsom/proxy-server.conf rename to templates/folsom/proxy-server.conf diff --git a/hooks/templates/folsom/swift-rings b/templates/folsom/swift-rings similarity index 100% rename from hooks/templates/folsom/swift-rings rename to templates/folsom/swift-rings diff --git a/hooks/templates/folsom/swift.conf b/templates/folsom/swift.conf similarity index 100% rename from hooks/templates/folsom/swift.conf rename to templates/folsom/swift.conf diff --git a/hooks/templates/grizzly/memcached.conf b/templates/grizzly/memcached.conf similarity index 100% rename from hooks/templates/grizzly/memcached.conf rename to templates/grizzly/memcached.conf diff --git a/hooks/templates/grizzly/proxy-server.conf b/templates/grizzly/proxy-server.conf similarity index 100% rename from hooks/templates/grizzly/proxy-server.conf rename to templates/grizzly/proxy-server.conf diff --git a/hooks/templates/grizzly/swift-rings b/templates/grizzly/swift-rings similarity index 100% rename from hooks/templates/grizzly/swift-rings rename to templates/grizzly/swift-rings diff --git a/hooks/templates/grizzly/swift.conf b/templates/grizzly/swift.conf similarity index 100% rename from hooks/templates/grizzly/swift.conf rename to templates/grizzly/swift.conf diff --git a/hooks/templates/haproxy.cfg b/templates/haproxy.cfg similarity index 100% rename from hooks/templates/haproxy.cfg rename to templates/haproxy.cfg