~alexey-pustovalov/charms/precise/zabbix-agent/trunk

« back to all changes in this revision

Viewing changes to hooks/charmhelpers/core/services/helpers.py

  • Committer: Alexey Pustovalov
  • Date: 2015-02-06 10:50:57 UTC
  • Revision ID: alexey.pustovalov@zabbix.com-20150206105057-h7ftvzumkjy2psr9
Initial charm

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
from charmhelpers.core import hookenv
 
2
from charmhelpers.core import templating
 
3
 
 
4
from charmhelpers.core.services.base import ManagerCallback
 
5
 
 
6
 
 
7
__all__ = ['RelationContext', 'TemplateCallback',
 
8
           'render_template', 'template']
 
9
 
 
10
 
 
11
class RelationContext(dict):
 
12
    """
 
13
    Base class for a context generator that gets relation data from juju.
 
14
 
 
15
    Subclasses must provide the attributes `name`, which is the name of the
 
16
    interface of interest, `interface`, which is the type of the interface of
 
17
    interest, and `required_keys`, which is the set of keys required for the
 
18
    relation to be considered complete.  The data for all interfaces matching
 
19
    the `name` attribute that are complete will used to populate the dictionary
 
20
    values (see `get_data`, below).
 
21
 
 
22
    The generated context will be namespaced under the interface type, to prevent
 
23
    potential naming conflicts.
 
24
    """
 
25
    name = None
 
26
    interface = None
 
27
    required_keys = []
 
28
 
 
29
    def __init__(self, *args, **kwargs):
 
30
        super(RelationContext, self).__init__(*args, **kwargs)
 
31
        self.get_data()
 
32
 
 
33
    def __bool__(self):
 
34
        """
 
35
        Returns True if all of the required_keys are available.
 
36
        """
 
37
        return self.is_ready()
 
38
 
 
39
    __nonzero__ = __bool__
 
40
 
 
41
    def __repr__(self):
 
42
        return super(RelationContext, self).__repr__()
 
43
 
 
44
    def is_ready(self):
 
45
        """
 
46
        Returns True if all of the `required_keys` are available from any units.
 
47
        """
 
48
        ready = len(self.get(self.name, [])) > 0
 
49
        if not ready:
 
50
            hookenv.log('Incomplete relation: {}'.format(self.__class__.__name__), hookenv.DEBUG)
 
51
        return ready
 
52
 
 
53
    def _is_ready(self, unit_data):
 
54
        """
 
55
        Helper method that tests a set of relation data and returns True if
 
56
        all of the `required_keys` are present.
 
57
        """
 
58
        return set(unit_data.keys()).issuperset(set(self.required_keys))
 
59
 
 
60
    def get_data(self):
 
61
        """
 
62
        Retrieve the relation data for each unit involved in a relation and,
 
63
        if complete, store it in a list under `self[self.name]`.  This
 
64
        is automatically called when the RelationContext is instantiated.
 
65
 
 
66
        The units are sorted lexographically first by the service ID, then by
 
67
        the unit ID.  Thus, if an interface has two other services, 'db:1'
 
68
        and 'db:2', with 'db:1' having two units, 'wordpress/0' and 'wordpress/1',
 
69
        and 'db:2' having one unit, 'mediawiki/0', all of which have a complete
 
70
        set of data, the relation data for the units will be stored in the
 
71
        order: 'wordpress/0', 'wordpress/1', 'mediawiki/0'.
 
72
 
 
73
        If you only care about a single unit on the relation, you can just
 
74
        access it as `{{ interface[0]['key'] }}`.  However, if you can at all
 
75
        support multiple units on a relation, you should iterate over the list,
 
76
        like::
 
77
 
 
78
            {% for unit in interface -%}
 
79
                {{ unit['key'] }}{% if not loop.last %},{% endif %}
 
80
            {%- endfor %}
 
81
 
 
82
        Note that since all sets of relation data from all related services and
 
83
        units are in a single list, if you need to know which service or unit a
 
84
        set of data came from, you'll need to extend this class to preserve
 
85
        that information.
 
86
        """
 
87
        if not hookenv.relation_ids(self.name):
 
88
            return
 
89
 
 
90
        ns = self.setdefault(self.name, [])
 
91
        for rid in sorted(hookenv.relation_ids(self.name)):
 
92
            for unit in sorted(hookenv.related_units(rid)):
 
93
                reldata = hookenv.relation_get(rid=rid, unit=unit)
 
94
                if self._is_ready(reldata):
 
95
                    ns.append(reldata)
 
96
 
 
97
    def provide_data(self):
 
98
        """
 
99
        Return data to be relation_set for this interface.
 
100
        """
 
101
        return {}
 
102
 
 
103
 
 
104
class TemplateCallback(ManagerCallback):
 
105
    """
 
106
    Callback class that will render a template, for use as a ready action.
 
107
    """
 
108
    def __init__(self, source, target, owner='root', group='root', perms=0444):
 
109
        self.source = source
 
110
        self.target = target
 
111
        self.owner = owner
 
112
        self.group = group
 
113
        self.perms = perms
 
114
 
 
115
    def __call__(self, manager, service_name, event_name):
 
116
        service = manager.get_service(service_name)
 
117
        context = {}
 
118
        for ctx in service.get('required_data', []):
 
119
            context.update(ctx)
 
120
        templating.render(self.source, self.target, context,
 
121
                          self.owner, self.group, self.perms)
 
122
 
 
123
 
 
124
# Convenience aliases for templates
 
125
render_template = template = TemplateCallback