1# 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 8import logging 9 10from autotest_lib.client.bin import utils 11from autotest_lib.client.common_lib import global_config 12from autotest_lib.client.cros.chameleon import chameleon 13from autotest_lib.server.cros import dnsname_mangler 14from autotest_lib.server.cros.dynamic_suite import frontend_wrappers 15from autotest_lib.server.hosts import ssh_host 16 17 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 23_CONFIG = global_config.global_config 24ENABLE_SSH_TUNNEL_FOR_CHAMELEON = _CONFIG.get_config_value( 25 'CROS', 'enable_ssh_tunnel_for_chameleon', type=bool, default=False) 26 27class ChameleonHostError(Exception): 28 """Error in ChameleonHost.""" 29 pass 30 31 32class 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. 51 If this is passed in by IP address, it will be 52 treated as not in lab. 53 @param chameleon_port: Port the chameleond process is listening on. 54 55 """ 56 super(ChameleonHost, self)._initialize(hostname=chameleon_host, 57 *args, **dargs) 58 59 self._is_in_lab = None 60 self._check_if_is_in_lab() 61 62 self._chameleon_port = chameleon_port 63 self._local_port = None 64 self._tunneling_process = None 65 66 try: 67 if self._is_in_lab and not ENABLE_SSH_TUNNEL_FOR_CHAMELEON: 68 self._chameleon_connection = chameleon.ChameleonConnection( 69 self.hostname, chameleon_port) 70 else: 71 # A proxy generator is passed as an argument so that a proxy 72 # could be re-created on demand in ChameleonConnection 73 # whenever needed, e.g., after a reboot. 74 proxy_generator = ( 75 lambda: self.rpc_server_tracker.xmlrpc_connect( 76 None, chameleon_port, 77 ready_test_name=chameleon.CHAMELEON_READY_TEST, 78 timeout_seconds=60)) 79 self._chameleon_connection = chameleon.ChameleonConnection( 80 None, proxy_generator=proxy_generator) 81 82 except Exception as e: 83 raise ChameleonHostError('Can not connect to Chameleon: %s(%s)', 84 e.__class__, e) 85 86 87 def _check_if_is_in_lab(self): 88 """Checks if Chameleon host is in lab and set self._is_in_lab. 89 90 If self.hostname is an IP address, we treat it as is not in lab zone. 91 92 """ 93 self._is_in_lab = (False if dnsname_mangler.is_ip_address(self.hostname) 94 else utils.host_is_in_lab_zone(self.hostname)) 95 96 97 def is_in_lab(self): 98 """Check whether the chameleon host is a lab device. 99 100 @returns: True if the chameleon host is in Cros Lab, otherwise False. 101 102 """ 103 return self._is_in_lab 104 105 106 def get_wait_up_processes(self): 107 """Get the list of local processes to wait for in wait_up. 108 109 Override get_wait_up_processes in 110 autotest_lib.client.common_lib.hosts.base_classes.Host. 111 Wait for chameleond process to go up. Called by base class when 112 rebooting the device. 113 114 """ 115 processes = [self.CHAMELEOND_PROCESS] 116 return processes 117 118 119 def create_chameleon_board(self): 120 """Create a ChameleonBoard object with error recovery. 121 122 This function will reboot the chameleon board once and retry if we can't 123 create chameleon board. 124 125 @return A ChameleonBoard object. 126 """ 127 # TODO(waihong): Add verify and repair logic which are required while 128 # deploying to Cros Lab. 129 chameleon_board = None 130 try: 131 chameleon_board = chameleon.ChameleonBoard( 132 self._chameleon_connection, self) 133 return chameleon_board 134 except: 135 self.reboot() 136 chameleon_board = chameleon.ChameleonBoard( 137 self._chameleon_connection, self) 138 return chameleon_board 139 140 141def create_chameleon_host(dut, chameleon_args): 142 """Create a ChameleonHost object. 143 144 There three possible cases: 145 1) If the DUT is in Cros Lab and has a chameleon board, then create 146 a ChameleonHost object pointing to the board. chameleon_args 147 is ignored. 148 2) If not case 1) and chameleon_args is neither None nor empty, then 149 create a ChameleonHost object using chameleon_args. 150 3) If neither case 1) or 2) applies, return None. 151 152 @param dut: host name of the host that chameleon connects. It can be used 153 to lookup the chameleon in test lab using naming convention. 154 If dut is an IP address, it can not be used to lookup the 155 chameleon in test lab. 156 @param chameleon_args: A dictionary that contains args for creating 157 a ChameleonHost object, 158 e.g. {'chameleon_host': '172.11.11.112', 159 'chameleon_port': 9992}. 160 161 @returns: A ChameleonHost object or None. 162 163 """ 164 if not utils.is_in_container(): 165 is_moblab = utils.is_moblab() 166 else: 167 is_moblab = _CONFIG.get_config_value( 168 'SSP', 'is_moblab', type=bool, default=False) 169 170 if not is_moblab: 171 dut_is_hostname = not dnsname_mangler.is_ip_address(dut) 172 if dut_is_hostname: 173 chameleon_hostname = chameleon.make_chameleon_hostname(dut) 174 if utils.host_is_in_lab_zone(chameleon_hostname): 175 # Be more tolerant on chameleon in the lab because 176 # we don't want dead chameleon blocks non-chameleon tests. 177 if utils.ping(chameleon_hostname, deadline=3): 178 logging.warning( 179 'Chameleon %s is not accessible. Please file a bug' 180 ' to test lab', chameleon_hostname) 181 return None 182 return ChameleonHost(chameleon_host=chameleon_hostname) 183 if chameleon_args: 184 return ChameleonHost(**chameleon_args) 185 else: 186 return None 187 else: 188 afe = frontend_wrappers.RetryingAFE(timeout_min=5, delay_sec=10) 189 hosts = afe.get_hosts(hostname=dut) 190 if hosts and CHAMELEON_HOST_ATTR in hosts[0].attributes: 191 return ChameleonHost( 192 chameleon_host=hosts[0].attributes[CHAMELEON_HOST_ATTR], 193 chameleon_port=hosts[0].attributes.get( 194 CHAMELEON_PORT_ATTR, 9992) 195 ) 196 else: 197 return None 198