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