~frankban/charms/precise/juju-gui/guiserver-async-watcher

« back to all changes in this revision

Viewing changes to scripts/charmsupport/hookenv.py

  • Committer: Francesco Banconi
  • Date: 2013-08-19 08:44:08 UTC
  • mfrom: (89.2.1 unstable)
  • Revision ID: francesco.banconi@canonical.com-20130819084408-br0gkerolwwoy61q
Merged trunk and bumped up revision.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
"Interactions with the Juju environment"
 
2
# source: 27:lp:charmsupport
 
3
# Copyright 2012 Canonical Ltd.
 
4
#
 
5
# Authors:
 
6
#  Matthew Wedgwood <matthew.wedgwood@canonical.com>
 
7
 
 
8
import os
 
9
import json
 
10
import yaml
 
11
import subprocess
 
12
 
 
13
CRITICAL = "CRITICAL"
 
14
ERROR = "ERROR"
 
15
WARNING = "WARNING"
 
16
INFO = "INFO"
 
17
DEBUG = "DEBUG"
 
18
def log(message, level=DEBUG):
 
19
    "Write a message to the juju log"
 
20
    subprocess.call( [ 'juju-log', '-l', level, message ] )
 
21
 
 
22
class Serializable(object):
 
23
    "Wrapper, an object that can be serialized to yaml or json"
 
24
    def __init__(self, obj):
 
25
        # wrap the object
 
26
        super(Serializable, self).__init__()
 
27
        self._wrapped_obj = obj
 
28
 
 
29
    def __getattr__(self, attr):
 
30
        # see if this object has attr
 
31
        if attr in self.__dict__:
 
32
            return getattr(self, attr)
 
33
        # proxy to the wrapped object
 
34
        return self[attr]
 
35
 
 
36
    def __getitem__(self, key):
 
37
        return self._wrapped_obj[key]
 
38
 
 
39
    def json(self):
 
40
        "Serialize the object to json"
 
41
        return json.dumps(self._wrapped_obj)
 
42
 
 
43
    def yaml(self):
 
44
        "Serialize the object to yaml"
 
45
        return yaml.dump(self._wrapped_obj)
 
46
 
 
47
def execution_environment():
 
48
    """A convenient bundling of the current execution context"""
 
49
    context = {}
 
50
    context['conf'] = config()
 
51
    context['unit'] = local_unit()
 
52
    context['rel'] = relations_of_type()
 
53
    context['env'] = os.environ
 
54
    return context
 
55
 
 
56
def in_relation_hook():
 
57
    "Determine whether we're running in a relation hook"
 
58
    return os.environ.has_key('JUJU_RELATION')
 
59
 
 
60
def relation_type():
 
61
    "The scope for the current relation hook"
 
62
    return os.environ['JUJU_RELATION']
 
63
def relation_id():
 
64
    "The relation ID for the current relation hook"
 
65
    return os.environ['JUJU_RELATION_ID']
 
66
def local_unit():
 
67
    "Local unit ID"
 
68
    return os.environ['JUJU_UNIT_NAME']
 
69
def remote_unit():
 
70
    "The remote unit for the current relation hook"
 
71
    return os.environ['JUJU_REMOTE_UNIT']
 
72
 
 
73
def config(scope=None):
 
74
    "Juju charm configuration"
 
75
    config_cmd_line = ['config-get']
 
76
    if scope is not None:
 
77
        config_cmd_line.append(scope)
 
78
    config_cmd_line.append('--format=json')
 
79
    try:
 
80
        config_data = json.loads(subprocess.check_output(config_cmd_line))
 
81
    except (ValueError, OSError, subprocess.CalledProcessError) as err:
 
82
        log(str(err), level=ERROR)
 
83
        raise err
 
84
    return Serializable(config_data)
 
85
 
 
86
def relation_ids(reltype=None):
 
87
    "A list of relation_ids"
 
88
    reltype = reltype or relation_type()
 
89
    relids = []
 
90
    relid_cmd_line = ['relation-ids', '--format=json', reltype]
 
91
    relids.extend(json.loads(subprocess.check_output(relid_cmd_line)))
 
92
    return relids
 
93
 
 
94
def related_units(relid=None):
 
95
    "A list of related units"
 
96
    relid = relid or relation_id()
 
97
    units_cmd_line = ['relation-list', '--format=json', '-r', relid]
 
98
    units = json.loads(subprocess.check_output(units_cmd_line))
 
99
    return units
 
100
 
 
101
def relation_for_unit(unit=None):
 
102
    "Get the json represenation of a unit's relation"
 
103
    unit = unit or remote_unit()
 
104
    relation_cmd_line = ['relation-get', '--format=json', '-', unit]
 
105
    try:
 
106
        relation = json.loads(subprocess.check_output(relation_cmd_line))
 
107
    except (ValueError, OSError, subprocess.CalledProcessError), err:
 
108
        log(str(err), level=ERROR)
 
109
        raise err
 
110
    for key in relation:
 
111
        if key.endswith('-list'):
 
112
            relation[key] = relation[key].split()
 
113
    relation['__unit__'] = unit
 
114
    return Serializable(relation)
 
115
 
 
116
def relations_for_id(relid=None):
 
117
    "Get relations of a specific relation ID"
 
118
    relation_data = []
 
119
    relid = relid or relation_ids()
 
120
    for unit in related_units(relid):
 
121
        unit_data = relation_for_unit(unit)
 
122
        unit_data['__relid__'] = relid
 
123
        relation_data.append(unit_data)
 
124
    return relation_data
 
125
 
 
126
def relations_of_type(reltype=None):
 
127
    "Get relations of a specific type"
 
128
    relation_data = []
 
129
    if in_relation_hook():
 
130
        reltype = reltype or relation_type()
 
131
        for relid in relation_ids(reltype):
 
132
            for relation in relations_for_id(relid):
 
133
                relation['__relid__'] = relid
 
134
                relation_data.append(relation)
 
135
    return relation_data
 
136
 
 
137
class UnregisteredHookError(Exception): pass
 
138
 
 
139
class Hooks(object):
 
140
    def __init__(self):
 
141
        super(Hooks, self).__init__()
 
142
        self._hooks = {}
 
143
    def register(self, name, function):
 
144
        self._hooks[name] = function
 
145
    def execute(self, args):
 
146
        hook_name = os.path.basename(args[0])
 
147
        if hook_name in self._hooks:
 
148
            self._hooks[hook_name]()
 
149
        else:
 
150
            raise UnregisteredHookError(hook_name)