You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
285 lines
11 KiB
285 lines
11 KiB
# Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
|
|
# Use of this source code is governed by a BSD-style license that can be
|
|
# found in the LICENSE file.
|
|
#
|
|
|
|
"""This file provides core logic for connecting a Chameleon Daemon."""
|
|
|
|
import logging
|
|
|
|
from autotest_lib.client.bin import utils
|
|
from autotest_lib.client.common_lib import global_config
|
|
from autotest_lib.client.cros.chameleon import chameleon
|
|
from autotest_lib.server.cros import dnsname_mangler
|
|
from autotest_lib.server.cros.dynamic_suite import frontend_wrappers
|
|
from autotest_lib.server.hosts import ssh_host
|
|
|
|
|
|
# Names of the host attributes in the database that represent the values for
|
|
# the chameleon_host and chameleon_port for a servo connected to the DUT.
|
|
CHAMELEON_HOST_ATTR = 'chameleon_host'
|
|
CHAMELEON_PORT_ATTR = 'chameleon_port'
|
|
|
|
_CONFIG = global_config.global_config
|
|
ENABLE_SSH_TUNNEL_FOR_CHAMELEON = _CONFIG.get_config_value(
|
|
'CROS', 'enable_ssh_tunnel_for_chameleon', type=bool, default=False)
|
|
|
|
class ChameleonHostError(Exception):
|
|
"""Error in ChameleonHost."""
|
|
pass
|
|
|
|
|
|
class ChameleonHost(ssh_host.SSHHost):
|
|
"""Host class for a host that controls a Chameleon."""
|
|
|
|
# Chameleond process name.
|
|
CHAMELEOND_PROCESS = 'chameleond'
|
|
|
|
|
|
# TODO(waihong): Add verify and repair logic which are required while
|
|
# deploying to Cros Lab.
|
|
|
|
|
|
def _initialize(self, chameleon_host='localhost', chameleon_port=9992,
|
|
*args, **dargs):
|
|
"""Initialize a ChameleonHost instance.
|
|
|
|
A ChameleonHost instance represents a host that controls a Chameleon.
|
|
|
|
@param chameleon_host: Name of the host where the chameleond process
|
|
is running.
|
|
If this is passed in by IP address, it will be
|
|
treated as not in lab.
|
|
@param chameleon_port: Port the chameleond process is listening on.
|
|
|
|
"""
|
|
super(ChameleonHost, self)._initialize(hostname=chameleon_host,
|
|
*args, **dargs)
|
|
|
|
self._is_in_lab = None
|
|
self._check_if_is_in_lab()
|
|
|
|
self._chameleon_port = chameleon_port
|
|
self._local_port = None
|
|
self._tunneling_process = None
|
|
|
|
try:
|
|
if self._is_in_lab and not ENABLE_SSH_TUNNEL_FOR_CHAMELEON:
|
|
self._chameleon_connection = chameleon.ChameleonConnection(
|
|
self.hostname, chameleon_port)
|
|
else:
|
|
# A proxy generator is passed as an argument so that a proxy
|
|
# could be re-created on demand in ChameleonConnection
|
|
# whenever needed, e.g., after a reboot.
|
|
proxy_generator = (
|
|
lambda: self.rpc_server_tracker.xmlrpc_connect(
|
|
None, chameleon_port,
|
|
ready_test_name=chameleon.CHAMELEON_READY_TEST,
|
|
timeout_seconds=60))
|
|
self._chameleon_connection = chameleon.ChameleonConnection(
|
|
None, proxy_generator=proxy_generator)
|
|
|
|
except Exception as e:
|
|
raise ChameleonHostError('Can not connect to Chameleon: %s(%s)',
|
|
e.__class__, e)
|
|
|
|
|
|
def _check_if_is_in_lab(self):
|
|
"""Checks if Chameleon host is in lab and set self._is_in_lab.
|
|
|
|
If self.hostname is an IP address, we treat it as is not in lab zone.
|
|
|
|
"""
|
|
self._is_in_lab = (False if dnsname_mangler.is_ip_address(self.hostname)
|
|
else utils.host_is_in_lab_zone(self.hostname))
|
|
|
|
|
|
def is_in_lab(self):
|
|
"""Check whether the chameleon host is a lab device.
|
|
|
|
@returns: True if the chameleon host is in Cros Lab, otherwise False.
|
|
|
|
"""
|
|
return self._is_in_lab
|
|
|
|
|
|
def get_wait_up_processes(self):
|
|
"""Get the list of local processes to wait for in wait_up.
|
|
|
|
Override get_wait_up_processes in
|
|
autotest_lib.client.common_lib.hosts.base_classes.Host.
|
|
Wait for chameleond process to go up. Called by base class when
|
|
rebooting the device.
|
|
|
|
"""
|
|
processes = [self.CHAMELEOND_PROCESS]
|
|
return processes
|
|
|
|
|
|
def create_chameleon_board(self):
|
|
"""Create a ChameleonBoard object with error recovery.
|
|
|
|
This function will reboot the chameleon board once and retry if we can't
|
|
create chameleon board.
|
|
|
|
@return A ChameleonBoard object.
|
|
"""
|
|
# TODO(waihong): Add verify and repair logic which are required while
|
|
# deploying to Cros Lab.
|
|
chameleon_board = None
|
|
try:
|
|
chameleon_board = chameleon.ChameleonBoard(
|
|
self._chameleon_connection, self)
|
|
return chameleon_board
|
|
except:
|
|
self.reboot()
|
|
chameleon_board = chameleon.ChameleonBoard(
|
|
self._chameleon_connection, self)
|
|
return chameleon_board
|
|
|
|
|
|
def create_chameleon_host(dut, chameleon_args):
|
|
"""Create a ChameleonHost object.
|
|
|
|
There three possible cases:
|
|
1) If the DUT is in Cros Lab and has a chameleon board, then create
|
|
a ChameleonHost object pointing to the board. chameleon_args
|
|
is ignored.
|
|
2) If not case 1) and chameleon_args is neither None nor empty, then
|
|
create a ChameleonHost object using chameleon_args.
|
|
3) If neither case 1) or 2) applies, return None.
|
|
|
|
@param dut: host name of the host that chameleon connects. It can be used
|
|
to lookup the chameleon in test lab using naming convention.
|
|
If dut is an IP address, it can not be used to lookup the
|
|
chameleon in test lab.
|
|
@param chameleon_args: A dictionary that contains args for creating
|
|
a ChameleonHost object,
|
|
e.g. {'chameleon_host': '172.11.11.112',
|
|
'chameleon_port': 9992}.
|
|
|
|
@returns: A ChameleonHost object or None.
|
|
|
|
"""
|
|
if not utils.is_in_container():
|
|
is_moblab = utils.is_moblab()
|
|
else:
|
|
is_moblab = _CONFIG.get_config_value(
|
|
'SSP', 'is_moblab', type=bool, default=False)
|
|
|
|
if not is_moblab:
|
|
dut_is_hostname = not dnsname_mangler.is_ip_address(dut)
|
|
if dut_is_hostname:
|
|
chameleon_hostname = chameleon.make_chameleon_hostname(dut)
|
|
if utils.host_is_in_lab_zone(chameleon_hostname):
|
|
# Be more tolerant on chameleon in the lab because
|
|
# we don't want dead chameleon blocks non-chameleon tests.
|
|
if utils.ping(chameleon_hostname, deadline=3):
|
|
logging.warning(
|
|
'Chameleon %s is not accessible. Please file a bug'
|
|
' to test lab', chameleon_hostname)
|
|
return None
|
|
return ChameleonHost(chameleon_host=chameleon_hostname)
|
|
if chameleon_args:
|
|
return ChameleonHost(**chameleon_args)
|
|
else:
|
|
return None
|
|
else:
|
|
afe = frontend_wrappers.RetryingAFE(timeout_min=5, delay_sec=10)
|
|
hosts = afe.get_hosts(hostname=dut)
|
|
if hosts and CHAMELEON_HOST_ATTR in hosts[0].attributes:
|
|
return ChameleonHost(
|
|
chameleon_host=hosts[0].attributes[CHAMELEON_HOST_ATTR],
|
|
chameleon_port=hosts[0].attributes.get(
|
|
CHAMELEON_PORT_ATTR, 9992)
|
|
)
|
|
else:
|
|
return None
|
|
|
|
|
|
def create_btpeer_host(dut, btpeer_args_list):
|
|
"""Create a ChameleonHost object for a Bluetooth peer
|
|
|
|
This is similar to create_chameleon_host but unlike chameleon board
|
|
there can be multiple btpeers with a single DUT
|
|
|
|
There four possible cases:
|
|
1) If the DUT is in Cros Lab then assume that it can have up to 4 bluetooth
|
|
peers. Ping the url and create a Chameleon host for each Bluetooth peer
|
|
present. btpeer_args_list is ignored.
|
|
2) If not case 1) and btpeer_args_list is not empty, then
|
|
create a BtpeerHost object for each host specified in btpeer_args_list.
|
|
3) If neither case 1) or 2) applies, return None.
|
|
4) This DUT is controlled by moblab. This case is not implemented.
|
|
|
|
|
|
@param dut: host name of the host that btpeer connects. It can be used
|
|
to lookup the btpeer in test lab using naming convention.
|
|
If dut is an IP address, it can not be used to lookup the
|
|
btpeer in test lab. Naming convention in the lab is
|
|
<hostname>-btpeer[1-4]
|
|
@param btpeer_args_list: A list of dictionaries that contains args for
|
|
creating a BtpeerHost object,
|
|
e.g. {'btpeer_host': '172.11.11.112',
|
|
'btpeer_port': 9992}.
|
|
|
|
@returns: A list of BtpeerHost objects
|
|
|
|
"""
|
|
def _convert_btpeer_args(args):
|
|
"""Convert btpeer args to format accepted by ChameleonHost."""
|
|
ret_args = {}
|
|
if 'btpeer_host' in args:
|
|
ret_args['chameleon_host'] = args['btpeer_host']
|
|
if 'btpeer_port' in args:
|
|
ret_args['chameleon_port'] = args['btpeer_port']
|
|
if 'btpeer_ssh_port' in args:
|
|
ret_args['port'] = int(args['btpeer_ssh_port'])
|
|
return ret_args
|
|
|
|
if not utils.is_in_container():
|
|
is_moblab = utils.is_moblab()
|
|
else:
|
|
is_moblab = _CONFIG.get_config_value(
|
|
'SSP', 'is_moblab', type=bool, default=False)
|
|
|
|
btpeer_hosts = []
|
|
|
|
if not is_moblab:
|
|
if (not dnsname_mangler.is_ip_address(dut) and
|
|
utils.host_is_in_lab_zone(dut)):
|
|
# This is a device in the lab. Ignore any arguments passed and
|
|
# derive peer hostnames from the DUT hostname
|
|
btpeer_hostnames = chameleon.make_btpeer_hostnames(dut)
|
|
for btpeer_hostname in btpeer_hostnames:
|
|
# Not all test bed have 4 Bluetooth peers
|
|
if utils.ping(btpeer_hostname, deadline=3):
|
|
logging.warning('Btpeer %s is not accessible. This maybe '
|
|
'expected or it maybe an issue with the '
|
|
'Bluetooth peer. Please Check the test bed.'
|
|
, btpeer_hostname)
|
|
continue
|
|
else:
|
|
logging.debug("Creating btpeer from %s",btpeer_hostname)
|
|
btpeer_hosts.append(
|
|
ChameleonHost(chameleon_host=btpeer_hostname))
|
|
return btpeer_hosts
|
|
else:
|
|
# IP address given or DNS address is not in lab.
|
|
# Create the Bluetooth peers from the arguments passed
|
|
return [ ChameleonHost(**_convert_btpeer_args(btpeer_args))
|
|
for btpeer_args in btpeer_args_list]
|
|
else:
|
|
# TODO(b:149606762)
|
|
# moblab still create Bluetooth peer from chameleon_args
|
|
afe = frontend_wrappers.RetryingAFE(timeout_min=5, delay_sec=10)
|
|
hosts = afe.get_hosts(hostname=dut)
|
|
if hosts and CHAMELEON_HOST_ATTR in hosts[0].attributes:
|
|
return [ChameleonHost(
|
|
chameleon_host=hosts[0].attributes[CHAMELEON_HOST_ATTR],
|
|
chameleon_port=hosts[0].attributes.get(
|
|
CHAMELEON_PORT_ATTR, 9992)
|
|
)]
|
|
else:
|
|
return []
|