~hazmat/pyjuju/proposed-support

« back to all changes in this revision

Viewing changes to juju/hooks/invoker.py

  • Committer: kapil.thangavelu at canonical
  • Date: 2012-05-22 22:08:15 UTC
  • mfrom: (484.1.53 trunk)
  • Revision ID: kapil.thangavelu@canonical.com-20120522220815-acyt8m89i9ybe0w1
merge trunk

Show diffs side-by-side

added added

removed removed

Lines of Context:
6
6
from twisted.python.failure import Failure
7
7
 
8
8
from juju import errors
 
9
from juju.state.errors import RelationStateNotFound
 
10
from juju.state.hook import RelationHookContext
9
11
 
10
12
 
11
13
class HookProtocol(protocol.ProcessProtocol):
47
49
        """
48
50
        exit_code = reason.value.exitCode
49
51
        if exit_code == 0:
50
 
            deferred.callback(exit_code)
 
52
            return deferred.callback(exit_code)
51
53
        elif exit_code == None and reason.value.signal:
52
54
            error = errors.CharmInvocationError(
53
55
                self._hook_name, exit_code, signal=reason.value.signal)
64
66
        self._process_reason(reason, self.ended)
65
67
 
66
68
 
67
 
class FormatItems(object):
68
 
    """Wrapper to delay executing __str_ of items until formatted in log."""
69
 
    def __init__(self, items):
70
 
        self.items = items
 
69
class FormatSettingChanges(object):
 
70
    """Wrapper to delay executing __str_ of changes until written, if at all.
 
71
 
 
72
    :param list changes: Each change is a pair (`relation_ident`,
 
73
       `item`), where `item` may be an `AddedItem`, `DeletedItem`, or
 
74
       `ModifiedItem`. If `relation_ident` is None, this implies that
 
75
       it is a setting on the implied (or parent) context; it is
 
76
       sorted first and the relation_ident for the implied context is
 
77
       not logged.
 
78
    """
 
79
    def __init__(self, changes):
 
80
        self.changes = changes
71
81
 
72
82
    def __str__(self):
73
 
        items = sorted(self.items, key=lambda item: item.key)
74
 
        return "\n".join("    %s" % str(item) for item in items)
 
83
        changes = sorted(
 
84
            self.changes,
 
85
            key=lambda (relation_ident, item): (relation_ident, item.key))
 
86
        lines = []
 
87
        for relation_ident, item in changes:
 
88
            if relation_ident is None:
 
89
                lines.append("    %s" % str(item))
 
90
            else:
 
91
                lines.append("    %s on %r" % (str(item), relation_ident))
 
92
        return "\n".join(lines)
75
93
 
76
94
 
77
95
class Invoker(object):
124
142
        """
125
143
        self.environment = {}
126
144
        self._context = context
 
145
        self._relation_contexts = {}
127
146
        self._change = change
128
147
        self._client_id = client_id
129
148
        self._socket_path = socket_path
143
162
        # properly terminated with loseConnection
144
163
        self._reaper = None
145
164
 
 
165
        # Add the initial context to the relation contexts if it's in
 
166
        # fact such
 
167
        if isinstance(context, RelationHookContext):
 
168
            self._relation_contexts[context.relation_ident] = context
 
169
 
 
170
    @inlineCallbacks
 
171
    def start(self):
 
172
        """Cache relation hook contexts for all relation idents."""
 
173
        # Get all relation idents (None means "all")
 
174
        relation_idents = set((yield self.get_relation_idents(None)))
 
175
        if isinstance(self._context, RelationHookContext):
 
176
            # Exclude the parent context for being looked up as a child
 
177
            relation_idents.discard(self._context.relation_ident)
 
178
            display_parent_relation_ident = " on %r" % \
 
179
                self._context.relation_ident
 
180
        else:
 
181
            display_parent_relation_ident = ""
 
182
        for relation_ident in relation_idents:
 
183
            child = yield self._context.get_relation_hook_context(
 
184
                relation_ident)
 
185
            self._relation_contexts[relation_ident] = child
 
186
        self._log.debug("Cached relation hook contexts%s: %r" % (
 
187
                display_parent_relation_ident,
 
188
                sorted(relation_idents)))
 
189
 
146
190
    @property
147
191
    def ended(self):
148
192
        return self._ended
180
224
        """Returns the hook context for the invocation."""
181
225
        return self._context
182
226
 
 
227
    def get_relation_hook_context(self, relation_ident):
 
228
        """Returns a hook context corresponding to `relation_ident`"""
 
229
        try:
 
230
            return self._relation_contexts[relation_ident]
 
231
        except KeyError:
 
232
            raise RelationStateNotFound()
 
233
 
 
234
    def get_relation_idents(self, relation_name):
 
235
        return self._context.get_relation_idents(relation_name)
 
236
 
183
237
    def validate_hook(self, hook_filename):
184
238
        """Verify that the hook_filename exists and is executable. """
185
239
        if not os.path.exists(hook_filename):
252
306
 
253
307
        # Flush context changes back to zookeeper if hook was successful.
254
308
        if result == 0 and self._context:
255
 
            relation_setting_changes = yield self._context.flush()
 
309
            relation_setting_changes = []
 
310
            for context in self._relation_contexts.itervalues():
 
311
                changes = yield context.flush()
 
312
                if changes:
 
313
                    for change in changes:
 
314
                        if context is self._context:
 
315
                            relation_setting_changes.append((None, change))
 
316
                        else:
 
317
                            # Only log relation idents for relation settings
 
318
                            # on child relation hook contexts
 
319
                            relation_setting_changes.append(
 
320
                                (context.relation_ident, change))
256
321
            if relation_setting_changes:
 
322
                if hasattr(self._context, "relation_ident"):
 
323
                    display_parent_relation_ident = " on %r" % \
 
324
                        self._context.relation_ident
 
325
                else:
 
326
                    display_parent_relation_ident = ""
257
327
                self._log.debug(
258
 
                    "Flushed values for hook %r\n%s",
 
328
                    "Flushed values for hook %r%s\n%s",
259
329
                    os.path.basename(hook),
260
 
                    FormatItems(relation_setting_changes))
 
330
                    display_parent_relation_ident,
 
331
                    FormatSettingChanges(relation_setting_changes))
261
332
 
262
333
        returnValue(result)
263
334