Home | History | Annotate | Download | only in hosts
      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 
      8 import logging
      9 
     10 from autotest_lib.client.bin import utils
     11 from autotest_lib.client.common_lib import global_config
     12 from autotest_lib.client.cros.chameleon import chameleon
     13 from autotest_lib.server.cros import dnsname_mangler
     14 from autotest_lib.server.cros.dynamic_suite import frontend_wrappers
     15 from 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.
     20 CHAMELEON_HOST_ATTR = 'chameleon_host'
     21 CHAMELEON_PORT_ATTR = 'chameleon_port'
     22 
     23 _CONFIG = global_config.global_config
     24 ENABLE_SSH_TUNNEL_FOR_CHAMELEON = _CONFIG.get_config_value(
     25         'CROS', 'enable_ssh_tunnel_for_chameleon', type=bool, default=False)
     26 
     27 class ChameleonHostError(Exception):
     28     """Error in ChameleonHost."""
     29     pass
     30 
     31 
     32 class 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 
    141 def 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