blob: dc56e606d2e7a9d98e09cc8b1917f1dd6a9c4b41 [file] [log] [blame]
Tom Wai-Hong Tamefe1c7f2014-01-02 14:00:11 +08001# Copyright (c) 2014 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
6"""This file provides core logic for connecting a Chameleon Daemon."""
7
Cheng-Yi Chiang630179d2015-08-20 20:43:39 +08008import logging
Tom Wai-Hong Tamefe1c7f2014-01-02 14:00:11 +08009
Tom Wai-Hong Tamefe1c7f2014-01-02 14:00:11 +080010from autotest_lib.client.bin import utils
Nicolas Boichat4eee8012015-10-21 19:13:41 +080011from autotest_lib.client.common_lib import global_config
Tom Wai-Hong Tam2588ce12014-08-21 06:24:05 +080012from autotest_lib.client.cros.chameleon import chameleon
Cheng-Yi Chiang22612862015-08-20 20:39:57 +080013from autotest_lib.server.cros import dnsname_mangler
Tom Wai-Hong Tamefe1c7f2014-01-02 14:00:11 +080014from autotest_lib.server.hosts import ssh_host
15
Nicolas Boichatacfcf232015-10-20 16:29:12 +080016
17# Names of the host attributes in the database that represent the values for
18# the chameleon_host and chameleon_port for a servo connected to the DUT.
19CHAMELEON_HOST_ATTR = 'chameleon_host'
20CHAMELEON_PORT_ATTR = 'chameleon_port'
21
Nicolas Boichat4eee8012015-10-21 19:13:41 +080022_CONFIG = global_config.global_config
xixuan6cf6d2f2016-01-29 15:29:00 -080023ENABLE_SSH_TUNNEL_FOR_CHAMELEON = _CONFIG.get_config_value(
24 'CROS', 'enable_ssh_tunnel_for_chameleon', type=bool, default=False)
Nicolas Boichat4eee8012015-10-21 19:13:41 +080025
Cheng-Yi Chiang630179d2015-08-20 20:43:39 +080026class ChameleonHostError(Exception):
27 """Error in ChameleonHost."""
28 pass
29
Tom Wai-Hong Tamefe1c7f2014-01-02 14:00:11 +080030
Tom Wai-Hong Tamefe1c7f2014-01-02 14:00:11 +080031class ChameleonHost(ssh_host.SSHHost):
32 """Host class for a host that controls a Chameleon."""
33
34 # Chameleond process name.
35 CHAMELEOND_PROCESS = 'chameleond'
36
37
38 # TODO(waihong): Add verify and repair logic which are required while
39 # deploying to Cros Lab.
40
41
42 def _initialize(self, chameleon_host='localhost', chameleon_port=9992,
43 *args, **dargs):
44 """Initialize a ChameleonHost instance.
45
46 A ChameleonHost instance represents a host that controls a Chameleon.
47
48 @param chameleon_host: Name of the host where the chameleond process
49 is running.
Cheng-Yi Chiang22612862015-08-20 20:39:57 +080050 If this is passed in by IP address, it will be
51 treated as not in lab.
Tom Wai-Hong Tamefe1c7f2014-01-02 14:00:11 +080052 @param chameleon_port: Port the chameleond process is listening on.
53
54 """
55 super(ChameleonHost, self)._initialize(hostname=chameleon_host,
56 *args, **dargs)
Tom Wai-Hong Tamefe1c7f2014-01-02 14:00:11 +080057
Cheng-Yi Chiang22612862015-08-20 20:39:57 +080058 self._is_in_lab = None
59 self._check_if_is_in_lab()
60
Cheng-Yi Chiang630179d2015-08-20 20:43:39 +080061 self._chameleon_port = chameleon_port
62 self._local_port = None
63 self._tunneling_process = None
64
xixuan5492f3d2016-04-25 16:35:05 -070065 try:
66 if self._is_in_lab and not ENABLE_SSH_TUNNEL_FOR_CHAMELEON:
67 self._chameleon_connection = chameleon.ChameleonConnection(
68 self.hostname, chameleon_port)
69 else:
Joseph Hwangd3e32002016-08-26 15:23:08 +080070 # A proxy generator is passed as an argument so that a proxy
71 # could be re-created on demand in ChameleonConnection
72 # whenever needed, e.g., after a reboot.
73 proxy_generator = (
74 lambda: self.rpc_server_tracker.xmlrpc_connect(
75 None, chameleon_port,
76 ready_test_name=chameleon.CHAMELEON_READY_TEST,
77 timeout_seconds=60))
xixuan5492f3d2016-04-25 16:35:05 -070078 self._chameleon_connection = chameleon.ChameleonConnection(
Joseph Hwangd3e32002016-08-26 15:23:08 +080079 None, proxy_generator=proxy_generator)
80
xixuan5492f3d2016-04-25 16:35:05 -070081 except Exception as e:
82 raise ChameleonHostError('Can not connect to Chameleon: %s(%s)',
83 e.__class__, e)
Cheng-Yi Chiang630179d2015-08-20 20:43:39 +080084
Cheng-Yi Chiang22612862015-08-20 20:39:57 +080085
86 def _check_if_is_in_lab(self):
87 """Checks if Chameleon host is in lab and set self._is_in_lab.
88
89 If self.hostname is an IP address, we treat it as is not in lab zone.
90
91 """
92 self._is_in_lab = (False if dnsname_mangler.is_ip_address(self.hostname)
93 else utils.host_is_in_lab_zone(self.hostname))
94
95
Tom Wai-Hong Tamefe1c7f2014-01-02 14:00:11 +080096 def is_in_lab(self):
97 """Check whether the chameleon host is a lab device.
98
99 @returns: True if the chameleon host is in Cros Lab, otherwise False.
100
101 """
102 return self._is_in_lab
103
104
Tom Wai-Hong Tamefe1c7f2014-01-02 14:00:11 +0800105 def get_wait_up_processes(self):
106 """Get the list of local processes to wait for in wait_up.
107
108 Override get_wait_up_processes in
109 autotest_lib.client.common_lib.hosts.base_classes.Host.
110 Wait for chameleond process to go up. Called by base class when
111 rebooting the device.
112
113 """
114 processes = [self.CHAMELEOND_PROCESS]
115 return processes
116
Tom Wai-Hong Tameaee3402014-01-22 08:52:10 +0800117
118 def create_chameleon_board(self):
Moja Hsu57d93a12017-01-13 17:57:52 +0800119 """Create a ChameleonBoard object with error recovery.
120
121 This function will reboot the chameleon board once and retry if we can't
122 create chameleon board.
123
124 @return A ChameleonBoard object.
125 """
Tom Wai-Hong Tameaee3402014-01-22 08:52:10 +0800126 # TODO(waihong): Add verify and repair logic which are required while
127 # deploying to Cros Lab.
Moja Hsu57d93a12017-01-13 17:57:52 +0800128 chameleon_board = None
129 try:
130 chameleon_board = chameleon.ChameleonBoard(
131 self._chameleon_connection, self)
132 return chameleon_board
133 except:
134 self.reboot()
135 chameleon_board = chameleon.ChameleonBoard(
136 self._chameleon_connection, self)
137 return chameleon_board
Tom Wai-Hong Tam3d6790d2014-04-14 16:15:47 +0800138
139
140def create_chameleon_host(dut, chameleon_args):
141 """Create a ChameleonHost object.
142
143 There three possible cases:
144 1) If the DUT is in Cros Lab and has a chameleon board, then create
145 a ChameleonHost object pointing to the board. chameleon_args
146 is ignored.
147 2) If not case 1) and chameleon_args is neither None nor empty, then
148 create a ChameleonHost object using chameleon_args.
149 3) If neither case 1) or 2) applies, return None.
150
151 @param dut: host name of the host that chameleon connects. It can be used
152 to lookup the chameleon in test lab using naming convention.
Cheng-Yi Chiang22612862015-08-20 20:39:57 +0800153 If dut is an IP address, it can not be used to lookup the
154 chameleon in test lab.
Tom Wai-Hong Tam3d6790d2014-04-14 16:15:47 +0800155 @param chameleon_args: A dictionary that contains args for creating
156 a ChameleonHost object,
157 e.g. {'chameleon_host': '172.11.11.112',
158 'chameleon_port': 9992}.
159
160 @returns: A ChameleonHost object or None.
161
162 """
Nicolas Boichat4eee8012015-10-21 19:13:41 +0800163 if not utils.is_in_container():
164 is_moblab = utils.is_moblab()
165 else:
166 is_moblab = _CONFIG.get_config_value(
167 'SSP', 'is_moblab', type=bool, default=False)
168
Nicolas Boichatacfcf232015-10-20 16:29:12 +0800169 if not is_moblab:
170 dut_is_hostname = not dnsname_mangler.is_ip_address(dut)
171 if dut_is_hostname:
172 chameleon_hostname = chameleon.make_chameleon_hostname(dut)
173 if utils.host_is_in_lab_zone(chameleon_hostname):
Cheng-Yi Chiang8d3cbbb2016-10-20 15:54:40 +0800174 # Be more tolerant on chameleon in the lab because
175 # we don't want dead chameleon blocks non-chameleon tests.
176 if utils.ping(chameleon_hostname, deadline=3):
177 logging.warning(
178 'Chameleon %s is not accessible. Please file a bug'
179 ' to test lab', chameleon_hostname)
180 return None
Prathmesh Prabhufd49f0d2018-09-20 21:02:38 +0000181 return ChameleonHost(chameleon_host=chameleon_hostname)
Nicolas Boichatacfcf232015-10-20 16:29:12 +0800182 if chameleon_args:
183 return ChameleonHost(**chameleon_args)
184 else:
185 return None
Tom Wai-Hong Tam3d6790d2014-04-14 16:15:47 +0800186 else:
Nicolas Boichatacfcf232015-10-20 16:29:12 +0800187 afe = frontend_wrappers.RetryingAFE(timeout_min=5, delay_sec=10)
188 hosts = afe.get_hosts(hostname=dut)
189 if hosts and CHAMELEON_HOST_ATTR in hosts[0].attributes:
Prathmesh Prabhufd49f0d2018-09-20 21:02:38 +0000190 return ChameleonHost(
Nicolas Boichatacfcf232015-10-20 16:29:12 +0800191 chameleon_host=hosts[0].attributes[CHAMELEON_HOST_ATTR],
192 chameleon_port=hosts[0].attributes.get(
193 CHAMELEON_PORT_ATTR, 9992)
194 )
195 else:
196 return None
Shijin Abrahamc09587d2020-02-14 20:46:55 -0800197
198
199def create_btpeer_host(dut, btpeer_args_list):
200 """Create a ChameleonHost object for a Bluetooth peer
201
202 This is similar to create_chameleon_host but unlike chameleon board
203 there can be multiple btpeers with a single DUT
204
205 There four possible cases:
206 1) If the DUT is in Cros Lab then assume that it can have up to 4 bluetooth
207 peers. Ping the url and create a Chameleon host for each Bluetooth peer
208 present. btpeer_args_list is ignored.
209 2) If not case 1) and btpeer_args_list is not empty, then
210 create a BtpeerHost object for each host specified in btpeer_args_list.
211 3) If neither case 1) or 2) applies, return None.
212 4) This DUT is controlled by moblab. This case is not implemented.
213
214
215 @param dut: host name of the host that btpeer connects. It can be used
216 to lookup the btpeer in test lab using naming convention.
217 If dut is an IP address, it can not be used to lookup the
218 btpeer in test lab. Naming convention in the lab is
219 <hostname>-btpeer[1-4]
220 @param btpeer_args_list: A list of dictionaries that contains args for
221 creating a BtpeerHost object,
222 e.g. {'btpeer_host': '172.11.11.112',
223 'btpeer_port': 9992}.
224
225 @returns: A list of BtpeerHost objects
226
227 """
228 def _convert_btpeer_args(args):
229 """Convert btpeer args to format accepted by ChameleonHost."""
230 ret_args = {}
231 if 'btpeer_host' in args:
232 ret_args['chameleon_host'] = args['btpeer_host']
233 if 'btpeer_port' in args:
234 ret_args['chameleon_port'] = args['btpeer_port']
Anand K Mistrye8933092020-08-05 14:49:41 +1000235 if 'btpeer_ssh_port' in args:
236 ret_args['port'] = int(args['btpeer_ssh_port'])
Shijin Abrahamc09587d2020-02-14 20:46:55 -0800237 return ret_args
238
239 if not utils.is_in_container():
240 is_moblab = utils.is_moblab()
241 else:
242 is_moblab = _CONFIG.get_config_value(
243 'SSP', 'is_moblab', type=bool, default=False)
244
245 btpeer_hosts = []
246
247 if not is_moblab:
248 if (not dnsname_mangler.is_ip_address(dut) and
249 utils.host_is_in_lab_zone(dut)):
250 # This is a device in the lab. Ignore any arguments passed and
251 # derive peer hostnames from the DUT hostname
252 btpeer_hostnames = chameleon.make_btpeer_hostnames(dut)
253 for btpeer_hostname in btpeer_hostnames:
254 # Not all test bed have 4 Bluetooth peers
255 if utils.ping(btpeer_hostname, deadline=3):
256 logging.warning('Btpeer %s is not accessible. This maybe '
257 'expected or it maybe an issue with the '
258 'Bluetooth peer. Please Check the test bed.'
259 , btpeer_hostname)
260 continue
261 else:
262 logging.debug("Creating btpeer from %s",btpeer_hostname)
263 btpeer_hosts.append(
264 ChameleonHost(chameleon_host=btpeer_hostname))
265 return btpeer_hosts
266 else:
267 # IP address given or DNS address is not in lab.
268 # Create the Bluetooth peers from the arguments passed
269 return [ ChameleonHost(**_convert_btpeer_args(btpeer_args))
270 for btpeer_args in btpeer_args_list]
271 else:
272 # TODO(b:149606762)
273 # moblab still create Bluetooth peer from chameleon_args
274 afe = frontend_wrappers.RetryingAFE(timeout_min=5, delay_sec=10)
275 hosts = afe.get_hosts(hostname=dut)
276 if hosts and CHAMELEON_HOST_ATTR in hosts[0].attributes:
277 return [ChameleonHost(
278 chameleon_host=hosts[0].attributes[CHAMELEON_HOST_ATTR],
279 chameleon_port=hosts[0].attributes.get(
280 CHAMELEON_PORT_ATTR, 9992)
281 )]
282 else:
283 return []