Chris Masone | 9807bd6 | 2012-07-11 14:44:17 -0700 | [diff] [blame^] | 1 | # Copyright (c) 2012 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 | import logging |
| 6 | |
| 7 | import common |
| 8 | from autotest_lib.client.common_lib import error |
| 9 | from autotest_lib.server.cros import frontend_wrappers |
| 10 | |
| 11 | """HostLockManager class, for the dynamic_suite module. |
| 12 | |
| 13 | A HostLockManager instance manages locking and unlocking a set of |
| 14 | autotest DUTs. Once primed with a given set, it is bound to that set |
| 15 | for the remainder of its lifetime and cannot be repurposed. If the |
| 16 | caller fails to unlock() locked hosts before the instance is destroyed, |
| 17 | it will attempt to unlock() the hosts automatically, but this is to be |
| 18 | avoided. |
| 19 | |
| 20 | Usage: |
| 21 | manager = host_lock_manager.HostLockManager() |
| 22 | try: |
| 23 | manager.prime(['host1']) |
| 24 | manager.lock() |
| 25 | # do things |
| 26 | finally: |
| 27 | manager.unlock() |
| 28 | """ |
| 29 | |
| 30 | class HostLockManager(object): |
| 31 | """ |
| 32 | @var _afe: an instance of AFE as defined in server/frontend.py. |
| 33 | @var _hosts: an iterable of DUT hostnames. |
| 34 | @var _hosts_are_locked: whether we believe the hosts are locked |
| 35 | """ |
| 36 | |
| 37 | |
| 38 | def __init__(self, afe=None): |
| 39 | """ |
| 40 | Constructor |
| 41 | |
| 42 | @param afe: an instance of AFE as defined in server/frontend.py. |
| 43 | """ |
| 44 | self._afe = afe or frontend_wrappers.RetryingAFE(timeout_min=30, |
| 45 | delay_sec=10, |
| 46 | debug=False) |
| 47 | self._hosts = None |
| 48 | self._hosts_are_locked = False |
| 49 | |
| 50 | |
| 51 | def __del__(self): |
| 52 | if self._hosts_are_locked: |
| 53 | logging.error('Caller failed to unlock %r! ' |
| 54 | 'Forcing unlock now.' % self._hosts) |
| 55 | self.unlock() |
| 56 | |
| 57 | |
| 58 | def prime(self, hosts): |
| 59 | """Permanently associate this instance with |hosts|. |
| 60 | |
| 61 | @param hosts: iterable of hostnames to take over locking/unlocking. |
| 62 | @raise error.HostLockManagerReuse if already prime()d. |
| 63 | """ |
| 64 | if not self._hosts: |
| 65 | self._hosts = frozenset(hosts) |
| 66 | else: |
| 67 | raise error.HostLockManagerReuse( |
| 68 | 'Already primed with %r.' % self._hosts) |
| 69 | |
| 70 | |
| 71 | def lock(self): |
| 72 | """Lock all DUTs in self._hosts.""" |
| 73 | self._host_modifier(locked=True) |
| 74 | self._hosts_are_locked = True |
| 75 | |
| 76 | |
| 77 | def unlock(self): |
| 78 | """Unlock all DUTs in self._hosts.""" |
| 79 | self._host_modifier(locked=False) |
| 80 | self._hosts_are_locked = False |
| 81 | |
| 82 | |
| 83 | def _host_modifier(self, **kwargs): |
| 84 | """Helper that runs the modify_host() RPC with specified args. |
| 85 | |
| 86 | Passes kwargs through to the RPC directly. |
| 87 | """ |
| 88 | self._afe.run('modify_hosts', |
| 89 | host_filter_data={'hostname__in': self._hosts}, |
| 90 | update_data=kwargs) |