Julius Werner | fe3474b | 2016-03-23 17:40:05 -0700 | [diff] [blame] | 1 | # 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 | |
Gregory Nisbet | 80c31a3 | 2020-07-14 18:07:06 -0700 | [diff] [blame] | 5 | from __future__ import print_function |
| 6 | |
Julius Werner | fe3474b | 2016-03-23 17:40:05 -0700 | [diff] [blame] | 7 | import logging, re |
| 8 | |
| 9 | # http://docs.python.org/2/library/errno.html |
| 10 | import errno |
| 11 | |
| 12 | from autotest_lib.client.common_lib import error |
| 13 | |
| 14 | class WatchdogTester(object): |
| 15 | """Helper class to perform various watchdog tests.""" |
| 16 | |
| 17 | WD_DEV = '/dev/watchdog' |
Salman Qazi | 1f0fee4 | 2020-05-18 15:55:40 -0700 | [diff] [blame] | 18 | DAISYDOG_PATH='/usr/sbin/daisydog' |
Julius Werner | fe3474b | 2016-03-23 17:40:05 -0700 | [diff] [blame] | 19 | def _exists_on_client(self): |
| 20 | return self._client.run('test -c "%s"' % self.WD_DEV, |
Salman Qazi | 1f0fee4 | 2020-05-18 15:55:40 -0700 | [diff] [blame] | 21 | ignore_status=True).exit_status == 0 and \ |
| 22 | self._client.run('test -x "%s"' % self.DAISYDOG_PATH, |
Julius Werner | fe3474b | 2016-03-23 17:40:05 -0700 | [diff] [blame] | 23 | ignore_status=True).exit_status == 0 |
Julius Werner | fe3474b | 2016-03-23 17:40:05 -0700 | [diff] [blame] | 24 | # If daisydog is running, stop it so we can use /dev/watchdog |
| 25 | def _stop_daemon(self): |
| 26 | """If running, stop daisydog so we can use /dev/watchdog.""" |
| 27 | self._client.run('stop daisydog', ignore_status=True) |
| 28 | |
| 29 | def _start_daemon(self): |
| 30 | self._client.run('start daisydog', ignore_status=True) |
| 31 | |
| 32 | def _query_hw_interval(self): |
| 33 | """Check how long the hardware interval is.""" |
| 34 | output = self._client.run('daisydog -c').stdout |
| 35 | secs = re.findall(r'HW watchdog interval is (\d*) seconds', output)[0] |
| 36 | return int(secs) |
| 37 | |
| 38 | def __init__(self, client): |
| 39 | self._client = client |
| 40 | self._supported = self._exists_on_client() |
| 41 | |
| 42 | def is_supported(self): |
| 43 | return self._supported |
| 44 | |
| 45 | def __enter__(self): |
| 46 | self._stop_daemon() |
| 47 | self._hw_interval = self._query_hw_interval() |
| 48 | |
| 49 | def trigger_watchdog(self, timeout=60): |
| 50 | """ |
| 51 | Trigger a watchdog reset by opening the watchdog device but not petting |
| 52 | it. Will ensure the device goes down and comes back up again. |
| 53 | """ |
| 54 | |
| 55 | try: |
| 56 | self._client.run('echo "z" > %s' % self.WD_DEV) |
Gregory Nisbet | 80c31a3 | 2020-07-14 18:07:06 -0700 | [diff] [blame] | 57 | except error.AutoservRunError as e: |
Julius Werner | fe3474b | 2016-03-23 17:40:05 -0700 | [diff] [blame] | 58 | raise error.TestError('write to %s failed (%s)' % |
| 59 | (self.WD_DEV, errno.errorcode[e.errno])) |
| 60 | |
| 61 | logging.info("WatchdogHelper: tickled watchdog on %s (%ds to reboot)", |
| 62 | self._client.hostname, self._hw_interval) |
| 63 | |
| 64 | # machine should became unpingable after lockup |
| 65 | # ...give 5 seconds slack... |
| 66 | wait_down = self._hw_interval + 5 |
| 67 | if not self._client.wait_down(timeout=wait_down): |
| 68 | raise error.TestError('machine should be unpingable ' |
| 69 | 'within %d seconds' % wait_down) |
| 70 | |
| 71 | # make sure the machine comes back, |
| 72 | # DHCP can take up to 45 seconds in odd cases. |
| 73 | if not self._client.wait_up(timeout=timeout): |
| 74 | raise error.TestError('machine did not reboot/ping within ' |
| 75 | '%d seconds of HW reset' % timeout) |
| 76 | |
| 77 | def __exit__(self, exception, value, traceback): |
| 78 | self._start_daemon() |