From 36a0f024716eb4db480db1802d1824d09aad3130 Mon Sep 17 00:00:00 2001 From: "yolanda.robla@canonical.com" <> Date: Tue, 12 Feb 2013 14:53:39 +0100 Subject: [PATCH] first version of charm --- config.yaml | 16 +++ copyright | 17 +++ hooks/ceilometer_utils.py | 5 + hooks/container-relation-joined | 1 + hooks/hooks.py | 23 ++++ hooks/install | 1 + hooks/lib/__init__.py | 1 + hooks/lib/openstack_common.py | 192 ++++++++++++++++++++++++++++++++ hooks/utils.py | 140 +++++++++++++++++++++++ local.yaml | 3 + metadata.yaml | 17 +++ readme | 17 +++ revision | 1 + 13 files changed, 434 insertions(+) create mode 100644 config.yaml create mode 100644 copyright create mode 100644 hooks/ceilometer_utils.py create mode 120000 hooks/container-relation-joined create mode 100755 hooks/hooks.py create mode 120000 hooks/install create mode 100644 hooks/lib/__init__.py create mode 100644 hooks/lib/openstack_common.py create mode 100644 hooks/utils.py create mode 100644 local.yaml create mode 100644 metadata.yaml create mode 100644 readme create mode 100644 revision diff --git a/config.yaml b/config.yaml new file mode 100644 index 0000000..60d279f --- /dev/null +++ b/config.yaml @@ -0,0 +1,16 @@ +options: + openstack-origin: + default: cloud:precise-folsom + type: string + description: | + Repository from which to install. May be one of the following: + distro (default), ppa:somecustom/ppa, a deb url sources entry, + or a supported Cloud Archive release pocket. + + Supported Cloud Archive sources include: cloud:precise-folsom, + cloud:precise-folsom/updates, cloud:precise-folsom/staging, + cloud:precise-folsom/proposed. + + Note that updating this setting to a source that is known to + provide a later version of OpenStack will trigger a software + upgrade. diff --git a/copyright b/copyright new file mode 100644 index 0000000..1632584 --- /dev/null +++ b/copyright @@ -0,0 +1,17 @@ +Format: http://dep.debian.net/deps/dep5/ + +Files: * +Copyright: Copyright 2011, Canonical Ltd., All Rights Reserved. +License: GPL-3 + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + . + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + . + You should have received a copy of the GNU General Public License + along with this program. If not, see . diff --git a/hooks/ceilometer_utils.py b/hooks/ceilometer_utils.py new file mode 100644 index 0000000..4c60a9a --- /dev/null +++ b/hooks/ceilometer_utils.py @@ -0,0 +1,5 @@ +import os +import uuid + +NOVA_CONF="/etc/nova/nova.conf" +CEILOMETER_COMPUTE_SERVICES = ['ceilometer-agent-compute'] diff --git a/hooks/container-relation-joined b/hooks/container-relation-joined new file mode 120000 index 0000000..9416ca6 --- /dev/null +++ b/hooks/container-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 0000000..2a349c8 --- /dev/null +++ b/hooks/hooks.py @@ -0,0 +1,23 @@ +#!/usr/bin/python + +import sys +import time +import os +import utils +import ceilometer_utils + +def install(): + utils.configure_source() + packages = ['ceilometer-common', 'ceilometer-agent-compute'] + utils.install(*packages) + +def container_joined(): + utils.modify_config_file(ceilometer_utils.NOVA_CONF) + utils.restart(*ceilometer_utils.CEILOMETER_COMPUTE_SERVICES) + return True + +utils.do_hooks({ + "install": install, + "container-relation-joined": container_joined +}) +sys.exit(0) diff --git a/hooks/install b/hooks/install new file mode 120000 index 0000000..9416ca6 --- /dev/null +++ b/hooks/install @@ -0,0 +1 @@ +hooks.py \ No newline at end of file diff --git a/hooks/lib/__init__.py b/hooks/lib/__init__.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/hooks/lib/__init__.py @@ -0,0 +1 @@ + diff --git a/hooks/lib/openstack_common.py b/hooks/lib/openstack_common.py new file mode 100644 index 0000000..c9cbb3b --- /dev/null +++ b/hooks/lib/openstack_common.py @@ -0,0 +1,192 @@ +#!/usr/bin/python + +# Common python helper functions used for OpenStack charms. + +import subprocess + +CLOUD_ARCHIVE_URL = "http://ubuntu-cloud.archive.canonical.com/ubuntu" +CLOUD_ARCHIVE_KEY_ID = '5EDB1B62EC4926EA' + +ubuntu_openstack_release = { + 'oneiric': 'diablo', + 'precise': 'essex', + 'quantal': 'folsom', + 'raring' : 'grizzly' +} + + +openstack_codenames = { + '2011.2': 'diablo', + '2012.1': 'essex', + '2012.2': 'folsom', + '2012.3': 'grizzly' +} + + +def juju_log(msg): + subprocess.check_call(['juju-log', msg]) + + +def error_out(msg): + juju_log("FATAL ERROR: %s" % msg) + exit(1) + + +def lsb_release(): + '''Return /etc/lsb-release in a dict''' + lsb = open('/etc/lsb-release', 'r') + d = {} + for l in lsb: + k, v = l.split('=') + d[k.strip()] = v.strip() + return d + + +def get_os_codename_install_source(src): + '''Derive OpenStack release codename from a given installation source.''' + ubuntu_rel = lsb_release()['DISTRIB_CODENAME'] + + rel = '' + if src == 'distro': + try: + rel = ubuntu_openstack_release[ubuntu_rel] + except KeyError: + e = 'Code not derive openstack release for '\ + 'this Ubuntu release: %s' % rel + error_out(e) + return rel + + if src.startswith('cloud:'): + ca_rel = src.split(':')[1] + ca_rel = ca_rel.split('%s-' % ubuntu_rel)[1].split('/')[0] + return ca_rel + + # Best guess match based on deb string provided + if src.startswith('deb'): + for k, v in openstack_codenames.iteritems(): + if v in src: + return v + +def get_os_codename_version(vers): + '''Determine OpenStack codename from version number.''' + try: + return openstack_codenames[vers] + except KeyError: + e = 'Could not determine OpenStack codename for version %s' % vers + error_out(e) + + +def get_os_version_codename(codename): + '''Determine OpenStack version number from codename.''' + for k, v in openstack_codenames.iteritems(): + if v == codename: + return k + e = 'Code not derive OpenStack version for '\ + 'codename: %s' % codename + error_out(e) + + +def get_os_codename_package(pkg): + '''Derive OpenStack release codename from an installed package.''' + cmd = ['dpkg', '-l', pkg] + + try: + output = subprocess.check_output(cmd) + except subprocess.CalledProcessError: + e = 'Could not derive OpenStack version from package that is not '\ + 'installed; %s' % pkg + error_out(e) + + def _clean(line): + line = line.split(' ') + clean = [] + for c in line: + if c != '': + clean.append(c) + return clean + + vers = None + for l in output.split('\n'): + if l.startswith('ii'): + l = _clean(l) + if l[1] == pkg: + vers = l[2] + + if not vers: + e = 'Could not determine version of installed package: %s' % pkg + error_out(e) + + vers = vers[:6] + try: + return openstack_codenames[vers] + except KeyError: + e = 'Could not determine OpenStack codename for version %s' % vers + error_out(e) + + +def configure_installation_source(rel): + '''Configure apt installation source.''' + + def _import_key(id): + cmd = "apt-key adv --keyserver keyserver.ubuntu.com " \ + "--recv-keys %s" % id + try: + subprocess.check_call(cmd.split(' ')) + except: + error_out("Error importing repo key %s" % id) + + if rel == 'distro': + return + elif rel[:4] == "ppa:": + src = rel + subprocess.check_call(["add-apt-repository", "-y", src]) + elif rel[:3] == "deb": + l = len(rel.split('|')) + if l == 2: + src, key = rel.split('|') + juju_log("Importing PPA key from keyserver for %s" % src) + _import_key(key) + elif l == 1: + src = rel + else: + error_out("Invalid openstack-release: %s" % rel) + + with open('/etc/apt/sources.list.d/juju_deb.list', 'w') as f: + f.write(src) + elif rel[:6] == 'cloud:': + ubuntu_rel = lsb_release()['DISTRIB_CODENAME'] + rel = rel.split(':')[1] + u_rel = rel.split('-')[0] + ca_rel = rel.split('-')[1] + + if u_rel != ubuntu_rel: + e = 'Cannot install from Cloud Archive pocket %s on this Ubuntu '\ + 'version (%s)' % (ca_rel, ubuntu_rel) + error_out(e) + + if ca_rel == 'folsom/staging': + # staging is just a regular PPA. + cmd = 'add-apt-repository -y ppa:ubuntu-cloud-archive/folsom-staging' + subprocess.check_call(cmd.split(' ')) + return + + # map charm config options to actual archive pockets. + pockets = { + 'folsom': 'precise-updates/folsom', + 'folsom/updates': 'precise-updates/folsom', + 'folsom/proposed': 'precise-proposed/folsom' + } + + try: + pocket = pockets[ca_rel] + except KeyError: + e = 'Invalid Cloud Archive release specified: %s' % rel + error_out(e) + + src = "deb %s %s main" % (CLOUD_ARCHIVE_URL, pocket) + _import_key(CLOUD_ARCHIVE_KEY_ID) + + with open('/etc/apt/sources.list.d/cloud-archive.list', 'w') as f: + f.write(src) + else: + error_out("Invalid openstack-release specified: %s" % rel) diff --git a/hooks/utils.py b/hooks/utils.py new file mode 100644 index 0000000..948ed91 --- /dev/null +++ b/hooks/utils.py @@ -0,0 +1,140 @@ +# +# Copyright 2012 Canonical Ltd. +# +# Authors: +# James Page +# Paul Collins +# + +import os +import subprocess +import socket +import sys +import apt_pkg as apt +import re +import ceilometer_utils +import ConfigParser + +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) + +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 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/ceilometer.list', 'w') as apt: + apt.write(apt_line + "\n") + cmd = [ + 'apt-get', + 'update' + ] + subprocess.check_call(cmd) + +def juju_log(severity, message): + cmd = [ + 'juju-log', + '--log-level', severity, + message + ] + subprocess.check_call(cmd) + +def config_get(attribute): + cmd = [ + 'config-get', + attribute + ] + value = subprocess.check_output(cmd).strip() # IGNORE:E1103 + if value == "": + return None + else: + return value + +def _service_ctl(service, action): + subprocess.check_call(['service', service, action]) + + +def restart(*services): + for service in services: + _service_ctl(service, 'restart') + + +def stop(*services): + for service in services: + _service_ctl(service, 'stop') + + +def start(*services): + for service in services: + _service_ctl(service, 'start') + + +def get_os_version(package=None): + apt.init() + cache = apt.Cache() + pkg = cache[package or 'quantum-common'] + if pkg.current_ver: + return apt.upstream_version(pkg.current_ver.ver_str) + else: + return None + +def modify_config_file(nova_conf): + config = ConfigParser.ConfigParser() + config.read(nova_conf) + juju_log("INFO", str(config.items("DEFAULT"))) diff --git a/local.yaml b/local.yaml new file mode 100644 index 0000000..9df185f --- /dev/null +++ b/local.yaml @@ -0,0 +1,3 @@ +ceilometer-agent: + openstack-origin: "ppa:openstack-ubuntu-testing/grizzly-trunk-testing" + config-file: "/etc/nova/nova.conf" diff --git a/metadata.yaml b/metadata.yaml new file mode 100644 index 0000000..4fde709 --- /dev/null +++ b/metadata.yaml @@ -0,0 +1,17 @@ +name: ceilometer-agent +subordinate: true +summary: Subordinate charm for deploying Ceilometer compute agent +maintainer: Yolanda Robla +description: | + Ceilometer project aims to become the infrastructure to collect measurements + within OpenStack so that no two agents would need to be written to collect + the same data. It's primary targets are monitoring and metering, but the + framework should be easily expandable to collect for other needs. To that + effect, Ceilometer should be able to share collected data with a variety of consumers. + . + This charm should be used in conjunction with the ceilometer and nova charm to collect + Openstack measures. +requires: + container: + interface: juju-info + scope: container diff --git a/readme b/readme new file mode 100644 index 0000000..caec721 --- /dev/null +++ b/readme @@ -0,0 +1,17 @@ +Overview +-------- + +This charm provides the Ceilometer service for OpenStack. It is intended to +be used alongside the other OpenStack components, starting with the Folsom +release. + +Ceilometer is made up of 2 separate services: an API service, and a collector +service. This charm allows them to be deployed in different +combination, depending on user preference and requirements. + +This charm was developed to support deploying Folsom on both +Ubuntu Quantal and Ubuntu Precise. Since Ceilometer is only available for +Ubuntu 12.04 via the Ubuntu Cloud Archive, deploying this charm to a +Precise machine will by default install Ceilometer and its dependencies from +the Cloud Archive. + diff --git a/revision b/revision new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/revision @@ -0,0 +1 @@ +1