From b183a9d41fd5427ea23f69f60a6d9d0477afbb2f Mon Sep 17 00:00:00 2001 From: James Page Date: Mon, 8 Oct 2012 16:58:16 +0100 Subject: [PATCH] Minor tweak to copyright --- .bzrignore | 2 + README | 58 +++++++++++ TODO | 5 + config.yaml | 22 +++++ copyright | 9 ++ files/www/s3gw.fcgi | 2 + hooks/config-changed | 1 + hooks/gateway-relation-joined | 1 + hooks/hooks.py | 139 ++++++++++++++++++++++++++ hooks/install | 1 + hooks/mon-relation-changed | 1 + hooks/mon-relation-departed | 1 + hooks/start | 1 + hooks/stop | 1 + hooks/upgrade-charm | 1 + hooks/utils.py | 177 ++++++++++++++++++++++++++++++++++ metadata.yaml | 15 +++ revision | 1 + templates/ceph.conf | 9 ++ templates/rgw | 25 +++++ 20 files changed, 472 insertions(+) create mode 100644 .bzrignore create mode 100644 README create mode 100644 TODO create mode 100644 config.yaml create mode 100644 copyright create mode 100755 files/www/s3gw.fcgi create mode 120000 hooks/config-changed create mode 120000 hooks/gateway-relation-joined create mode 100755 hooks/hooks.py create mode 120000 hooks/install create mode 120000 hooks/mon-relation-changed create mode 120000 hooks/mon-relation-departed create mode 120000 hooks/start create mode 120000 hooks/stop create mode 120000 hooks/upgrade-charm create mode 100644 hooks/utils.py create mode 100644 metadata.yaml create mode 100644 revision create mode 100644 templates/ceph.conf create mode 100644 templates/rgw diff --git a/.bzrignore b/.bzrignore new file mode 100644 index 00000000..a9af2130 --- /dev/null +++ b/.bzrignore @@ -0,0 +1,2 @@ +.project +.pydevproject diff --git a/README b/README new file mode 100644 index 00000000..9bc2552d --- /dev/null +++ b/README @@ -0,0 +1,58 @@ +Overview +======== + +Ceph is a distributed storage and network file system designed to provide +excellent performance, reliability, and scalability. + +This charm deploys the RADOS Gateway, a S3 and Swift compatible HTTP gateway +for online object storage on-top of a ceph cluster. + +This charm only supports the S3 gateway at this point in time. + +Usage +===== + +In order to use this charm, it assumed that you have already deployed a ceph +storage cluster using the 'ceph' charm with something like this:: + + juju deploy -n 3 --config ceph.yaml ceph + +To deploy the RADOS gateway simple do:: + + juju deploy ceph-radosgw + juju add-relation ceph-radosgw ceph + +You can then directly access the RADOS gateway by exposing the service:: + + juju expose ceph-radosgw + +The gateway can be accessed over port 80 (as show in juju status exposed +ports). + +Note that you will need to login to one of the service units supporting the +ceph-radosgw charm to generate some access credentials:: + + radosgw-admin user create --uid="ubuntu" --display-name="Ubuntu Ceph" + +Scale-out +========= + +Its possible to scale-out the RADOS Gateway itself:: + + juju add-unit -n 2 ceph-radosgw + +and then stick a HA loadbalancer on the front:: + + juju deploy haproxy + juju add-relation haproxy ceph-radosgw + +Should give you a bit more bang on the front end if you really need it. + +Bootnotes +========= + +The Ceph RADOS Gateway makes use of a multiverse package, +libapache2-mod-fastcgi. As such it will try to automatically enable the +multiverse pocket in /etc/apt/sources.list. Note that there is noting +'wrong' with multiverse components - they typically have less liberal +licensing policies or suchlike. diff --git a/TODO b/TODO new file mode 100644 index 00000000..cd30ef3b --- /dev/null +++ b/TODO @@ -0,0 +1,5 @@ +RADOS Gateway Charm +------------------- + + * cephx support + * Improved process control of radosgw daemon (to many restarts) diff --git a/config.yaml b/config.yaml new file mode 100644 index 00000000..1312bec9 --- /dev/null +++ b/config.yaml @@ -0,0 +1,22 @@ +options: + source: + type: string + default: ppa:ceph-ubuntu/dev + description: | + Optional configuration to support use of additional sources such as: + . + - ppa:myteam/ppa + - cloud:folsom-proposed + - http://my.archive.com/ubuntu main + . + The last option should be used in conjunction with the key configuration + option. + . + Note that a minimum ceph version of 0.48.2 is required for use with this + charm which is NOT provided by the packages in the main Ubuntu archive + for precise. + key: + type: string + description: | + Key ID to import to the apt keyring to support use with arbitary source + configuration from outside of Launchpad archives or PPA's. diff --git a/copyright b/copyright new file mode 100644 index 00000000..f35c8617 --- /dev/null +++ b/copyright @@ -0,0 +1,9 @@ +Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0 + +Files: * +Copyright: 2012, Canonical Ltd. +License: LGPL-2.1 + +License: LGPL-2.1 + On Debian GNU/Linux system you can find the complete text of the + LGPL-2.1 license in '/usr/share/common-licenses/LGPL-2.1' diff --git a/files/www/s3gw.fcgi b/files/www/s3gw.fcgi new file mode 100755 index 00000000..c0f4854a --- /dev/null +++ b/files/www/s3gw.fcgi @@ -0,0 +1,2 @@ +#!/bin/sh +exec /usr/bin/radosgw -c /etc/ceph/ceph.conf -n client.rados.gateway \ No newline at end of file diff --git a/hooks/config-changed b/hooks/config-changed new file mode 120000 index 00000000..9416ca6a --- /dev/null +++ b/hooks/config-changed @@ -0,0 +1 @@ +hooks.py \ No newline at end of file diff --git a/hooks/gateway-relation-joined b/hooks/gateway-relation-joined new file mode 120000 index 00000000..9416ca6a --- /dev/null +++ b/hooks/gateway-relation-joined @@ -0,0 +1 @@ +hooks.py \ No newline at end of file diff --git a/hooks/hooks.py b/hooks/hooks.py new file mode 100755 index 00000000..23f3babb --- /dev/null +++ b/hooks/hooks.py @@ -0,0 +1,139 @@ +#!/usr/bin/python + +# +# Copyright 2012 Canonical Ltd. +# +# Authors: +# James Page +# + +import shutil +import subprocess +import sys +import glob +import os + +import utils + + +def install_www_scripts(): + for x in glob.glob('files/www/*'): + shutil.copy(x, '/var/www/') + + +def install(): + utils.juju_log('INFO', 'Begin install hook.') + utils.enable_pocket('multiverse') + utils.configure_source() + utils.install('radosgw', + 'libapache2-mod-fastcgi', + 'apache2') + utils.juju_log('INFO', 'End install hook.') + + +def emit_cephconf(): + # Ensure ceph directory actually exists + if not os.path.exists('/etc/ceph'): + os.makedirs('/etc/ceph') + + cephcontext = { + 'mon_hosts': ' '.join(get_mon_hosts()), + 'hostname': utils.get_unit_hostname() + } + + with open('/etc/ceph/ceph.conf', 'w') as cephconf: + cephconf.write(utils.render_template('ceph.conf', cephcontext)) + + +def emit_apacheconf(): + apachecontext = { + "hostname": utils.unit_get('private-address') + } + with open('/etc/apache2/sites-available/rgw', 'w') as apacheconf: + apacheconf.write(utils.render_template('rgw', apachecontext)) + + +def apache_sites(): + utils.juju_log('INFO', 'Begin apache_sites.') + subprocess.check_call(['a2dissite', 'default']) + subprocess.check_call(['a2ensite', 'rgw']) + utils.juju_log('INFO', 'End apache_sites.') + + +def apache_modules(): + utils.juju_log('INFO', 'Begin apache_sites.') + subprocess.check_call(['a2enmod', 'fastcgi']) + subprocess.check_call(['a2enmod', 'rewrite']) + utils.juju_log('INFO', 'End apache_sites.') + + +def apache_reload(): + subprocess.call(['service', 'apache2', 'reload']) + + +def config_changed(): + utils.juju_log('INFO', 'Begin config-changed hook.') + emit_cephconf() + emit_apacheconf() + install_www_scripts() + apache_sites() + apache_modules() + apache_reload() + utils.juju_log('INFO', 'End config-changed hook.') + + +def get_mon_hosts(): + hosts = [] + for relid in utils.relation_ids('mon'): + for unit in utils.relation_list(relid): + hosts.append( + '{}:6789'.format(utils.get_host_ip( + utils.relation_get('private-address', + unit, relid))) + ) + + hosts.sort() + return hosts + + +def mon_relation(): + utils.juju_log('INFO', 'Begin mon-relation hook.') + emit_cephconf() + restart() + utils.juju_log('INFO', 'End mon-relation hook.') + + +def gateway_relation(): + utils.juju_log('INFO', 'Begin gateway-relation hook.') + utils.relation_set(hostname=utils.unit_get('private-address'), + port=80) + utils.juju_log('INFO', 'Begin gateway-relation hook.') + + +def upgrade_charm(): + utils.juju_log('INFO', 'Begin upgrade-charm hook.') + utils.juju_log('INFO', 'End upgrade-charm hook.') + + +def start(): + # In case we're being redeployed to the same machines, try + # to make sure everything is running as soon as possible. + subprocess.call(['service', 'radosgw', 'start']) + utils.expose(port=80) + + +def restart(): + subprocess.call(['service', 'radosgw', 'restart']) + + +utils.do_hooks({ + 'install': install, + 'config-changed': config_changed, + 'mon-relation-departed': mon_relation, + 'mon-relation-changed': mon_relation, + 'gateway-relation-joined': gateway_relation, + 'start': start, + 'upgrade-charm': config_changed, # same function ATM + }) + +sys.exit(0) diff --git a/hooks/install b/hooks/install new file mode 120000 index 00000000..9416ca6a --- /dev/null +++ b/hooks/install @@ -0,0 +1 @@ +hooks.py \ No newline at end of file diff --git a/hooks/mon-relation-changed b/hooks/mon-relation-changed new file mode 120000 index 00000000..9416ca6a --- /dev/null +++ b/hooks/mon-relation-changed @@ -0,0 +1 @@ +hooks.py \ No newline at end of file diff --git a/hooks/mon-relation-departed b/hooks/mon-relation-departed new file mode 120000 index 00000000..9416ca6a --- /dev/null +++ b/hooks/mon-relation-departed @@ -0,0 +1 @@ +hooks.py \ No newline at end of file diff --git a/hooks/start b/hooks/start new file mode 120000 index 00000000..9416ca6a --- /dev/null +++ b/hooks/start @@ -0,0 +1 @@ +hooks.py \ No newline at end of file diff --git a/hooks/stop b/hooks/stop new file mode 120000 index 00000000..9416ca6a --- /dev/null +++ b/hooks/stop @@ -0,0 +1 @@ +hooks.py \ No newline at end of file diff --git a/hooks/upgrade-charm b/hooks/upgrade-charm new file mode 120000 index 00000000..9416ca6a --- /dev/null +++ b/hooks/upgrade-charm @@ -0,0 +1 @@ +hooks.py \ No newline at end of file diff --git a/hooks/utils.py b/hooks/utils.py new file mode 100644 index 00000000..c6556dc7 --- /dev/null +++ b/hooks/utils.py @@ -0,0 +1,177 @@ + +# +# Copyright 2012 Canonical Ltd. +# +# Authors: +# James Page +# Paul Collins +# + +import os +import subprocess +import socket +import sys +import re + + +def do_hooks(hooks): + hook = os.path.basename(sys.argv[0]) + + try: + hooks[hook]() + except KeyError: + juju_log('INFO', + "This charm doesn't know how to handle '{}'.".format(hook)) + + +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 + + +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) + + +def configure_source(): + source = config_get('source') + if (source.startswith('ppa:') or + source.startswith('cloud:') or + source.startswith('http:')): + cmd = [ + 'add-apt-repository', + source + ] + subprocess.check_call(cmd) + if source.startswith('http:'): + key = config_get('key') + cmd = [ + 'apt-key', + 'import', + key + ] + subprocess.check_call(cmd) + cmd = [ + 'apt-get', + 'update' + ] + subprocess.check_call(cmd) + + +def enable_pocket(pocket): + apt_sources = "/etc/apt/sources.list" + with open(apt_sources, "r") as sources: + lines = sources.readlines() + with open(apt_sources, "w") as sources: + for line in lines: + if pocket in line: + sources.write(re.sub('^# deb', 'deb', line)) + else: + sources.write(line) + + +# 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 + ] + return subprocess.check_output(cmd).split() # IGNORE:E1103 + + +def relation_list(rid): + cmd = [ + 'relation-list', + '-r', rid, + ] + return subprocess.check_output(cmd).split() # IGNORE:E1103 + + +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) + return subprocess.check_output(cmd).strip() # IGNORE:E1103 + + +def relation_set(**kwargs): + cmd = [ + 'relation-set' + ] + for k, v in kwargs.items(): + cmd.append('{}={}'.format(k, v)) + subprocess.check_call(cmd) + + +def unit_get(attribute): + cmd = [ + 'unit-get', + attribute + ] + return subprocess.check_output(cmd).strip() # IGNORE:E1103 + + +def config_get(attribute): + cmd = [ + 'config-get', + attribute + ] + return subprocess.check_output(cmd).strip() # IGNORE:E1103 + + +def get_unit_hostname(): + return socket.gethostname() + + +def get_host_ip(hostname=unit_get('private-address')): + cmd = [ + 'dig', + '+short', + hostname + ] + return subprocess.check_output(cmd).strip() # IGNORE:E1103 diff --git a/metadata.yaml b/metadata.yaml new file mode 100644 index 00000000..9db4b805 --- /dev/null +++ b/metadata.yaml @@ -0,0 +1,15 @@ +name: ceph-radosgw +summary: Highly scalable distributed storage - RADOS HTTP Gateway +maintainer: James Page +description: | + Ceph is a distributed storage and network file system designed to provide + excellent performance, reliability, and scalability. + . + This charm provides the RADOS HTTP gateway supporting S3 and Swift protocols + for object storage. +requires: + mon: + interface: ceph-radosgw +provides: + gateway: + interface: http diff --git a/revision b/revision new file mode 100644 index 00000000..b4de3947 --- /dev/null +++ b/revision @@ -0,0 +1 @@ +11 diff --git a/templates/ceph.conf b/templates/ceph.conf new file mode 100644 index 00000000..73389c63 --- /dev/null +++ b/templates/ceph.conf @@ -0,0 +1,9 @@ +[global] + auth supported = none + mon host = {{ mon_hosts }} + +[client.radosgw.gateway] + host = {{ hostname }} + keyring = /etc/ceph/keyring.rados.gateway + rgw socket path = /tmp/radosgw.sock + log file = /var/log/ceph/radosgw.log diff --git a/templates/rgw b/templates/rgw new file mode 100644 index 00000000..7b3f8b6e --- /dev/null +++ b/templates/rgw @@ -0,0 +1,25 @@ + + FastCgiExternalServer /var/www/s3gw.fcgi -socket /tmp/radosgw.sock + + + + ServerName {{ hostname }} + ServerAdmin ceph@ubuntu.com + DocumentRoot /var/www + RewriteEngine On + RewriteRule ^/([a-zA-Z0-9-_.]*)([/]?.*) /s3gw.fcgi?page=$1¶ms=$2&%{QUERY_STRING} [E=HTTP_AUTHORIZATION:%{HTTP:Authorization},L] + + + Options +ExecCGI + AllowOverride All + SetHandler fastcgi-script + Order allow,deny + Allow from all + AuthBasicAuthoritative Off + + + AllowEncodedSlashes On + ErrorLog /var/log/apache2/error.log + CustomLog /var/log/apache2/access.log combined + ServerSignature Off +