From 9b4e44913b37f86ab7f4f61ffbd954255061cd86 Mon Sep 17 00:00:00 2001 From: James Page Date: Mon, 18 Mar 2013 10:28:17 +0000 Subject: [PATCH 1/3] Refactoring to support use of openstack charm helpers --- hooks/amqp-relation-changed | 2 +- hooks/ceph-relation-changed | 2 +- hooks/ceph-relation-joined | 2 +- hooks/cluster-relation-changed | 2 +- hooks/cluster-relation-joined | 2 +- hooks/ha-relation-changed | 2 +- hooks/ha-relation-joined | 2 +- hooks/install | 2 +- hooks/lib/{ceph.py => ceph_utils.py} | 28 +- hooks/lib/cluster_utils.py | 130 +++++ hooks/lib/utils.py | 287 +++++++++++ hooks/rabbit_utils.py | 3 +- ...ations.py => rabbitmq_server_relations.py} | 62 +-- hooks/upgrade-charm | 2 +- hooks/utils.py | 457 ------------------ 15 files changed, 482 insertions(+), 503 deletions(-) rename hooks/lib/{ceph.py => ceph_utils.py} (94%) create mode 100644 hooks/lib/cluster_utils.py create mode 100644 hooks/lib/utils.py rename hooks/{rabbitmq-server-relations.py => rabbitmq_server_relations.py} (83%) delete mode 100644 hooks/utils.py diff --git a/hooks/amqp-relation-changed b/hooks/amqp-relation-changed index 46ff2b7a..eecaaa74 120000 --- a/hooks/amqp-relation-changed +++ b/hooks/amqp-relation-changed @@ -1 +1 @@ -rabbitmq-server-relations.py \ No newline at end of file +rabbitmq_server_relations.py \ No newline at end of file diff --git a/hooks/ceph-relation-changed b/hooks/ceph-relation-changed index 46ff2b7a..eecaaa74 120000 --- a/hooks/ceph-relation-changed +++ b/hooks/ceph-relation-changed @@ -1 +1 @@ -rabbitmq-server-relations.py \ No newline at end of file +rabbitmq_server_relations.py \ No newline at end of file diff --git a/hooks/ceph-relation-joined b/hooks/ceph-relation-joined index 46ff2b7a..eecaaa74 120000 --- a/hooks/ceph-relation-joined +++ b/hooks/ceph-relation-joined @@ -1 +1 @@ -rabbitmq-server-relations.py \ No newline at end of file +rabbitmq_server_relations.py \ No newline at end of file diff --git a/hooks/cluster-relation-changed b/hooks/cluster-relation-changed index 46ff2b7a..eecaaa74 120000 --- a/hooks/cluster-relation-changed +++ b/hooks/cluster-relation-changed @@ -1 +1 @@ -rabbitmq-server-relations.py \ No newline at end of file +rabbitmq_server_relations.py \ No newline at end of file diff --git a/hooks/cluster-relation-joined b/hooks/cluster-relation-joined index 46ff2b7a..eecaaa74 120000 --- a/hooks/cluster-relation-joined +++ b/hooks/cluster-relation-joined @@ -1 +1 @@ -rabbitmq-server-relations.py \ No newline at end of file +rabbitmq_server_relations.py \ No newline at end of file diff --git a/hooks/ha-relation-changed b/hooks/ha-relation-changed index 46ff2b7a..eecaaa74 120000 --- a/hooks/ha-relation-changed +++ b/hooks/ha-relation-changed @@ -1 +1 @@ -rabbitmq-server-relations.py \ No newline at end of file +rabbitmq_server_relations.py \ No newline at end of file diff --git a/hooks/ha-relation-joined b/hooks/ha-relation-joined index 46ff2b7a..eecaaa74 120000 --- a/hooks/ha-relation-joined +++ b/hooks/ha-relation-joined @@ -1 +1 @@ -rabbitmq-server-relations.py \ No newline at end of file +rabbitmq_server_relations.py \ No newline at end of file diff --git a/hooks/install b/hooks/install index 46ff2b7a..eecaaa74 120000 --- a/hooks/install +++ b/hooks/install @@ -1 +1 @@ -rabbitmq-server-relations.py \ No newline at end of file +rabbitmq_server_relations.py \ No newline at end of file diff --git a/hooks/lib/ceph.py b/hooks/lib/ceph_utils.py similarity index 94% rename from hooks/lib/ceph.py rename to hooks/lib/ceph_utils.py index 027119f6..f3dc52ed 100644 --- a/hooks/lib/ceph.py +++ b/hooks/lib/ceph_utils.py @@ -1,10 +1,18 @@ -import utils +# +# Copyright 2012 Canonical Ltd. +# +# This file is sourced from lp:openstack-charm-helpers +# +# Authors: +# James Page +# Adam Gandelman +# + import commands -import re import subprocess -import sys import os import shutil +import lib.utils as utils KEYRING = '/etc/ceph/ceph.client.%s.keyring' KEYFILE = '/etc/ceph/ceph.client.%s.key' @@ -15,6 +23,7 @@ CEPH_CONF = """[global] mon host = %(mon_hosts)s """ + def execute(cmd): subprocess.check_call(cmd) @@ -55,6 +64,7 @@ def pool_exists(service, name): (rc, out) = commands.getstatusoutput("rados --id %s lspools" % service) return name in out + def create_pool(service, name): cmd = [ 'rados', @@ -69,9 +79,11 @@ def create_pool(service, name): def keyfile_path(service): return KEYFILE % service + def keyring_path(service): return KEYRING % service + def create_keyring(service, key): keyring = keyring_path(service) if os.path.exists(keyring): @@ -91,11 +103,11 @@ def create_key_file(service, key): # create a file containing the key keyfile = keyfile_path(service) if os.path.exists(keyfile): - utils.juju_log('INFO', 'ceph: Keyfile exists at %s.' % keyfile) + utils.juju_log('INFO', 'ceph: Keyfile exists at %s.' % keyfile) fd = open(keyfile, 'w') fd.write(key) fd.close() - utils.juju_log('INFO', 'ceph: Created new keyfile at %s.' % keyfile) + utils.juju_log('INFO', 'ceph: Created new keyfile at %s.' % keyfile) def get_ceph_nodes(): @@ -122,6 +134,7 @@ def image_mapped(image_name): (rc, out) = commands.getstatusoutput('rbd showmapped') return image_name in out + def map_block_storage(service, pool, image): cmd = [ 'rbd', @@ -138,6 +151,7 @@ def map_block_storage(service, pool, image): def filesystem_mounted(fs): return subprocess.call(['grep', '-wqs', fs, '/proc/mounts']) == 0 + def make_filesystem(blk_device, fstype='ext4'): utils.juju_log('INFO', 'ceph: Formatting block device %s as filesystem %s.' %\ @@ -173,9 +187,10 @@ def place_data_on_ceph(service, blk_device, data_src_dst, fstype='ext4'): cmd = ['chown', '-R', '%s:%s' % (uid, gid), data_src_dst] execute(cmd) + # TODO: re-use def modprobe_kernel_module(module): - utils.juju_log('INFO','Loading kernel module') + utils.juju_log('INFO', 'Loading kernel module') cmd = ['modprobe', module] execute(cmd) cmd = 'echo %s >> /etc/modules' % module @@ -191,6 +206,7 @@ def copy_files(src, dst, symlinks=False, ignore=None): else: shutil.copy2(s, d) + def ensure_ceph_storage(service, pool, rbd_img, sizemb, mount_point, blk_device, fstype, system_services=[]): """ diff --git a/hooks/lib/cluster_utils.py b/hooks/lib/cluster_utils.py new file mode 100644 index 00000000..63f358b2 --- /dev/null +++ b/hooks/lib/cluster_utils.py @@ -0,0 +1,130 @@ +# +# Copyright 2012 Canonical Ltd. +# +# This file is sourced from lp:openstack-charm-helpers +# +# Authors: +# James Page +# Adam Gandelman +# + +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') == "yes": + 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/utils.py b/hooks/lib/utils.py new file mode 100644 index 00000000..43c8d566 --- /dev/null +++ b/hooks/lib/utils.py @@ -0,0 +1,287 @@ +# +# Copyright 2012 Canonical Ltd. +# +# This file is sourced from lp:openstack-charm-helpers +# +# Authors: +# James Page +# Paul Collins +# Adam Gandelman +# + +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') + + +def running(service): + try: + output = subprocess.check_output(['service', service, 'status']) + except subprocess.CalledProcessError: + return False + else: + if ("start/running" in output or + "is running" in output): + return True + else: + return False + + +def is_relation_made(relation, key='private-address'): + relation_data = [] + for r_id in (relation_ids(relation) or []): + for unit in (relation_list(r_id) or []): + relation_data.append(relation_get(key, + rid=r_id, + unit=unit)) + if not relation_data: + return False + return True diff --git a/hooks/rabbit_utils.py b/hooks/rabbit_utils.py index a8bb5264..007f81c8 100644 --- a/hooks/rabbit_utils.py +++ b/hooks/rabbit_utils.py @@ -1,7 +1,7 @@ import os import re import subprocess -import utils +import lib.utils as utils import apt_pkg as apt PACKAGES = ['pwgen', 'rabbitmq-server'] @@ -9,6 +9,7 @@ PACKAGES = ['pwgen', 'rabbitmq-server'] RABBITMQ_CTL = '/usr/sbin/rabbitmqctl' COOKIE_PATH = '/var/lib/rabbitmq/.erlang.cookie' + def vhost_exists(vhost): cmd = [RABBITMQ_CTL, 'list_vhosts'] out = subprocess.check_output(cmd) diff --git a/hooks/rabbitmq-server-relations.py b/hooks/rabbitmq_server_relations.py similarity index 83% rename from hooks/rabbitmq-server-relations.py rename to hooks/rabbitmq_server_relations.py index bb8f40e7..0a197e64 100755 --- a/hooks/rabbitmq-server-relations.py +++ b/hooks/rabbitmq_server_relations.py @@ -7,19 +7,22 @@ import subprocess import rabbit_utils as rabbit -import utils -import lib.ceph as ceph +import lib.utils as utils +import lib.cluster_utils as cluster +import lib.ceph_utils as ceph SERVICE_NAME = os.getenv('JUJU_UNIT_NAME').split('/')[0] POOL_NAME = SERVICE_NAME -RABBIT_DIR='/var/lib/rabbitmq' +RABBIT_DIR = '/var/lib/rabbitmq' + def install(): utils.install(*rabbit.PACKAGES) utils.expose(5672) + def amqp_changed(relation_id=None, remote_unit=None): - if not utils.eligible_leader('res_rabbitmq_vip'): + if not cluster.eligible_leader('res_rabbitmq_vip'): msg = 'amqp_changed(): Deferring amqp_changed to eligible_leader.' utils.juju_log('INFO', msg) return @@ -47,7 +50,7 @@ def amqp_changed(relation_id=None, remote_unit=None): relation_settings = { 'password': password } - if utils.is_clustered(): + if cluster.is_clustered(): relation_settings['clustered'] = 'true' relation_settings['vip'] = utils.config_get('vip') if relation_id: @@ -107,7 +110,6 @@ def cluster_changed(): def ha_joined(): - config = {} corosync_bindiface = utils.config_get('ha-bindiface') corosync_mcastport = utils.config_get('ha-mcastport') vip = utils.config_get('vip') @@ -121,14 +123,13 @@ def ha_joined(): 'configure hacluster.') sys.exit(1) - - if not utils.is_relation_made('ceph'): + if not utils.is_relation_made('ceph', 'auth'): utils.juju_log('INFO', 'ha_joined: No ceph relation yet, deferring.') return # rabbit node-name need to match on all nodes. - utils.juju_log('INFO','Stopping rabbitmq-server.') + utils.juju_log('INFO', 'Stopping rabbitmq-server.') utils.stop('rabbitmq-server') rabbit.set_node_name('%s@localhost' % SERVICE_NAME) @@ -138,26 +139,28 @@ def ha_joined(): relation_settings['corosync_mcastport'] = corosync_mcastport relation_settings['resources'] = { - 'res_rabbitmq_rbd':'ocf:ceph:rbd', - 'res_rabbitmq_fs':'ocf:heartbeat:Filesystem', - 'res_rabbitmq_vip':'ocf:heartbeat:IPaddr2', - 'res_rabbitmq-server':'lsb:rabbitmq-server', + 'res_rabbitmq_rbd': 'ocf:ceph:rbd', + 'res_rabbitmq_fs': 'ocf:heartbeat:Filesystem', + 'res_rabbitmq_vip': 'ocf:heartbeat:IPaddr2', + 'res_rabbitmq-server': 'lsb:rabbitmq-server', } relation_settings['resource_params'] = { - 'res_rabbitmq_rbd': 'params name="%s" pool="%s" user="%s" secret="%s"' %\ - (rbd_name, POOL_NAME, SERVICE_NAME, ceph.keyfile_path(SERVICE_NAME)), + 'res_rabbitmq_rbd': 'params name="%s" pool="%s" user="%s" ' + 'secret="%s"' % \ + (rbd_name, POOL_NAME, + SERVICE_NAME, ceph.keyfile_path(SERVICE_NAME)), 'res_rabbitmq_fs': 'params device="/dev/rbd/%s/%s" directory="%s" '\ - 'fstype="ext4" op start start-delay="10s"' %\ - (POOL_NAME, rbd_name, RABBIT_DIR), - 'res_rabbitmq_vip':'params ip="%s" cidr_netmask="%s" nic="%s"' %\ - (vip, vip_cidr, vip_iface), - 'res_rabbitmqd':'op start start-delay="5s" op monitor interval="5s"', + 'fstype="ext4" op start start-delay="10s"' %\ + (POOL_NAME, rbd_name, RABBIT_DIR), + 'res_rabbitmq_vip': 'params ip="%s" cidr_netmask="%s" nic="%s"' %\ + (vip, vip_cidr, vip_iface), + 'res_rabbitmqd': 'op start start-delay="5s" op monitor interval="5s"', } relation_settings['groups'] = { - 'grp_rabbitmq':'res_rabbitmq_rbd res_rabbitmq_fs res_rabbitmq_vip '\ - 'res_rabbitmq-server', + 'grp_rabbitmq': 'res_rabbitmq_rbd res_rabbitmq_fs res_rabbitmq_vip '\ + 'res_rabbitmq-server', } for rel_id in utils.relation_ids('ha'): @@ -165,14 +168,12 @@ def ha_joined(): def ha_changed(): - if not utils.is_clustered(): + if not cluster.is_clustered(): return vip = utils.config_get('vip') - utils.juju_log('INFO', 'ha_changed(): We are now HA clustered. '\ + utils.juju_log('INFO', 'ha_changed(): We are now HA clustered. ' 'Advertising our VIP (%s) to all AMQP clients.' %\ vip) - relation_settings = {'vip': vip, 'clustered': 'true'} - # need to re-authenticate all clients since node-name changed. for rid in utils.relation_ids('amqp'): for unit in utils.relation_list(rid): @@ -195,7 +196,7 @@ def ceph_changed(): ceph.configure(service=SERVICE_NAME, key=key, auth=auth) - if utils.eligible_leader('res_rabbitmq_vip'): + if cluster.eligible_leader('res_rabbitmq_vip'): rbd_img = utils.config_get('rbd-name') rbd_size = utils.config_get('rbd-size') sizemb = int(rbd_size.split('G')[0]) * 1024 @@ -208,7 +209,7 @@ def ceph_changed(): else: utils.juju_log('INFO', 'This is not the peer leader. Not configuring RBD.') - utils.juju_log('INFO','Stopping rabbitmq-server.') + utils.juju_log('INFO', 'Stopping rabbitmq-server.') utils.stop('rabbitmq-server') # If 'ha' relation has been made before the 'ceph' relation @@ -230,8 +231,9 @@ def upgrade_charm(): if f.endswith('.passwd'): s = os.path.join('/var/lib/juju', f) d = os.path.join('/var/lib/rabbitmq', f) - m = 'upgrade_charm: Migrating stored passwd from %s to %s.' % (s, d) - utils.juju_log('INFO', m) + utils.juju_log('INFO', + 'upgrade_charm: Migrating stored passwd' + ' from %s to %s.' % (s, d)) shutil.move(s, d) hooks = { diff --git a/hooks/upgrade-charm b/hooks/upgrade-charm index 46ff2b7a..eecaaa74 120000 --- a/hooks/upgrade-charm +++ b/hooks/upgrade-charm @@ -1 +1 @@ -rabbitmq-server-relations.py \ No newline at end of file +rabbitmq_server_relations.py \ No newline at end of file diff --git a/hooks/utils.py b/hooks/utils.py deleted file mode 100644 index f5fd708e..00000000 --- a/hooks/utils.py +++ /dev/null @@ -1,457 +0,0 @@ - -# -# Copyright 2012 Canonical Ltd. -# -# Authors: -# James Page -# Paul Collins -# - -import os -import subprocess -import socket -import sys -import fcntl -import struct -import json -import time - - -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 can_install(): - try: - fd = os.open("/var/lib/dpkg/lock", os.O_RDWR|os.O_CREAT|os.O_NOFOLLOW, 0640) - fcntl.lockf(fd, fcntl.LOCK_EX | fcntl.LOCK_NB) - except IOError, message: - os.close(fd) - return False - else: - fcntl.lockf(fd, fcntl.LOCK_UN) - os.close(fd) - return True - - -def install(*pkgs): - cmd = [ - 'apt-get', - '-y', - 'install' - ] - for pkg in pkgs: - cmd.append(pkg) - while not can_install(): - juju_log('INFO', - "dpkg is busy, can't install %s yet, waiting..." % pkgs) - time.sleep(1) - subprocess.check_call(cmd) - -TEMPLATES_DIR = 'templates' - -try: - import jinja2 -except ImportError: - install('python-jinja2') - import jinja2 - -try: - from netaddr import IPNetwork -except ImportError: - install('python-netaddr') - from netaddr import IPNetwork - -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 = { - 'precise-folsom': 'precise-updates/folsom', - 'precise-folsom/updates': 'precise-updates/folsom', - 'precise-folsom/proposed': 'precise-proposed/folsom', - 'precise-grizzly': 'precise-updates/grizzly', - 'precise-grizzly/updates': 'precise-updates/grizzly', - 'precise-grizzly/proposed': 'precise-proposed/grizzly' - } - - -def execute(cmd, die=False, echo=False): - """ Executes a command - - if die=True, script will exit(1) if command does not return 0 - if echo=True, output of command will be printed to stdout - - returns a tuple: (stdout, stderr, return code) - """ - p = subprocess.Popen(cmd.split(" "), - stdout=subprocess.PIPE, - stdin=subprocess.PIPE, - stderr=subprocess.PIPE) - stdout="" - stderr="" - - def print_line(l): - if echo: - print l.strip('\n') - sys.stdout.flush() - - for l in iter(p.stdout.readline, ''): - print_line(l) - stdout += l - for l in iter(p.stderr.readline, ''): - print_line(l) - stderr += l - - p.communicate() - rc = p.returncode - - if die and rc != 0: - error_out("ERROR: command %s return non-zero.\n" % cmd) - return (stdout, stderr, rc) - - -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 - ] - 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) - value = subprocess.check_output(cmd).strip() # IGNORE:E1103 - if value == "": - return None - else: - return value - - -def relation_get_dict(relation_id=None, remote_unit=None): - """Obtain all relation data as dict by way of JSON""" - cmd = 'relation-get --format=json' - if relation_id: - cmd += ' -r %s' % relation_id - if remote_unit: - remote_unit_orig = os.getenv('JUJU_REMOTE_UNIT', None) - os.environ['JUJU_REMOTE_UNIT'] = remote_unit - j = execute(cmd, die=True)[0] - if remote_unit and remote_unit_orig: - os.environ['JUJU_REMOTE_UNIT'] = remote_unit_orig - d = json.loads(j) - settings = {} - # convert unicode to strings - for k, v in d.iteritems(): - settings[str(k)] = str(v) - return settings - - -def relation_set(**kwargs): - cmd = [ - 'relation-set' - ] - args = [] - for k, v in kwargs.items(): - if k == 'rid': - 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', - attribute - ] - value = subprocess.check_output(cmd).strip() # IGNORE:E1103 - if value == "": - return None - else: - return value - - -def get_unit_hostname(): - return socket.gethostname() - - -def get_unit_name(): - return os.environ.get('JUJU_UNIT_NAME').replace('/','-') - - -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: - pass - try: - answers = dns.resolver.query(hostname, 'A') - if answers: - return answers[0].address - except dns.resolver.NXDOMAIN: - pass - 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 running(service): - # TODO: ensure compat. /w sysv init scripts. - try: - output = subprocess.check_output(['service', service, 'status']) - except subprocess.CalledProcessError: - return False - else: - if ("start/running" in output or - "is running" in output): - return True - else: - return False - - -def disable_upstart_services(*services): - for service in services: - with open("/etc/init/{}.override".format(service), "w") as override: - override.write("manual") - - -def enable_upstart_services(*services): - for service in services: - path = '/etc/init/{}.override'.format(service) - if os.path.exists(path): - os.remove(path) - - -def disable_lsb_services(*services): - for service in services: - subprocess.check_call(['update-rc.d', '-f', service, 'remove']) - - -def enable_lsb_services(*services): - for service in services: - subprocess.check_call(['update-rc.d', '-f', service, 'defaults']) - - -def get_iface_ipaddr(iface): - s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - return socket.inet_ntoa(fcntl.ioctl( - s.fileno(), - 0x8919, # SIOCGIFADDR - struct.pack('256s', iface[:15]) - )[20:24]) - - -def get_iface_netmask(iface): - s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - return socket.inet_ntoa(fcntl.ioctl( - s.fileno(), - 0x891b, # SIOCGIFNETMASK - struct.pack('256s', iface[:15]) - )[20:24]) - - -def get_netmask_cidr(netmask): - netmask = netmask.split('.') - binary_str = '' - for octet in netmask: - binary_str += bin(int(octet))[2:].zfill(8) - return str(len(binary_str.rstrip('0'))) - - -def get_network_address(iface): - if iface: - network = "{}/{}".format(get_iface_ipaddr(iface), - get_netmask_cidr(get_iface_netmask(iface))) - ip = IPNetwork(network) - return str(ip.network) - else: - return None - - -def is_clustered(): - for r_id in (relation_ids('ha') or []): - for unit in (relation_list(r_id) or []): - relation_data = \ - relation_get_dict(relation_id=r_id, - remote_unit=unit) - if 'clustered' in relation_data: - return True - return False - - -def is_leader(res): - status = execute('crm resource show %s' % res, echo=True)[0].strip() - hostname = execute('hostname', echo=True)[0].strip() - if 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(res): - if is_clustered(): - if not is_leader(res): - 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 is_relation_made(relation=None): - relation_data = [] - for r_id in (relation_ids(relation) or []): - for unit in (relation_list(r_id) or []): - relation_data.append(relation_get_dict(relation_id=r_id, - remote_unit=unit)) - if not relation_data: - return False - return True From 244a4b7d3f703322f053fb112501f083a751e517 Mon Sep 17 00:00:00 2001 From: James Page Date: Mon, 18 Mar 2013 10:48:35 +0000 Subject: [PATCH 2/3] Fixup is_relation_made --- hooks/lib/utils.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/hooks/lib/utils.py b/hooks/lib/utils.py index 43c8d566..e452d5d5 100644 --- a/hooks/lib/utils.py +++ b/hooks/lib/utils.py @@ -276,12 +276,8 @@ def running(service): def is_relation_made(relation, key='private-address'): - relation_data = [] for r_id in (relation_ids(relation) or []): for unit in (relation_list(r_id) or []): - relation_data.append(relation_get(key, - rid=r_id, - unit=unit)) - if not relation_data: - return False - return True + if relation_get(key, rid=r_id, unit=unit): + return True + return False From 333555787f33910bbfc6260575a62b961c0a29cd Mon Sep 17 00:00:00 2001 From: James Page Date: Mon, 18 Mar 2013 11:06:15 +0000 Subject: [PATCH 3/3] Added pydev project --- .project | 17 +++++++++++++++++ .pydevproject | 8 ++++++++ 2 files changed, 25 insertions(+) create mode 100644 .project create mode 100644 .pydevproject diff --git a/.project b/.project new file mode 100644 index 00000000..bef1b638 --- /dev/null +++ b/.project @@ -0,0 +1,17 @@ + + + rabbitmq-server + + + + + + org.python.pydev.PyDevBuilder + + + + + + org.python.pydev.pythonNature + + diff --git a/.pydevproject b/.pydevproject new file mode 100644 index 00000000..984673a8 --- /dev/null +++ b/.pydevproject @@ -0,0 +1,8 @@ + + +python 2.7 +Default + +/rabbitmq-server/hooks + +