blob: 67c020108d03c1fab367eec8fa8f37b136489995 [file] [log] [blame]
J. Richard Barnettec542c432016-02-12 11:29:34 -08001# Copyright 2016 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""
J. Richard Barnette55228612016-02-16 17:38:06 -08006Framework for host verification and repair in Autotest.
J. Richard Barnettec542c432016-02-12 11:29:34 -08007
J. Richard Barnette55228612016-02-16 17:38:06 -08008The framework provides implementation code in support of `Host.verify()`
9and `Host.repair()` used in Verify and Repair special tasks.
J. Richard Barnettec542c432016-02-12 11:29:34 -080010
11The framework consists of these classes:
12 * `Verifier`: A class representing a single verification check.
J. Richard Barnette55228612016-02-16 17:38:06 -080013 * `RepairAction`: A class representing a repair operation that can fix
14 a failed verification check.
15 * `RepairStrategy`: A class for organizing a collection of `Verifier`
16 and `RepairAction` instances, and invoking them in order.
J. Richard Barnettec542c432016-02-12 11:29:34 -080017
J. Richard Barnette55228612016-02-16 17:38:06 -080018Individual operations during verification and repair are handled by
19instances of `Verifier` and `RepairAction`. `Verifier` objects are
20meant to test for specific conditions that may cause tests to fail.
21`RepairAction` objects provide operations designed to fix one or
22more failures identified by a `Verifier` object.
J. Richard Barnettec542c432016-02-12 11:29:34 -080023"""
24
Richard Barnettef6579932016-08-16 17:07:03 -070025import collections
J. Richard Barnettec542c432016-02-12 11:29:34 -080026import logging
27
28import common
29from autotest_lib.client.common_lib import error
30
31
J. Richard Barnettecfe6b0d2016-03-17 11:39:12 -070032class AutoservVerifyError(error.AutoservError):
J. Richard Barnettec542c432016-02-12 11:29:34 -080033 """
34 Generic Exception for failures from `Verifier` objects.
35
36 Instances of this exception can be raised when a `verify()`
37 method fails, if no more specific exception is available.
38 """
39 pass
40
41
Richard Barnettef6579932016-08-16 17:07:03 -070042_DependencyFailure = collections.namedtuple(
43 '_DependencyFailure', ('dependency', 'error'))
44
45
J. Richard Barnettecfe6b0d2016-03-17 11:39:12 -070046class AutoservVerifyDependencyError(error.AutoservError):
J. Richard Barnettec542c432016-02-12 11:29:34 -080047 """
48 Exception raised for failures in dependencies.
49
50 This exception is used to distinguish an original failure from a
51 failure being passed back from a verification dependency. That is,
52 if 'B' depends on 'A', and 'A' fails, 'B' will raise this exception
53 to signal that the original failure is further down the dependency
54 chain.
55
Richard Barnettef6579932016-08-16 17:07:03 -070056 The `failures` argument to the constructor for this class is a set
57 of instances of `_DependencyFailure`, each corresponding to one
58 failed dependency:
59 * The `dependency` attribute of each failure is the description
60 of the failed dependency.
61 * The `error` attribute of each failure is the string value of
62 the exception from the failed dependency.
J. Richard Barnettec542c432016-02-12 11:29:34 -080063
Richard Barnettef6579932016-08-16 17:07:03 -070064 Multiple methods in this module recognize and handle this exception
J. Richard Barnettec542c432016-02-12 11:29:34 -080065 specially.
Richard Barnettef6579932016-08-16 17:07:03 -070066
67 @property failures Set of failures passed to the constructor.
68 @property _node Instance of `_DependencyNode` reporting the
69 failed dependencies.
J. Richard Barnettec542c432016-02-12 11:29:34 -080070 """
Richard Barnettef6579932016-08-16 17:07:03 -070071 def __init__(self, node, failures):
72 """
73 Constructor for `AutoservVerifyDependencyError`.
74
75 @param node Instance of _DependencyNode reporting the
76 failed dependencies.
77 @param failures List of failure tuples as described above.
78 """
79 super(AutoservVerifyDependencyError, self).__init__(
80 '\n'.join([f.error for f in failures]))
81 self.failures = failures
82 self._node = node
83
84
85 def log_dependencies(self, action, deps):
86 """
87 Log an `AutoservVerifyDependencyError`.
88
89 This writes a short summary of the dependency failures captured
90 in this exception, using standard Python logging.
91
92 The passed in `action` string plus `self._node.description`
93 are logged at INFO level. The `action` argument should
94 introduce or describe an action relative to `self._node`.
95
96 The passed in `deps` string and the description of each failed
97 dependency in `self` are be logged at DEBUG level. The `deps`
98 argument is used to introduce the various failed dependencies.
99
100 @param action A string mentioning the action being logged
101 relative to `self._node`.
102 @param deps A string introducing the dependencies that
103 failed.
104 """
105 logging.info('%s: %s', action, self._node.description)
106 logging.debug('%s:', deps)
107 for failure in self.failures:
108 logging.debug(' %s', failure.dependency)
J. Richard Barnettec542c432016-02-12 11:29:34 -0800109
110
J. Richard Barnette55228612016-02-16 17:38:06 -0800111class AutoservRepairError(error.AutoservError):
J. Richard Barnettec542c432016-02-12 11:29:34 -0800112 """
J. Richard Barnette55228612016-02-16 17:38:06 -0800113 Generic Exception for failures from `RepairAction` objects.
J. Richard Barnettec542c432016-02-12 11:29:34 -0800114
J. Richard Barnette55228612016-02-16 17:38:06 -0800115 Instances of this exception can be raised when a `repair()`
116 method fails, if no more specific exception is available.
117 """
118 pass
J. Richard Barnettec542c432016-02-12 11:29:34 -0800119
J. Richard Barnettec542c432016-02-12 11:29:34 -0800120
J. Richard Barnette55228612016-02-16 17:38:06 -0800121class _DependencyNode(object):
122 """
123 An object that can depend on verifiers.
J. Richard Barnettec542c432016-02-12 11:29:34 -0800124
J. Richard Barnette55228612016-02-16 17:38:06 -0800125 Both repair and verify operations have the notion of dependencies
126 that must pass before the operation proceeds. This class captures
127 the shared behaviors required by both classes.
J. Richard Barnettec542c432016-02-12 11:29:34 -0800128
129 @property tag Short identifier to be used in logging.
J. Richard Barnette55228612016-02-16 17:38:06 -0800130 @property description Text summary of this node's action, to be
131 used in debug logs.
J. Richard Barnettec542c432016-02-12 11:29:34 -0800132 @property _dependency_list Dependency pre-requisites.
133 """
134
J. Richard Barnettea4784062016-03-04 14:14:10 -0800135 def __init__(self, tag, dependencies):
J. Richard Barnettec542c432016-02-12 11:29:34 -0800136 self._dependency_list = dependencies
J. Richard Barnettea4784062016-03-04 14:14:10 -0800137 self._tag = tag
J. Richard Barnettec542c432016-02-12 11:29:34 -0800138
139
Richard Barnettef6579932016-08-16 17:07:03 -0700140 def _verify_list(self, host, verifiers):
J. Richard Barnettec542c432016-02-12 11:29:34 -0800141 """
142 Test a list of verifiers against a given host.
143
144 This invokes `_verify_host()` on every verifier in the given
145 list. If any verifier in the transitive closure of dependencies
J. Richard Barnettecfe6b0d2016-03-17 11:39:12 -0700146 in the list fails, an `AutoservVerifyDependencyError` is raised
J. Richard Barnettec542c432016-02-12 11:29:34 -0800147 containing the description of each failed verifier. Only
148 original failures are reported; verifiers that don't run due
149 to a failed dependency are omitted.
150
151 By design, original failures are logged once in `_verify_host()`
152 when `verify()` originally fails. The additional data gathered
153 here is for the debug logs to indicate why a subsequent
154 operation never ran.
155
156 @param host The host to be tested against the verifiers.
157 @param verifiers List of verifiers to be checked.
158
J. Richard Barnettecfe6b0d2016-03-17 11:39:12 -0700159 @raises AutoservVerifyDependencyError Raised when at least
J. Richard Barnettec542c432016-02-12 11:29:34 -0800160 one verifier in the list has failed.
161 """
J. Richard Barnette2654c552016-03-01 17:26:35 -0800162 failures = set()
J. Richard Barnettec542c432016-02-12 11:29:34 -0800163 for v in verifiers:
164 try:
165 v._verify_host(host)
J. Richard Barnettecfe6b0d2016-03-17 11:39:12 -0700166 except AutoservVerifyDependencyError as e:
Richard Barnettef6579932016-08-16 17:07:03 -0700167 failures.update(e.failures)
J. Richard Barnettec542c432016-02-12 11:29:34 -0800168 except Exception as e:
Richard Barnettef6579932016-08-16 17:07:03 -0700169 failures.add(_DependencyFailure(v.description, str(e)))
J. Richard Barnettec542c432016-02-12 11:29:34 -0800170 if failures:
Richard Barnettef6579932016-08-16 17:07:03 -0700171 raise AutoservVerifyDependencyError(self, failures)
J. Richard Barnette55228612016-02-16 17:38:06 -0800172
173
174 def _verify_dependencies(self, host):
175 """
176 Verify that all of this node's dependencies pass for a host.
177
178 @param host The host to be verified.
179 """
180 try:
181 self._verify_list(host, self._dependency_list)
182 except AutoservVerifyDependencyError as e:
Richard Barnettef6579932016-08-16 17:07:03 -0700183 e.log_dependencies(
184 'Skipping this operation',
185 'The following dependencies failed')
J. Richard Barnette55228612016-02-16 17:38:06 -0800186 raise
187
188
189 @property
190 def tag(self):
191 """
192 Tag for use in logging status records.
193
194 This is a property with a short string used to identify the node
195 in the 'status.log' file and during node construction. The tag
196 should contain only letters, digits, and '_' characters. This
197 tag is not used alone, but is combined with other identifiers,
198 based on the operation being logged.
199
200 @return A short identifier-like string.
201 """
202 return self._tag
203
204
205 @property
206 def description(self):
207 """
208 Text description of this node for log messages.
209
210 This string will be logged with failures, and should describe
211 the condition required for success.
212
213 N.B. Subclasses are required to override this method, but we
214 _don't_ raise NotImplementedError here. Various methods fail in
215 inscrutable ways if this method raises any exception, so for
216 debugging purposes, it's better to return a default value.
217
218 @return A descriptive string.
219 """
220 return ('Class %s fails to implement description().' %
221 type(self).__name__)
222
223
224class Verifier(_DependencyNode):
225 """
226 Abstract class embodying one verification check.
227
228 A concrete subclass of `Verifier` provides a simple check that can
229 determine a host's fitness for testing. Failure indicates that the
230 check found a problem that can cause at least one test to fail.
231
232 `Verifier` objects are organized in a DAG identifying dependencies
233 among operations. The DAG controls ordering and prevents wasted
234 effort: If verification operation V2 requires that verification
235 operation V1 pass, then a) V1 will run before V2, and b) if V1
236 fails, V2 won't run at all. The `_verify_host()` method ensures
237 that all dependencies run and pass before invoking the `verify()`
238 method.
239
240 A `Verifier` object caches its result the first time it calls
241 `verify()`. Subsequent calls return the cached result, without
242 re-running the check code. The `_reverify()` method clears the
243 cached result in the current node, and in all dependencies.
244
245 Subclasses must supply these properties and methods:
246 * `verify()`: This is the method to perform the actual
247 verification check.
248 * `description`: A one-line summary of the verification check for
249 debug log messages.
250
251 Subclasses must override all of the above attributes; subclasses
252 should not override or extend any other attributes of this class.
253
254 The description string should be a simple sentence explaining what
255 must be true for the verifier to pass. Do not include a terminating
256 period. For example:
257
258 Host is available via ssh
259
260 The base class manages the following private data:
261 * `_result`: The cached result of verification.
262 * `_dependency_list`: The list of dependencies.
263 Subclasses should not use these attributes.
264
265 @property _result Cached result of verification.
266 """
267
268 def __init__(self, tag, dependencies):
269 super(Verifier, self).__init__(tag, dependencies)
270 self._result = None
271 self._verify_tag = 'verify.' + self.tag
272
273
J. Richard Barnettec542c432016-02-12 11:29:34 -0800274 def _reverify(self):
275 """
276 Discard cached verification results.
277
278 Reset the cached verification result for this node, and for the
279 transitive closure of all dependencies.
280 """
281 if self._result is not None:
282 self._result = None
283 for v in self._dependency_list:
284 v._reverify()
285
286
287 def _verify_host(self, host):
288 """
289 Determine the result of verification, and log results.
290
291 If this verifier does not have a cached verification result,
292 check dependencies, and if they pass, run `verify()`. Log
293 informational messages regarding failed dependencies. If we
294 call `verify()`, log the result in `status.log`.
295
296 If we already have a cached result, return that result without
297 logging any message.
298
299 @param host The host to be tested for a problem.
300 """
301 if self._result is not None:
302 if isinstance(self._result, Exception):
303 raise self._result # cached failure
304 elif self._result:
305 return # cached success
306 self._result = False
J. Richard Barnette55228612016-02-16 17:38:06 -0800307 self._verify_dependencies(host)
J. Richard Barnettec542c432016-02-12 11:29:34 -0800308 logging.info('Verifying this condition: %s', self.description)
309 try:
310 self.verify(host)
J. Richard Barnette55228612016-02-16 17:38:06 -0800311 host.record('GOOD', None, self._verify_tag)
J. Richard Barnettec542c432016-02-12 11:29:34 -0800312 except Exception as e:
313 logging.exception('Failed: %s', self.description)
314 self._result = e
J. Richard Barnette55228612016-02-16 17:38:06 -0800315 host.record('FAIL', None, self._verify_tag, str(e))
J. Richard Barnettec542c432016-02-12 11:29:34 -0800316 raise
317 self._result = True
318
319
320 def verify(self, host):
321 """
322 Unconditionally perform a verification check.
323
324 This method is responsible for testing for a single problem on a
325 host. Implementations should follow these guidelines:
326 * The check should find a problem that will cause testing to
327 fail.
328 * Verification checks on a working system should run quickly
329 and should be optimized for success; a check that passes
330 should finish within seconds.
331 * Verification checks are not expected have side effects, but
332 may apply trivial fixes if they will finish within the time
333 constraints above.
334
335 A verification check should normally trigger a single set of
336 repair actions. If two different failures can require two
337 different repairs, ideally they should use two different
338 subclasses of `Verifier`.
339
340 Implementations indicate failure by raising an exception. The
341 exception text should be a short, 1-line summary of the error.
342 The text should be concise and diagnostic, as it will appear in
343 `status.log` files.
344
345 If this method finds no problems, it returns without raising any
346 exception.
347
348 Implementations should avoid most logging actions, but can log
349 DEBUG level messages if they provide significant information for
350 diagnosing failures.
351
352 @param host The host to be tested for a problem.
353 """
354 raise NotImplementedError('Class %s does not implement '
355 'verify()' % type(self).__name__)
356
357
J. Richard Barnette55228612016-02-16 17:38:06 -0800358class RepairAction(_DependencyNode):
359 """
360 Abstract class embodying one repair procedure.
361
362 A `RepairAction` is responsible for fixing one or more failed
363 `Verifier` checks, in order to make those checks pass.
364
365 Each repair action includes one or more verifier triggers that
366 determine when the repair action should run. A repair action
367 will call its `repair()` method if one or more of its triggers
368 fails. A repair action is successful if all of its triggers pass
369 after calling `repair()`.
370
371 A `RepairAction` is a subclass of `_DependencyNode`; if any of a
372 repair action's dependencies fail, the action does not check its
373 triggers, and doesn't call `repair()`.
374
375 Subclasses must supply these attributes:
376 * `repair()`: This is the method to perform the necessary
377 repair. The method should avoid most logging actions, but
378 can log DEBUG level messages if they provide significant
379 information for diagnosing failures.
380 * `description`: A one-line summary of the repair action for
381 debug log messages.
382
383 Subclasses must override both of the above attributes and should
384 not override any other attributes of this class.
385
386 The description string should be a simple sentence explaining the
387 operation that will be performed. Do not include a terminating
388 period. For example:
389
390 Re-install the stable build via AU
391
392 @property _trigger_list List of verification checks that will
393 trigger this repair when they fail.
394 """
395
396 def __init__(self, tag, dependencies, triggers):
397 super(RepairAction, self).__init__(tag, dependencies)
398 self._trigger_list = triggers
399 self._repair_tag = 'repair.' + self.tag
400
401
402 def _repair_host(self, host):
J. Richard Barnettec542c432016-02-12 11:29:34 -0800403 """
J. Richard Barnette55228612016-02-16 17:38:06 -0800404 Apply this repair action if any triggers fail.
J. Richard Barnettec542c432016-02-12 11:29:34 -0800405
J. Richard Barnette55228612016-02-16 17:38:06 -0800406 Repair is triggered when all dependencies are successful, and at
407 least one trigger fails.
J. Richard Barnettec542c432016-02-12 11:29:34 -0800408
J. Richard Barnette55228612016-02-16 17:38:06 -0800409 If the `repair()` method triggers, the success or failure of
410 this operation is logged in `status.log` bracketed by 'START'
411 and 'END' records. Details of whether or why `repair()`
412 triggered are written to the debug logs. If repair doesn't
413 trigger, nothing is logged to `status.log`.
414
415 @param host The host to be repaired.
J. Richard Barnettec542c432016-02-12 11:29:34 -0800416 """
J. Richard Barnette55228612016-02-16 17:38:06 -0800417 self._verify_dependencies(host)
418 try:
419 self._verify_list(host, self._trigger_list)
420 except AutoservVerifyDependencyError as e:
Richard Barnettef6579932016-08-16 17:07:03 -0700421 e.log_dependencies(
422 'Attempting this repair action',
Richard Barnette121239d2016-08-25 14:22:05 -0700423 'Repairing because these triggers failed')
J. Richard Barnette55228612016-02-16 17:38:06 -0800424 host.record('START', None, self._repair_tag)
425 try:
426 self.repair(host)
427 except Exception as e:
428 logging.exception('Repair failed: %s', self.description)
429 host.record('FAIL', None, self._repair_tag, str(e))
430 host.record('END FAIL', None, self._repair_tag)
431 raise
432 try:
433 for v in self._trigger_list:
434 v._reverify()
435 self._verify_list(host, self._trigger_list)
436 host.record('END GOOD', None, self._repair_tag)
437 except AutoservVerifyDependencyError as e:
Richard Barnettef6579932016-08-16 17:07:03 -0700438 e.log_dependencies(
439 'This repair action reported success',
440 'However, these triggers still fail')
J. Richard Barnette55228612016-02-16 17:38:06 -0800441 host.record('END FAIL', None, self._repair_tag)
442 raise AutoservRepairError(
443 'Some verification checks still fail')
444 except Exception:
445 # The specification for `self._verify_list()` says
446 # that this can't happen; this is a defensive
447 # precaution.
448 host.record('END FAIL', None, self._repair_tag,
449 'Internal error in repair')
450 raise
451 else:
452 logging.info('No failed triggers, skipping repair: %s',
453 self.description)
J. Richard Barnettec542c432016-02-12 11:29:34 -0800454
455
J. Richard Barnette55228612016-02-16 17:38:06 -0800456 def repair(self, host):
J. Richard Barnettec542c432016-02-12 11:29:34 -0800457 """
J. Richard Barnette55228612016-02-16 17:38:06 -0800458 Apply this repair action to the given host.
J. Richard Barnettec542c432016-02-12 11:29:34 -0800459
J. Richard Barnette55228612016-02-16 17:38:06 -0800460 This method is responsible for applying changes to fix failures
461 in one or more verification checks. The repair is considered
462 successful if the DUT passes the specific checks after this
463 method completes.
J. Richard Barnettec542c432016-02-12 11:29:34 -0800464
J. Richard Barnette55228612016-02-16 17:38:06 -0800465 Implementations indicate failure by raising an exception. The
466 exception text should be a short, 1-line summary of the error.
467 The text should be concise and diagnostic, as it will appear in
468 `status.log` files.
J. Richard Barnettec542c432016-02-12 11:29:34 -0800469
J. Richard Barnette55228612016-02-16 17:38:06 -0800470 If this method completes successfully, it returns without
471 raising any exception.
472
473 Implementations should avoid most logging actions, but can log
474 DEBUG level messages if they provide significant information for
475 diagnosing failures.
476
477 @param host The host to be repaired.
J. Richard Barnettec542c432016-02-12 11:29:34 -0800478 """
J. Richard Barnette55228612016-02-16 17:38:06 -0800479 raise NotImplementedError('Class %s does not implement '
480 'repair()' % type(self).__name__)
J. Richard Barnettec542c432016-02-12 11:29:34 -0800481
482
483class _RootVerifier(Verifier):
484 """
485 Utility class used by `RepairStrategy`.
486
487 A node of this class by itself does nothing; it always passes (if it
488 can run). This class exists merely to be the root of a DAG of
489 dependencies in an instance of `RepairStrategy`.
490 """
491
J. Richard Barnettec542c432016-02-12 11:29:34 -0800492 def verify(self, host):
493 pass
494
495
496 @property
J. Richard Barnettec542c432016-02-12 11:29:34 -0800497 def description(self):
498 return 'All host verification checks pass'
499
500
501
502class RepairStrategy(object):
503 """
J. Richard Barnette55228612016-02-16 17:38:06 -0800504 A class for organizing `Verifier` and `RepairAction` objects.
J. Richard Barnettec542c432016-02-12 11:29:34 -0800505
J. Richard Barnette2654c552016-03-01 17:26:35 -0800506 An instance of `RepairStrategy` is organized as a DAG of `Verifier`
J. Richard Barnette55228612016-02-16 17:38:06 -0800507 objects, plus a list of `RepairAction` objects. The class provides
508 methods for invoking those objects in the required order, when
509 needed:
510 * The `verify()` method walks the verifier DAG in dependency
511 order.
512 * The `repair()` method invokes the repair actions in list order.
513 Each repair action will invoke its dependencies and triggers as
514 needed.
J. Richard Barnette2654c552016-03-01 17:26:35 -0800515
J. Richard Barnette55228612016-02-16 17:38:06 -0800516 # The Verifier DAG
517 The verifier DAG is constructed from the first argument passed to
518 the passed to the `RepairStrategy` constructor. That argument is an
519 iterable consisting of three-element tuples in the form
520 `(constructor, tag, deps)`:
J. Richard Barnette2654c552016-03-01 17:26:35 -0800521 * The `constructor` value is a callable that creates a `Verifier`
J. Richard Barnette55228612016-02-16 17:38:06 -0800522 as for the interface of the class constructor. For classes
J. Richard Barnette2654c552016-03-01 17:26:35 -0800523 that inherit the default constructor from `Verifier`, this can
524 be the class itself.
J. Richard Barnettea4784062016-03-04 14:14:10 -0800525 * The `tag` value is the tag to be associated with the constructed
526 verifier.
J. Richard Barnette2654c552016-03-01 17:26:35 -0800527 * The `deps` value is an iterable (e.g. list or tuple) of strings.
528 Each string corresponds to the `tag` member of a `Verifier`
529 dependency.
530
531 The tag names of verifiers in the constructed DAG must all be
J. Richard Barnette55228612016-02-16 17:38:06 -0800532 unique. The tag name defined by `RepairStrategy.ROOT_TAG` is
533 reserved and may not be used by any verifier.
J. Richard Barnette2654c552016-03-01 17:26:35 -0800534
535 In the input data for the constructor, dependencies must appear
J. Richard Barnettea4784062016-03-04 14:14:10 -0800536 before the nodes that depend on them. Thus:
J. Richard Barnette2654c552016-03-01 17:26:35 -0800537
J. Richard Barnettea4784062016-03-04 14:14:10 -0800538 ((A, 'a', ()), (B, 'b', ('a',))) # This is valid
539 ((B, 'b', ('a',)), (A, 'a', ())) # This will fail!
J. Richard Barnette2654c552016-03-01 17:26:35 -0800540
541 Internally, the DAG of verifiers is given unique root node. So,
542 given this input:
543
J. Richard Barnettea4784062016-03-04 14:14:10 -0800544 ((C, 'c', ()),
545 (A, 'a', ('c',)),
546 (B, 'b', ('c',)))
J. Richard Barnette2654c552016-03-01 17:26:35 -0800547
548 The following DAG is constructed:
549
550 Root
551 / \
552 A B
553 \ /
554 C
555
556 Since nothing depends on `A` or `B`, the root node guarantees that
557 these two verifiers will both be called and properly logged.
558
J. Richard Barnette55228612016-02-16 17:38:06 -0800559 The root node is not directly accessible; however repair actions can
560 trigger on it by using `RepairStrategy.ROOT_TAG`. Additionally, the
561 node will be logged in `status.log` whenever `verify()` succeeds.
562
563 # The Repair Actions List
564 The list of repair actions is constructed from the second argument
565 passed to the passed to the `RepairStrategy` constructor. That
566 argument is an iterable consisting of four-element tuples in the
567 form `(constructor, tag, deps, triggers)`:
568 * The `constructor` value is a callable that creates a
569 `RepairAction` as for the interface of the class constructor.
570 For classes that inherit the default constructor from
571 `RepairAction`, this can be the class itself.
572 * The `tag` value is the tag to be associated with the constructed
573 repair action.
574 * The `deps` value is an iterable (e.g. list or tuple) of strings.
575 Each string corresponds to the `tag` member of a `Verifier` that
576 the repair action depends on.
577 * The `triggers` value is an iterable (e.g. list or tuple) of
578 strings. Each string corresponds to the `tag` member of a
579 `Verifier` that can trigger the repair action.
580
581 `RepairStrategy` deps and triggers can only refer to verifiers,
582 not to other repair actions.
J. Richard Barnettec542c432016-02-12 11:29:34 -0800583 """
584
J. Richard Barnettea4784062016-03-04 14:14:10 -0800585 # This name is reserved; clients may not use it.
J. Richard Barnette55228612016-02-16 17:38:06 -0800586 ROOT_TAG = 'PASS'
J. Richard Barnette2654c552016-03-01 17:26:35 -0800587
J. Richard Barnette55228612016-02-16 17:38:06 -0800588 @staticmethod
589 def _add_verifier(verifiers, constructor, tag, dep_tags):
590 """
591 Construct and remember a verifier.
592
593 Create a `Verifier` using `constructor` and `tag`. Dependencies
594 for construction are found by looking up `dep_tags` in the
595 `verifiers` dictionary.
596
597 After construction, the new verifier is added to `verifiers`.
598
599 @param verifiers Dictionary of verifiers, indexed by tag.
600 @param constructor Verifier construction function.
601 @param tag Tag parameter for the construction function.
602 @param dep_tags Tags of dependencies for the constructor, to
603 be found in `verifiers`.
604 """
605 assert tag not in verifiers
606 deps = [verifiers[d] for d in dep_tags]
607 verifiers[tag] = constructor(tag, deps)
608
609
610 def __init__(self, verifier_data, repair_data):
J. Richard Barnettec542c432016-02-12 11:29:34 -0800611 """
612 Construct a `RepairStrategy` from simplified DAG data.
613
614 The input `verifier_data` object describes how to construct
615 verify nodes and the dependencies that relate them, as detailed
J. Richard Barnette2654c552016-03-01 17:26:35 -0800616 above.
J. Richard Barnettec542c432016-02-12 11:29:34 -0800617
J. Richard Barnette55228612016-02-16 17:38:06 -0800618 The input `repair_data` object describes how to construct repair
619 actions and their dependencies and triggers, as detailed above.
620
J. Richard Barnettec542c432016-02-12 11:29:34 -0800621 @param verifier_data Iterable value with constructors for the
622 elements of the verification DAG and their
623 dependencies.
J. Richard Barnette55228612016-02-16 17:38:06 -0800624 @param repair_data Iterable value with constructors for the
625 elements of the repair action list, and
626 their dependencies and triggers.
J. Richard Barnettec542c432016-02-12 11:29:34 -0800627 """
J. Richard Barnette2654c552016-03-01 17:26:35 -0800628 # We use the `all_verifiers` list to guarantee that our root
629 # verifier will execute its dependencies in the order provided
630 # to us by our caller.
J. Richard Barnettec542c432016-02-12 11:29:34 -0800631 verifier_map = {}
J. Richard Barnette55228612016-02-16 17:38:06 -0800632 all_tags = []
J. Richard Barnette2654c552016-03-01 17:26:35 -0800633 dependencies = set()
J. Richard Barnette55228612016-02-16 17:38:06 -0800634 for constructor, tag, deps in verifier_data:
635 self._add_verifier(verifier_map, constructor, tag, deps)
J. Richard Barnette2654c552016-03-01 17:26:35 -0800636 dependencies.update(deps)
J. Richard Barnette55228612016-02-16 17:38:06 -0800637 all_tags.append(tag)
J. Richard Barnette2654c552016-03-01 17:26:35 -0800638 # Capture all the verifiers that have nothing depending on them.
J. Richard Barnette55228612016-02-16 17:38:06 -0800639 root_tags = [t for t in all_tags if t not in dependencies]
640 self._add_verifier(verifier_map, _RootVerifier,
641 self.ROOT_TAG, root_tags)
642 self._verify_root = verifier_map[self.ROOT_TAG]
643 self._repair_actions = []
644 for constructor, tag, deps, triggers in repair_data:
645 r = constructor(tag,
646 [verifier_map[d] for d in deps],
647 [verifier_map[t] for t in triggers])
648 self._repair_actions.append(r)
J. Richard Barnettec542c432016-02-12 11:29:34 -0800649
650
651 def verify(self, host):
652 """
653 Run the verifier DAG on the given host.
654
655 @param host The target to be verified.
656 """
657 self._verify_root._reverify()
658 self._verify_root._verify_host(host)
J. Richard Barnette55228612016-02-16 17:38:06 -0800659
660
661 def repair(self, host):
662 """
663 Run the repair DAG on the given host.
664
665 @param host The target to be repaired.
666 """
667 self._verify_root._reverify()
668 for ra in self._repair_actions:
669 try:
670 ra._repair_host(host)
671 except Exception as e:
672 # all logging and exception handling was done at
673 # lower levels
674 pass
675 self._verify_root._verify_host(host)