blob: 6d0cebd8c3e9b26e6763708ad2649547c649a4b6 [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
xixuan6cf6d2f2016-01-29 15:29:00 -080066 if self._is_in_lab and not ENABLE_SSH_TUNNEL_FOR_CHAMELEON:
Cheng-Yi Chiang630179d2015-08-20 20:43:39 +080067 self._chameleon_connection = chameleon.ChameleonConnection(
68 self.hostname, chameleon_port)
69 else:
70 self._create_connection_through_tunnel()
71
Cheng-Yi Chiang22612862015-08-20 20:39:57 +080072
73 def _check_if_is_in_lab(self):
74 """Checks if Chameleon host is in lab and set self._is_in_lab.
75
76 If self.hostname is an IP address, we treat it as is not in lab zone.
77
78 """
79 self._is_in_lab = (False if dnsname_mangler.is_ip_address(self.hostname)
80 else utils.host_is_in_lab_zone(self.hostname))
81
82
Cheng-Yi Chiang630179d2015-08-20 20:43:39 +080083 def _create_connection_through_tunnel(self):
84 """Creates Chameleon connection through SSH tunnel.
85
86 For developers to run server side test on corp device against
87 testing device on Google Test Network, it is required to use
88 SSH tunneling to access ports other than SSH port.
89
90 """
91 try:
92 self._local_port = utils.get_unused_port()
xixuan6cf6d2f2016-01-29 15:29:00 -080093 self._tunneling_process = self.create_ssh_tunnel(
Cheng-Yi Chiang630179d2015-08-20 20:43:39 +080094 self._chameleon_port, self._local_port)
95
96 self._wait_for_connection_established()
97
98 # Always close tunnel when fail to create connection.
99 except:
100 logging.exception('Error in creating connection through tunnel.')
101 self._disconnect_tunneling()
102 raise
103
104
105 def _wait_for_connection_established(self):
106 """Wait for ChameleonConnection through tunnel being established."""
107
108 def _create_connection():
109 """Create ChameleonConnection.
110
111 @returns: True if success. False otherwise.
112
113 """
114 try:
115 self._chameleon_connection = chameleon.ChameleonConnection(
116 'localhost', self._local_port)
117 except chameleon.ChameleonConnectionError:
118 logging.debug('Connection is not ready yet ...')
119 return False
120
121 logging.debug('Connection is up')
122 return True
123
124 success = utils.wait_for_value(
125 _create_connection, expected_value=True, timeout_sec=30)
126
127 if not success:
128 raise ChameleonHostError('Can not connect to Chameleon')
Cheng-Yi Chiang22612862015-08-20 20:39:57 +0800129
Tom Wai-Hong Tamefe1c7f2014-01-02 14:00:11 +0800130
131 def is_in_lab(self):
132 """Check whether the chameleon host is a lab device.
133
134 @returns: True if the chameleon host is in Cros Lab, otherwise False.
135
136 """
137 return self._is_in_lab
138
139
Tom Wai-Hong Tamefe1c7f2014-01-02 14:00:11 +0800140 def get_wait_up_processes(self):
141 """Get the list of local processes to wait for in wait_up.
142
143 Override get_wait_up_processes in
144 autotest_lib.client.common_lib.hosts.base_classes.Host.
145 Wait for chameleond process to go up. Called by base class when
146 rebooting the device.
147
148 """
149 processes = [self.CHAMELEOND_PROCESS]
150 return processes
151
Tom Wai-Hong Tameaee3402014-01-22 08:52:10 +0800152
153 def create_chameleon_board(self):
154 """Create a ChameleonBoard object."""
155 # TODO(waihong): Add verify and repair logic which are required while
156 # deploying to Cros Lab.
Cheng-Yi Chiang65178312015-05-22 18:53:08 +0800157 return chameleon.ChameleonBoard(self._chameleon_connection, self)
Tom Wai-Hong Tam3d6790d2014-04-14 16:15:47 +0800158
159
Cheng-Yi Chiang630179d2015-08-20 20:43:39 +0800160 def _disconnect_tunneling(self):
161 """Disconnect the SSH tunnel."""
162 if not self._tunneling_process:
163 return
164
165 if self._tunneling_process.poll() is None:
166 self._tunneling_process.terminate()
167 logging.debug(
168 'chameleon_host terminated tunnel, pid %d',
169 self._tunneling_process.pid)
170 else:
171 logging.debug(
172 'chameleon_host tunnel pid %d terminated early, status %d',
173 self._tunneling_process.pid,
174 self._tunneling_process.returncode)
175
176
177 def close(self):
178 """Cleanup function when ChameleonHost is to be destroyed."""
179 self._disconnect_tunneling()
180 super(ChameleonHost, self).close()
181
182
Tom Wai-Hong Tam3d6790d2014-04-14 16:15:47 +0800183def create_chameleon_host(dut, chameleon_args):
184 """Create a ChameleonHost object.
185
186 There three possible cases:
187 1) If the DUT is in Cros Lab and has a chameleon board, then create
188 a ChameleonHost object pointing to the board. chameleon_args
189 is ignored.
190 2) If not case 1) and chameleon_args is neither None nor empty, then
191 create a ChameleonHost object using chameleon_args.
192 3) If neither case 1) or 2) applies, return None.
193
194 @param dut: host name of the host that chameleon connects. It can be used
195 to lookup the chameleon in test lab using naming convention.
Cheng-Yi Chiang22612862015-08-20 20:39:57 +0800196 If dut is an IP address, it can not be used to lookup the
197 chameleon in test lab.
Tom Wai-Hong Tam3d6790d2014-04-14 16:15:47 +0800198 @param chameleon_args: A dictionary that contains args for creating
199 a ChameleonHost object,
200 e.g. {'chameleon_host': '172.11.11.112',
201 'chameleon_port': 9992}.
202
203 @returns: A ChameleonHost object or None.
204
205 """
Nicolas Boichat4eee8012015-10-21 19:13:41 +0800206 if not utils.is_in_container():
207 is_moblab = utils.is_moblab()
208 else:
209 is_moblab = _CONFIG.get_config_value(
210 'SSP', 'is_moblab', type=bool, default=False)
211
Nicolas Boichatacfcf232015-10-20 16:29:12 +0800212 if not is_moblab:
213 dut_is_hostname = not dnsname_mangler.is_ip_address(dut)
214 if dut_is_hostname:
215 chameleon_hostname = chameleon.make_chameleon_hostname(dut)
216 if utils.host_is_in_lab_zone(chameleon_hostname):
217 return ChameleonHost(chameleon_host=chameleon_hostname)
218 if chameleon_args:
219 return ChameleonHost(**chameleon_args)
220 else:
221 return None
Tom Wai-Hong Tam3d6790d2014-04-14 16:15:47 +0800222 else:
Nicolas Boichatacfcf232015-10-20 16:29:12 +0800223 afe = frontend_wrappers.RetryingAFE(timeout_min=5, delay_sec=10)
224 hosts = afe.get_hosts(hostname=dut)
225 if hosts and CHAMELEON_HOST_ATTR in hosts[0].attributes:
226 return ChameleonHost(
227 chameleon_host=hosts[0].attributes[CHAMELEON_HOST_ATTR],
228 chameleon_port=hosts[0].attributes.get(
229 CHAMELEON_PORT_ATTR, 9992)
230 )
231 else:
232 return None