Allow overlapping sync and async startup requests
[chromium-blink-merge.git] / chrome / test / pyautolib / chromeos / power_strip.py
blobb614b676154f521362f573c4cc2b593f3e5eff93
1 # Copyright (c) 2011 The Chromium 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.
5 import logging
6 import re
7 import telnetlib
10 class PowerStrip(object):
11 """Controls Server Technology CW-16V1-C20M switched CDUs.
12 (Cabinet Power Distribution Unit)
14 This class is used to control the CW-16V1-C20M unit which
15 is a 16 port remote power strip. The strip supports AC devices
16 using 100-120V 50/60Hz input voltages. The commands in this
17 class are supported by switches that use Sentry Switched CDU Version 6.0g.
19 Opens a new connection for every command.
20 """
22 TIMEOUT = 10
24 def __init__(self, host, user='admn', password='admn'):
25 self._host = host
26 self._user = user
27 self._password = password
29 def PowerOff(self, outlet):
30 """Powers off the device that is plugged into the specified outlet.
32 Args:
33 outlet: The outlet ID defined on the switch (eg. .a14).
34 """
35 self._DoCommand('off', outlet)
37 def PowerOn(self, outlet):
38 """Powers on the device that is plugged into the specified outlet.
40 Args:
41 outlet: The outlet ID defined on the switch (eg. .a14).
42 """
43 self._DoCommand('on', outlet)
45 def _DoCommand(self, command, outlet):
46 """Performs power strip commands on the specified outlet.
48 Sample telnet interaction:
49 Escape character is '^]'.
51 Sentry Switched CDU Version 6.0g
53 Username: admn
54 Password: < password hidden from view >
56 Location:
58 Switched CDU: on .a1
60 Outlet Outlet Outlet Control
61 ID Name Status State
63 .A1 TowerA_Outlet1 On On
65 Command successful
67 Switched CDU: < cdu cmd >
69 Args:
70 command: A valid CW-16V1-C20M command that follows the format
71 <command> <outlet>.
72 outlet: The outlet ID defined on the switch (eg. .a14).
73 """
74 tn = telnetlib.Telnet()
75 # To avoid 'Connection Reset by Peer: 104' exceptions when rapid calls
76 # are made to the telnet server on the power strip, we retry executing
77 # a command.
78 retry = range(5)
79 for attempt in retry:
80 try:
81 tn.open(self._host, timeout=PowerStrip.TIMEOUT)
82 resp = tn.read_until('Username:', timeout=PowerStrip.TIMEOUT)
83 assert 'Username' in resp, 'Username not found in response. (%s)' % resp
84 tn.write(self._user + '\n')
86 resp = tn.read_until('Password:', timeout=PowerStrip.TIMEOUT)
87 assert 'Password' in resp, 'Password not found in response. (%s)' % resp
88 tn.write(self._password + '\n')
90 resp = tn.read_until('Switched CDU:', timeout=PowerStrip.TIMEOUT)
91 assert 'Switched CDU' in resp, 'Standard prompt not found in ' \
92 'response. (%s)' % resp
93 tn.write('%s %s\n' % (command, outlet))
95 # Obtain the output of command and make sure it matches with the action
96 # we performed.
97 # Sample valid output:
98 # .A1 TowerA_Outlet1 On On
99 resp = tn.read_until('Switched CDU:', timeout=PowerStrip.TIMEOUT)
100 if not re.search('%s\s+\S+\s+%s\s+%s' % (outlet, command, command),
101 resp, re.I):
102 raise Exception('Command \'%s\' execution failed. (%s)' %
103 (command, resp))
105 # Exiting the telnet session cleanly significantly reduces the chance of
106 # connection error on initiating the following telnet session.
107 tn.write('exit\n')
108 tn.read_all()
110 # If we've gotten this far, there is no need to retry.
111 break
112 except Exception as e:
113 logging.debug('Power strip retry on cmd "%s". Reason: %s'
114 % (command, str(e)))
115 if attempt == retry[-1]:
116 raise Exception('Sentry Command "%s" failed. '
117 'Reason: %s' % (command, str(e)))
118 finally:
119 tn.close()