blob: 83f6e9dc06a17de4436ce86fd19741cd376db768 [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
Tom Wai-Hong Tamefe1c7f2014-01-02 14:00:11 +080013from autotest_lib.server.hosts import ssh_host
14
Cheng-Yi Chiang630179d2015-08-20 20:43:39 +080015class ChameleonHostError(Exception):
16 """Error in ChameleonHost."""
17 pass
18
Tom Wai-Hong Tamefe1c7f2014-01-02 14:00:11 +080019
Tom Wai-Hong Tamefe1c7f2014-01-02 14:00:11 +080020class ChameleonHost(ssh_host.SSHHost):
21 """Host class for a host that controls a Chameleon."""
22
23 # Chameleond process name.
24 CHAMELEOND_PROCESS = 'chameleond'
25
26
27 # TODO(waihong): Add verify and repair logic which are required while
28 # deploying to Cros Lab.
29
30
31 def _initialize(self, chameleon_host='localhost', chameleon_port=9992,
32 *args, **dargs):
33 """Initialize a ChameleonHost instance.
34
35 A ChameleonHost instance represents a host that controls a Chameleon.
36
37 @param chameleon_host: Name of the host where the chameleond process
38 is running.
Cheng-Yi Chiang22612862015-08-20 20:39:57 +080039 If this is passed in by IP address, it will be
40 treated as not in lab.
Tom Wai-Hong Tamefe1c7f2014-01-02 14:00:11 +080041 @param chameleon_port: Port the chameleond process is listening on.
42
43 """
44 super(ChameleonHost, self)._initialize(hostname=chameleon_host,
45 *args, **dargs)
Tom Wai-Hong Tamefe1c7f2014-01-02 14:00:11 +080046
Cheng-Yi Chiang22612862015-08-20 20:39:57 +080047 self._is_in_lab = None
48 self._check_if_is_in_lab()
49
Cheng-Yi Chiang630179d2015-08-20 20:43:39 +080050 self._chameleon_port = chameleon_port
51 self._local_port = None
52 self._tunneling_process = None
53
54 if self._is_in_lab:
55 self._chameleon_connection = chameleon.ChameleonConnection(
56 self.hostname, chameleon_port)
57 else:
58 self._create_connection_through_tunnel()
59
Cheng-Yi Chiang22612862015-08-20 20:39:57 +080060
61 def _check_if_is_in_lab(self):
62 """Checks if Chameleon host is in lab and set self._is_in_lab.
63
64 If self.hostname is an IP address, we treat it as is not in lab zone.
65
66 """
67 self._is_in_lab = (False if dnsname_mangler.is_ip_address(self.hostname)
68 else utils.host_is_in_lab_zone(self.hostname))
69
70
Cheng-Yi Chiang630179d2015-08-20 20:43:39 +080071 def _create_connection_through_tunnel(self):
72 """Creates Chameleon connection through SSH tunnel.
73
74 For developers to run server side test on corp device against
75 testing device on Google Test Network, it is required to use
76 SSH tunneling to access ports other than SSH port.
77
78 """
79 try:
80 self._local_port = utils.get_unused_port()
81 self._tunneling_process = self._create_ssh_tunnel(
82 self._chameleon_port, self._local_port)
83
84 self._wait_for_connection_established()
85
86 # Always close tunnel when fail to create connection.
87 except:
88 logging.exception('Error in creating connection through tunnel.')
89 self._disconnect_tunneling()
90 raise
91
92
93 def _wait_for_connection_established(self):
94 """Wait for ChameleonConnection through tunnel being established."""
95
96 def _create_connection():
97 """Create ChameleonConnection.
98
99 @returns: True if success. False otherwise.
100
101 """
102 try:
103 self._chameleon_connection = chameleon.ChameleonConnection(
104 'localhost', self._local_port)
105 except chameleon.ChameleonConnectionError:
106 logging.debug('Connection is not ready yet ...')
107 return False
108
109 logging.debug('Connection is up')
110 return True
111
112 success = utils.wait_for_value(
113 _create_connection, expected_value=True, timeout_sec=30)
114
115 if not success:
116 raise ChameleonHostError('Can not connect to Chameleon')
Cheng-Yi Chiang22612862015-08-20 20:39:57 +0800117
Tom Wai-Hong Tamefe1c7f2014-01-02 14:00:11 +0800118
119 def is_in_lab(self):
120 """Check whether the chameleon host is a lab device.
121
122 @returns: True if the chameleon host is in Cros Lab, otherwise False.
123
124 """
125 return self._is_in_lab
126
127
Tom Wai-Hong Tamefe1c7f2014-01-02 14:00:11 +0800128 def get_wait_up_processes(self):
129 """Get the list of local processes to wait for in wait_up.
130
131 Override get_wait_up_processes in
132 autotest_lib.client.common_lib.hosts.base_classes.Host.
133 Wait for chameleond process to go up. Called by base class when
134 rebooting the device.
135
136 """
137 processes = [self.CHAMELEOND_PROCESS]
138 return processes
139
Tom Wai-Hong Tameaee3402014-01-22 08:52:10 +0800140
141 def create_chameleon_board(self):
142 """Create a ChameleonBoard object."""
143 # TODO(waihong): Add verify and repair logic which are required while
144 # deploying to Cros Lab.
Cheng-Yi Chiang65178312015-05-22 18:53:08 +0800145 return chameleon.ChameleonBoard(self._chameleon_connection, self)
Tom Wai-Hong Tam3d6790d2014-04-14 16:15:47 +0800146
147
Cheng-Yi Chiang630179d2015-08-20 20:43:39 +0800148 def _disconnect_tunneling(self):
149 """Disconnect the SSH tunnel."""
150 if not self._tunneling_process:
151 return
152
153 if self._tunneling_process.poll() is None:
154 self._tunneling_process.terminate()
155 logging.debug(
156 'chameleon_host terminated tunnel, pid %d',
157 self._tunneling_process.pid)
158 else:
159 logging.debug(
160 'chameleon_host tunnel pid %d terminated early, status %d',
161 self._tunneling_process.pid,
162 self._tunneling_process.returncode)
163
164
165 def close(self):
166 """Cleanup function when ChameleonHost is to be destroyed."""
167 self._disconnect_tunneling()
168 super(ChameleonHost, self).close()
169
170
Tom Wai-Hong Tam3d6790d2014-04-14 16:15:47 +0800171def create_chameleon_host(dut, chameleon_args):
172 """Create a ChameleonHost object.
173
174 There three possible cases:
175 1) If the DUT is in Cros Lab and has a chameleon board, then create
176 a ChameleonHost object pointing to the board. chameleon_args
177 is ignored.
178 2) If not case 1) and chameleon_args is neither None nor empty, then
179 create a ChameleonHost object using chameleon_args.
180 3) If neither case 1) or 2) applies, return None.
181
182 @param dut: host name of the host that chameleon connects. It can be used
183 to lookup the chameleon in test lab using naming convention.
Cheng-Yi Chiang22612862015-08-20 20:39:57 +0800184 If dut is an IP address, it can not be used to lookup the
185 chameleon in test lab.
Tom Wai-Hong Tam3d6790d2014-04-14 16:15:47 +0800186 @param chameleon_args: A dictionary that contains args for creating
187 a ChameleonHost object,
188 e.g. {'chameleon_host': '172.11.11.112',
189 'chameleon_port': 9992}.
190
191 @returns: A ChameleonHost object or None.
192
193 """
Cheng-Yi Chiang22612862015-08-20 20:39:57 +0800194 dut_is_hostname = not dnsname_mangler.is_ip_address(dut)
195 if dut_is_hostname:
196 chameleon_hostname = chameleon.make_chameleon_hostname(dut)
197 if utils.host_is_in_lab_zone(chameleon_hostname):
198 return ChameleonHost(chameleon_host=chameleon_hostname)
199 if chameleon_args:
Tom Wai-Hong Tam3d6790d2014-04-14 16:15:47 +0800200 return ChameleonHost(**chameleon_args)
201 else:
202 return None