1
from charmhelpers.core import hookenv
2
from charmhelpers.core import templating
4
from charmhelpers.core.services.base import ManagerCallback
7
__all__ = ['RelationContext', 'TemplateCallback',
8
'render_template', 'template']
11
class RelationContext(dict):
13
Base class for a context generator that gets relation data from juju.
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).
22
The generated context will be namespaced under the interface type, to prevent
23
potential naming conflicts.
29
def __init__(self, *args, **kwargs):
30
super(RelationContext, self).__init__(*args, **kwargs)
35
Returns True if all of the required_keys are available.
37
return self.is_ready()
39
__nonzero__ = __bool__
42
return super(RelationContext, self).__repr__()
46
Returns True if all of the `required_keys` are available from any units.
48
ready = len(self.get(self.name, [])) > 0
50
hookenv.log('Incomplete relation: {}'.format(self.__class__.__name__), hookenv.DEBUG)
53
def _is_ready(self, unit_data):
55
Helper method that tests a set of relation data and returns True if
56
all of the `required_keys` are present.
58
return set(unit_data.keys()).issuperset(set(self.required_keys))
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.
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'.
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,
78
{% for unit in interface -%}
79
{{ unit['key'] }}{% if not loop.last %},{% endif %}
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
87
if not hookenv.relation_ids(self.name):
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):
97
def provide_data(self):
99
Return data to be relation_set for this interface.
104
class TemplateCallback(ManagerCallback):
106
Callback class that will render a template, for use as a ready action.
108
def __init__(self, source, target, owner='root', group='root', perms=0444):
115
def __call__(self, manager, service_name, event_name):
116
service = manager.get_service(service_name)
118
for ctx in service.get('required_data', []):
120
templating.render(self.source, self.target, context,
121
self.owner, self.group, self.perms)
124
# Convenience aliases for templates
125
render_template = template = TemplateCallback