blob: 278dfa4bc1535fee16b13951b6cb5c6e434bb3bf [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
24
Cheng-Yi Chiang630179d2015-08-20 20:43:39 +080025class ChameleonHostError(Exception):
26 """Error in ChameleonHost."""
27 pass
28
Tom Wai-Hong Tamefe1c7f2014-01-02 14:00:11 +080029
Tom Wai-Hong Tamefe1c7f2014-01-02 14:00:11 +080030class ChameleonHost(ssh_host.SSHHost):
31 """Host class for a host that controls a Chameleon."""
32
33 # Chameleond process name.
34 CHAMELEOND_PROCESS = 'chameleond'
35
36
37 # TODO(waihong): Add verify and repair logic which are required while
38 # deploying to Cros Lab.
39
40
41 def _initialize(self, chameleon_host='localhost', chameleon_port=9992,
42 *args, **dargs):
43 """Initialize a ChameleonHost instance.
44
45 A ChameleonHost instance represents a host that controls a Chameleon.
46
47 @param chameleon_host: Name of the host where the chameleond process
48 is running.
Cheng-Yi Chiang22612862015-08-20 20:39:57 +080049 If this is passed in by IP address, it will be
50 treated as not in lab.
Tom Wai-Hong Tamefe1c7f2014-01-02 14:00:11 +080051 @param chameleon_port: Port the chameleond process is listening on.
52
53 """
54 super(ChameleonHost, self)._initialize(hostname=chameleon_host,
55 *args, **dargs)
Tom Wai-Hong Tamefe1c7f2014-01-02 14:00:11 +080056
Cheng-Yi Chiang22612862015-08-20 20:39:57 +080057 self._is_in_lab = None
58 self._check_if_is_in_lab()
59
Cheng-Yi Chiang630179d2015-08-20 20:43:39 +080060 self._chameleon_port = chameleon_port
61 self._local_port = None
62 self._tunneling_process = None
63
64 if self._is_in_lab:
65 self._chameleon_connection = chameleon.ChameleonConnection(
66 self.hostname, chameleon_port)
67 else:
68 self._create_connection_through_tunnel()
69
Cheng-Yi Chiang22612862015-08-20 20:39:57 +080070
71 def _check_if_is_in_lab(self):
72 """Checks if Chameleon host is in lab and set self._is_in_lab.
73
74 If self.hostname is an IP address, we treat it as is not in lab zone.
75
76 """
77 self._is_in_lab = (False if dnsname_mangler.is_ip_address(self.hostname)
78 else utils.host_is_in_lab_zone(self.hostname))
79
80
Cheng-Yi Chiang630179d2015-08-20 20:43:39 +080081 def _create_connection_through_tunnel(self):
82 """Creates Chameleon connection through SSH tunnel.
83
84 For developers to run server side test on corp device against
85 testing device on Google Test Network, it is required to use
86 SSH tunneling to access ports other than SSH port.
87
88 """
89 try:
90 self._local_port = utils.get_unused_port()
91 self._tunneling_process = self._create_ssh_tunnel(
92 self._chameleon_port, self._local_port)
93
94 self._wait_for_connection_established()
95
96 # Always close tunnel when fail to create connection.
97 except:
98 logging.exception('Error in creating connection through tunnel.')
99 self._disconnect_tunneling()
100 raise
101
102
103 def _wait_for_connection_established(self):
104 """Wait for ChameleonConnection through tunnel being established."""
105
106 def _create_connection():
107 """Create ChameleonConnection.
108
109 @returns: True if success. False otherwise.
110
111 """
112 try:
113 self._chameleon_connection = chameleon.ChameleonConnection(
114 'localhost', self._local_port)
115 except chameleon.ChameleonConnectionError:
116 logging.debug('Connection is not ready yet ...')
117 return False
118
119 logging.debug('Connection is up')
120 return True
121
122 success = utils.wait_for_value(
123 _create_connection, expected_value=True, timeout_sec=30)
124
125 if not success:
126 raise ChameleonHostError('Can not connect to Chameleon')
Cheng-Yi Chiang22612862015-08-20 20:39:57 +0800127
Tom Wai-Hong Tamefe1c7f2014-01-02 14:00:11 +0800128
129 def is_in_lab(self):
130 """Check whether the chameleon host is a lab device.
131
132 @returns: True if the chameleon host is in Cros Lab, otherwise False.
133
134 """
135 return self._is_in_lab
136
137
Tom Wai-Hong Tamefe1c7f2014-01-02 14:00:11 +0800138 def get_wait_up_processes(self):
139 """Get the list of local processes to wait for in wait_up.
140
141 Override get_wait_up_processes in
142 autotest_lib.client.common_lib.hosts.base_classes.Host.
143 Wait for chameleond process to go up. Called by base class when
144 rebooting the device.
145
146 """
147 processes = [self.CHAMELEOND_PROCESS]
148 return processes
149
Tom Wai-Hong Tameaee3402014-01-22 08:52:10 +0800150
151 def create_chameleon_board(self):
152 """Create a ChameleonBoard object."""
153 # TODO(waihong): Add verify and repair logic which are required while
154 # deploying to Cros Lab.
Cheng-Yi Chiang65178312015-05-22 18:53:08 +0800155 return chameleon.ChameleonBoard(self._chameleon_connection, self)
Tom Wai-Hong Tam3d6790d2014-04-14 16:15:47 +0800156
157
Cheng-Yi Chiang630179d2015-08-20 20:43:39 +0800158 def _disconnect_tunneling(self):
159 """Disconnect the SSH tunnel."""
160 if not self._tunneling_process:
161 return
162
163 if self._tunneling_process.poll() is None:
164 self._tunneling_process.terminate()
165 logging.debug(
166 'chameleon_host terminated tunnel, pid %d',
167 self._tunneling_process.pid)
168 else:
169 logging.debug(
170 'chameleon_host tunnel pid %d terminated early, status %d',
171 self._tunneling_process.pid,
172 self._tunneling_process.returncode)
173
174
175 def close(self):
176 """Cleanup function when ChameleonHost is to be destroyed."""
177 self._disconnect_tunneling()
178 super(ChameleonHost, self).close()
179
180
Tom Wai-Hong Tam3d6790d2014-04-14 16:15:47 +0800181def create_chameleon_host(dut, chameleon_args):
182 """Create a ChameleonHost object.
183
184 There three possible cases:
185 1) If the DUT is in Cros Lab and has a chameleon board, then create
186 a ChameleonHost object pointing to the board. chameleon_args
187 is ignored.
188 2) If not case 1) and chameleon_args is neither None nor empty, then
189 create a ChameleonHost object using chameleon_args.
190 3) If neither case 1) or 2) applies, return None.
191
192 @param dut: host name of the host that chameleon connects. It can be used
193 to lookup the chameleon in test lab using naming convention.
Cheng-Yi Chiang22612862015-08-20 20:39:57 +0800194 If dut is an IP address, it can not be used to lookup the
195 chameleon in test lab.
Tom Wai-Hong Tam3d6790d2014-04-14 16:15:47 +0800196 @param chameleon_args: A dictionary that contains args for creating
197 a ChameleonHost object,
198 e.g. {'chameleon_host': '172.11.11.112',
199 'chameleon_port': 9992}.
200
201 @returns: A ChameleonHost object or None.
202
203 """
Nicolas Boichat4eee8012015-10-21 19:13:41 +0800204 if not utils.is_in_container():
205 is_moblab = utils.is_moblab()
206 else:
207 is_moblab = _CONFIG.get_config_value(
208 'SSP', 'is_moblab', type=bool, default=False)
209
Nicolas Boichatacfcf232015-10-20 16:29:12 +0800210 if not is_moblab:
211 dut_is_hostname = not dnsname_mangler.is_ip_address(dut)
212 if dut_is_hostname:
213 chameleon_hostname = chameleon.make_chameleon_hostname(dut)
214 if utils.host_is_in_lab_zone(chameleon_hostname):
215 return ChameleonHost(chameleon_host=chameleon_hostname)
216 if chameleon_args:
217 return ChameleonHost(**chameleon_args)
218 else:
219 return None
Tom Wai-Hong Tam3d6790d2014-04-14 16:15:47 +0800220 else:
Nicolas Boichatacfcf232015-10-20 16:29:12 +0800221 afe = frontend_wrappers.RetryingAFE(timeout_min=5, delay_sec=10)
222 hosts = afe.get_hosts(hostname=dut)
223 if hosts and CHAMELEON_HOST_ATTR in hosts[0].attributes:
224 return ChameleonHost(
225 chameleon_host=hosts[0].attributes[CHAMELEON_HOST_ATTR],
226 chameleon_port=hosts[0].attributes.get(
227 CHAMELEON_PORT_ATTR, 9992)
228 )
229 else:
230 return None