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