blob: bc5fdd03e4ce365f1b9724366e13ae582330ac49 [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
Nicolas Boichatacfcf232015-10-20 16:29:12 +080014from autotest_lib.server.cros.dynamic_suite import frontend_wrappers
Tom Wai-Hong Tamefe1c7f2014-01-02 14:00:11 +080015from autotest_lib.server.hosts import ssh_host
16
Nicolas Boichatacfcf232015-10-20 16:29:12 +080017
18# Names of the host attributes in the database that represent the values for
19# the chameleon_host and chameleon_port for a servo connected to the DUT.
20CHAMELEON_HOST_ATTR = 'chameleon_host'
21CHAMELEON_PORT_ATTR = 'chameleon_port'
22
Nicolas Boichat4eee8012015-10-21 19:13:41 +080023_CONFIG = global_config.global_config
xixuan6cf6d2f2016-01-29 15:29:00 -080024ENABLE_SSH_TUNNEL_FOR_CHAMELEON = _CONFIG.get_config_value(
25 'CROS', 'enable_ssh_tunnel_for_chameleon', type=bool, default=False)
Nicolas Boichat4eee8012015-10-21 19:13:41 +080026
Cheng-Yi Chiang630179d2015-08-20 20:43:39 +080027class ChameleonHostError(Exception):
28 """Error in ChameleonHost."""
29 pass
30
Tom Wai-Hong Tamefe1c7f2014-01-02 14:00:11 +080031
Tom Wai-Hong Tamefe1c7f2014-01-02 14:00:11 +080032class ChameleonHost(ssh_host.SSHHost):
33 """Host class for a host that controls a Chameleon."""
34
35 # Chameleond process name.
36 CHAMELEOND_PROCESS = 'chameleond'
37
38
39 # TODO(waihong): Add verify and repair logic which are required while
40 # deploying to Cros Lab.
41
42
43 def _initialize(self, chameleon_host='localhost', chameleon_port=9992,
44 *args, **dargs):
45 """Initialize a ChameleonHost instance.
46
47 A ChameleonHost instance represents a host that controls a Chameleon.
48
49 @param chameleon_host: Name of the host where the chameleond process
50 is running.
Cheng-Yi Chiang22612862015-08-20 20:39:57 +080051 If this is passed in by IP address, it will be
52 treated as not in lab.
Tom Wai-Hong Tamefe1c7f2014-01-02 14:00:11 +080053 @param chameleon_port: Port the chameleond process is listening on.
54
55 """
56 super(ChameleonHost, self)._initialize(hostname=chameleon_host,
57 *args, **dargs)
Tom Wai-Hong Tamefe1c7f2014-01-02 14:00:11 +080058
Cheng-Yi Chiang22612862015-08-20 20:39:57 +080059 self._is_in_lab = None
60 self._check_if_is_in_lab()
61
Cheng-Yi Chiang630179d2015-08-20 20:43:39 +080062 self._chameleon_port = chameleon_port
63 self._local_port = None
64 self._tunneling_process = None
65
xixuan5492f3d2016-04-25 16:35:05 -070066 try:
67 if self._is_in_lab and not ENABLE_SSH_TUNNEL_FOR_CHAMELEON:
68 self._chameleon_connection = chameleon.ChameleonConnection(
69 self.hostname, chameleon_port)
70 else:
Joseph Hwangd3e32002016-08-26 15:23:08 +080071 # A proxy generator is passed as an argument so that a proxy
72 # could be re-created on demand in ChameleonConnection
73 # whenever needed, e.g., after a reboot.
74 proxy_generator = (
75 lambda: self.rpc_server_tracker.xmlrpc_connect(
76 None, chameleon_port,
77 ready_test_name=chameleon.CHAMELEON_READY_TEST,
78 timeout_seconds=60))
xixuan5492f3d2016-04-25 16:35:05 -070079 self._chameleon_connection = chameleon.ChameleonConnection(
Joseph Hwangd3e32002016-08-26 15:23:08 +080080 None, proxy_generator=proxy_generator)
81
xixuan5492f3d2016-04-25 16:35:05 -070082 except Exception as e:
83 raise ChameleonHostError('Can not connect to Chameleon: %s(%s)',
84 e.__class__, e)
Cheng-Yi Chiang630179d2015-08-20 20:43:39 +080085
Cheng-Yi Chiang22612862015-08-20 20:39:57 +080086
87 def _check_if_is_in_lab(self):
88 """Checks if Chameleon host is in lab and set self._is_in_lab.
89
90 If self.hostname is an IP address, we treat it as is not in lab zone.
91
92 """
93 self._is_in_lab = (False if dnsname_mangler.is_ip_address(self.hostname)
94 else utils.host_is_in_lab_zone(self.hostname))
95
96
Tom Wai-Hong Tamefe1c7f2014-01-02 14:00:11 +080097 def is_in_lab(self):
98 """Check whether the chameleon host is a lab device.
99
100 @returns: True if the chameleon host is in Cros Lab, otherwise False.
101
102 """
103 return self._is_in_lab
104
105
Tom Wai-Hong Tamefe1c7f2014-01-02 14:00:11 +0800106 def get_wait_up_processes(self):
107 """Get the list of local processes to wait for in wait_up.
108
109 Override get_wait_up_processes in
110 autotest_lib.client.common_lib.hosts.base_classes.Host.
111 Wait for chameleond process to go up. Called by base class when
112 rebooting the device.
113
114 """
115 processes = [self.CHAMELEOND_PROCESS]
116 return processes
117
Tom Wai-Hong Tameaee3402014-01-22 08:52:10 +0800118
119 def create_chameleon_board(self):
Moja Hsu57d93a12017-01-13 17:57:52 +0800120 """Create a ChameleonBoard object with error recovery.
121
122 This function will reboot the chameleon board once and retry if we can't
123 create chameleon board.
124
125 @return A ChameleonBoard object.
126 """
Tom Wai-Hong Tameaee3402014-01-22 08:52:10 +0800127 # TODO(waihong): Add verify and repair logic which are required while
128 # deploying to Cros Lab.
Moja Hsu57d93a12017-01-13 17:57:52 +0800129 chameleon_board = None
130 try:
131 chameleon_board = chameleon.ChameleonBoard(
132 self._chameleon_connection, self)
133 return chameleon_board
134 except:
135 self.reboot()
136 chameleon_board = chameleon.ChameleonBoard(
137 self._chameleon_connection, self)
138 return chameleon_board
Tom Wai-Hong Tam3d6790d2014-04-14 16:15:47 +0800139
140
141def create_chameleon_host(dut, chameleon_args):
142 """Create a ChameleonHost object.
143
144 There three possible cases:
145 1) If the DUT is in Cros Lab and has a chameleon board, then create
146 a ChameleonHost object pointing to the board. chameleon_args
147 is ignored.
148 2) If not case 1) and chameleon_args is neither None nor empty, then
149 create a ChameleonHost object using chameleon_args.
150 3) If neither case 1) or 2) applies, return None.
151
152 @param dut: host name of the host that chameleon connects. It can be used
153 to lookup the chameleon in test lab using naming convention.
Cheng-Yi Chiang22612862015-08-20 20:39:57 +0800154 If dut is an IP address, it can not be used to lookup the
155 chameleon in test lab.
Tom Wai-Hong Tam3d6790d2014-04-14 16:15:47 +0800156 @param chameleon_args: A dictionary that contains args for creating
157 a ChameleonHost object,
158 e.g. {'chameleon_host': '172.11.11.112',
159 'chameleon_port': 9992}.
160
161 @returns: A ChameleonHost object or None.
162
163 """
Nicolas Boichat4eee8012015-10-21 19:13:41 +0800164 if not utils.is_in_container():
165 is_moblab = utils.is_moblab()
166 else:
167 is_moblab = _CONFIG.get_config_value(
168 'SSP', 'is_moblab', type=bool, default=False)
169
Nicolas Boichatacfcf232015-10-20 16:29:12 +0800170 if not is_moblab:
171 dut_is_hostname = not dnsname_mangler.is_ip_address(dut)
172 if dut_is_hostname:
173 chameleon_hostname = chameleon.make_chameleon_hostname(dut)
174 if utils.host_is_in_lab_zone(chameleon_hostname):
Cheng-Yi Chiang8d3cbbb2016-10-20 15:54:40 +0800175 # Be more tolerant on chameleon in the lab because
176 # we don't want dead chameleon blocks non-chameleon tests.
177 if utils.ping(chameleon_hostname, deadline=3):
178 logging.warning(
179 'Chameleon %s is not accessible. Please file a bug'
180 ' to test lab', chameleon_hostname)
181 return None
Prathmesh Prabhufd49f0d2018-09-20 21:02:38 +0000182 return ChameleonHost(chameleon_host=chameleon_hostname)
Nicolas Boichatacfcf232015-10-20 16:29:12 +0800183 if chameleon_args:
184 return ChameleonHost(**chameleon_args)
185 else:
186 return None
Tom Wai-Hong Tam3d6790d2014-04-14 16:15:47 +0800187 else:
Nicolas Boichatacfcf232015-10-20 16:29:12 +0800188 afe = frontend_wrappers.RetryingAFE(timeout_min=5, delay_sec=10)
189 hosts = afe.get_hosts(hostname=dut)
190 if hosts and CHAMELEON_HOST_ATTR in hosts[0].attributes:
Prathmesh Prabhufd49f0d2018-09-20 21:02:38 +0000191 return ChameleonHost(
Nicolas Boichatacfcf232015-10-20 16:29:12 +0800192 chameleon_host=hosts[0].attributes[CHAMELEON_HOST_ATTR],
193 chameleon_port=hosts[0].attributes.get(
194 CHAMELEON_PORT_ATTR, 9992)
195 )
196 else:
197 return None
Shijin Abrahamc09587d2020-02-14 20:46:55 -0800198
199
200def create_btpeer_host(dut, btpeer_args_list):
201 """Create a ChameleonHost object for a Bluetooth peer
202
203 This is similar to create_chameleon_host but unlike chameleon board
204 there can be multiple btpeers with a single DUT
205
206 There four possible cases:
207 1) If the DUT is in Cros Lab then assume that it can have up to 4 bluetooth
208 peers. Ping the url and create a Chameleon host for each Bluetooth peer
209 present. btpeer_args_list is ignored.
210 2) If not case 1) and btpeer_args_list is not empty, then
211 create a BtpeerHost object for each host specified in btpeer_args_list.
212 3) If neither case 1) or 2) applies, return None.
213 4) This DUT is controlled by moblab. This case is not implemented.
214
215
216 @param dut: host name of the host that btpeer connects. It can be used
217 to lookup the btpeer in test lab using naming convention.
218 If dut is an IP address, it can not be used to lookup the
219 btpeer in test lab. Naming convention in the lab is
220 <hostname>-btpeer[1-4]
221 @param btpeer_args_list: A list of dictionaries that contains args for
222 creating a BtpeerHost object,
223 e.g. {'btpeer_host': '172.11.11.112',
224 'btpeer_port': 9992}.
225
226 @returns: A list of BtpeerHost objects
227
228 """
229 def _convert_btpeer_args(args):
230 """Convert btpeer args to format accepted by ChameleonHost."""
231 ret_args = {}
232 if 'btpeer_host' in args:
233 ret_args['chameleon_host'] = args['btpeer_host']
234 if 'btpeer_port' in args:
235 ret_args['chameleon_port'] = args['btpeer_port']
236 return ret_args
237
238 if not utils.is_in_container():
239 is_moblab = utils.is_moblab()
240 else:
241 is_moblab = _CONFIG.get_config_value(
242 'SSP', 'is_moblab', type=bool, default=False)
243
244 btpeer_hosts = []
245
246 if not is_moblab:
247 if (not dnsname_mangler.is_ip_address(dut) and
248 utils.host_is_in_lab_zone(dut)):
249 # This is a device in the lab. Ignore any arguments passed and
250 # derive peer hostnames from the DUT hostname
251 btpeer_hostnames = chameleon.make_btpeer_hostnames(dut)
252 for btpeer_hostname in btpeer_hostnames:
253 # Not all test bed have 4 Bluetooth peers
254 if utils.ping(btpeer_hostname, deadline=3):
255 logging.warning('Btpeer %s is not accessible. This maybe '
256 'expected or it maybe an issue with the '
257 'Bluetooth peer. Please Check the test bed.'
258 , btpeer_hostname)
259 continue
260 else:
261 logging.debug("Creating btpeer from %s",btpeer_hostname)
262 btpeer_hosts.append(
263 ChameleonHost(chameleon_host=btpeer_hostname))
264 return btpeer_hosts
265 else:
266 # IP address given or DNS address is not in lab.
267 # Create the Bluetooth peers from the arguments passed
268 return [ ChameleonHost(**_convert_btpeer_args(btpeer_args))
269 for btpeer_args in btpeer_args_list]
270 else:
271 # TODO(b:149606762)
272 # moblab still create Bluetooth peer from chameleon_args
273 afe = frontend_wrappers.RetryingAFE(timeout_min=5, delay_sec=10)
274 hosts = afe.get_hosts(hostname=dut)
275 if hosts and CHAMELEON_HOST_ATTR in hosts[0].attributes:
276 return [ChameleonHost(
277 chameleon_host=hosts[0].attributes[CHAMELEON_HOST_ATTR],
278 chameleon_port=hosts[0].attributes.get(
279 CHAMELEON_PORT_ATTR, 9992)
280 )]
281 else:
282 return []