blob: a0c849eae455b13bece97195b0c9f1dc2cb3c9e1 [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
Derek Beckett08ca8572021-08-25 14:46:35 -0700169 dut_is_hostname = not dnsname_mangler.is_ip_address(dut)
170 if dut_is_hostname:
171 chameleon_hostname = chameleon.make_chameleon_hostname(dut)
172 if utils.host_is_in_lab_zone(chameleon_hostname):
173 # Be more tolerant on chameleon in the lab because
174 # we don't want dead chameleon blocks non-chameleon tests.
175 if utils.ping(chameleon_hostname, deadline=3):
176 logging.warning(
177 'Chameleon %s is not accessible. Please file a bug'
178 ' to test lab', chameleon_hostname)
179 return None
180 return ChameleonHost(chameleon_host=chameleon_hostname)
181 if chameleon_args:
182 return ChameleonHost(**chameleon_args)
Tom Wai-Hong Tam3d6790d2014-04-14 16:15:47 +0800183 else:
Derek Beckett08ca8572021-08-25 14:46:35 -0700184 return None
Shijin Abrahamc09587d2020-02-14 20:46:55 -0800185
186
187def create_btpeer_host(dut, btpeer_args_list):
188 """Create a ChameleonHost object for a Bluetooth peer
189
190 This is similar to create_chameleon_host but unlike chameleon board
191 there can be multiple btpeers with a single DUT
192
193 There four possible cases:
194 1) If the DUT is in Cros Lab then assume that it can have up to 4 bluetooth
195 peers. Ping the url and create a Chameleon host for each Bluetooth peer
196 present. btpeer_args_list is ignored.
197 2) If not case 1) and btpeer_args_list is not empty, then
198 create a BtpeerHost object for each host specified in btpeer_args_list.
199 3) If neither case 1) or 2) applies, return None.
200 4) This DUT is controlled by moblab. This case is not implemented.
201
202
203 @param dut: host name of the host that btpeer connects. It can be used
204 to lookup the btpeer in test lab using naming convention.
205 If dut is an IP address, it can not be used to lookup the
206 btpeer in test lab. Naming convention in the lab is
207 <hostname>-btpeer[1-4]
208 @param btpeer_args_list: A list of dictionaries that contains args for
209 creating a BtpeerHost object,
210 e.g. {'btpeer_host': '172.11.11.112',
211 'btpeer_port': 9992}.
212
213 @returns: A list of BtpeerHost objects
214
215 """
216 def _convert_btpeer_args(args):
217 """Convert btpeer args to format accepted by ChameleonHost."""
218 ret_args = {}
219 if 'btpeer_host' in args:
220 ret_args['chameleon_host'] = args['btpeer_host']
221 if 'btpeer_port' in args:
222 ret_args['chameleon_port'] = args['btpeer_port']
Anand K Mistrye8933092020-08-05 14:49:41 +1000223 if 'btpeer_ssh_port' in args:
224 ret_args['port'] = int(args['btpeer_ssh_port'])
Shijin Abrahamc09587d2020-02-14 20:46:55 -0800225 return ret_args
226
227 if not utils.is_in_container():
228 is_moblab = utils.is_moblab()
229 else:
230 is_moblab = _CONFIG.get_config_value(
231 'SSP', 'is_moblab', type=bool, default=False)
232
233 btpeer_hosts = []
234
235 if not is_moblab:
236 if (not dnsname_mangler.is_ip_address(dut) and
237 utils.host_is_in_lab_zone(dut)):
238 # This is a device in the lab. Ignore any arguments passed and
239 # derive peer hostnames from the DUT hostname
240 btpeer_hostnames = chameleon.make_btpeer_hostnames(dut)
241 for btpeer_hostname in btpeer_hostnames:
242 # Not all test bed have 4 Bluetooth peers
243 if utils.ping(btpeer_hostname, deadline=3):
244 logging.warning('Btpeer %s is not accessible. This maybe '
245 'expected or it maybe an issue with the '
246 'Bluetooth peer. Please Check the test bed.'
247 , btpeer_hostname)
248 continue
249 else:
250 logging.debug("Creating btpeer from %s",btpeer_hostname)
251 btpeer_hosts.append(
252 ChameleonHost(chameleon_host=btpeer_hostname))
253 return btpeer_hosts
254 else:
255 # IP address given or DNS address is not in lab.
256 # Create the Bluetooth peers from the arguments passed
257 return [ ChameleonHost(**_convert_btpeer_args(btpeer_args))
258 for btpeer_args in btpeer_args_list]
259 else:
260 # TODO(b:149606762)
261 # moblab still create Bluetooth peer from chameleon_args
262 afe = frontend_wrappers.RetryingAFE(timeout_min=5, delay_sec=10)
263 hosts = afe.get_hosts(hostname=dut)
264 if hosts and CHAMELEON_HOST_ATTR in hosts[0].attributes:
265 return [ChameleonHost(
266 chameleon_host=hosts[0].attributes[CHAMELEON_HOST_ATTR],
267 chameleon_port=hosts[0].attributes.get(
268 CHAMELEON_PORT_ATTR, 9992)
269 )]
270 else:
271 return []