1d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng"""Provides a factory method to create a host object.""" 2d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng 346dadc9439355f72d394dcc4700902001bd797ffbeepsimport logging 45189dd5255f3cd8d68026b081367c32d135dd9b6Aviv Keshetfrom contextlib import closing 546dadc9439355f72d394dcc4700902001bd797ffbeeps 614622bb8249c2200e5258ea6d40b3362229e90a7Simran Basifrom autotest_lib.client.bin import local_host 7c5947faa755945f81537c6c33c322dccacac0adeAviv Keshetfrom autotest_lib.client.common_lib import error, global_config 81c3b853b2d9b80c9efcc1a27112731b96d765e6eJ. Richard Barnettefrom autotest_lib.server import utils as server_utils 91c3b853b2d9b80c9efcc1a27112731b96d765e6eJ. Richard Barnettefrom autotest_lib.server.hosts import cros_host, ssh_host 10e5f7ae44738af9f6138103e7c57c26b415486ce8Simran Basifrom autotest_lib.server.hosts import moblab_host, sonic_host 11549beb41550bf954394ca8c3a611397c97745759Kevin Chengfrom autotest_lib.server.hosts import adb_host, testbed 12e48bcfb879c943df72a866e3db8b2df8e930b2c1mbligh 13d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng 1455552bf4a54d847f2cc30b307b3636c2be2ad5a2mblighSSH_ENGINE = global_config.global_config.get_config_value('AUTOSERV', 1555552bf4a54d847f2cc30b307b3636c2be2ad5a2mbligh 'ssh_engine', 1655552bf4a54d847f2cc30b307b3636c2be2ad5a2mbligh type=str) 17d0672689266d3d63901f3a35fb7e0a2d96d6e39abeeps 18d0672689266d3d63901f3a35fb7e0a2d96d6e39abeeps# Default ssh options used in creating a host. 19d0672689266d3d63901f3a35fb7e0a2d96d6e39abeepsDEFAULT_SSH_USER = 'root' 20d0672689266d3d63901f3a35fb7e0a2d96d6e39abeepsDEFAULT_SSH_PASS = '' 21d0672689266d3d63901f3a35fb7e0a2d96d6e39abeepsDEFAULT_SSH_PORT = 22 22d0672689266d3d63901f3a35fb7e0a2d96d6e39abeepsDEFAULT_SSH_VERBOSITY = '' 23d0672689266d3d63901f3a35fb7e0a2d96d6e39abeepsDEFAULT_SSH_OPTIONS = '' 24d0672689266d3d63901f3a35fb7e0a2d96d6e39abeeps 25d60321a8b012aa6101bcb237341fab5bd2d25a04jadmanski# for tracking which hostnames have already had job_start called 26d60321a8b012aa6101bcb237341fab5bd2d25a04jadmanski_started_hostnames = set() 27d60321a8b012aa6101bcb237341fab5bd2d25a04jadmanski 2846dadc9439355f72d394dcc4700902001bd797ffbeeps# A list of all the possible host types, ordered according to frequency of 2946dadc9439355f72d394dcc4700902001bd797ffbeeps# host types in the lab, so the more common hosts don't incur a repeated ssh 3046dadc9439355f72d394dcc4700902001bd797ffbeeps# overhead in checking for less common host types. 31e5f7ae44738af9f6138103e7c57c26b415486ce8Simran Basihost_types = [cros_host.CrosHost, moblab_host.MoblabHost, sonic_host.SonicHost, 32e5f7ae44738af9f6138103e7c57c26b415486ce8Simran Basi adb_host.ADBHost,] 3314622bb8249c2200e5258ea6d40b3362229e90a7Simran BasiOS_HOST_DICT = {'cros' : cros_host.CrosHost, 3414622bb8249c2200e5258ea6d40b3362229e90a7Simran Basi 'android': adb_host.ADBHost} 3546dadc9439355f72d394dcc4700902001bd797ffbeeps 36d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng 37d0672689266d3d63901f3a35fb7e0a2d96d6e39abeepsdef _get_host_arguments(): 38d0672689266d3d63901f3a35fb7e0a2d96d6e39abeeps """Returns parameters needed to ssh into a host. 39d0672689266d3d63901f3a35fb7e0a2d96d6e39abeeps 40d0672689266d3d63901f3a35fb7e0a2d96d6e39abeeps There are currently 2 use cases for creating a host. 41d0672689266d3d63901f3a35fb7e0a2d96d6e39abeeps 1. Through the server_job, in which case the server_job injects 42d0672689266d3d63901f3a35fb7e0a2d96d6e39abeeps the appropriate ssh parameters into our name space and they 43d0672689266d3d63901f3a35fb7e0a2d96d6e39abeeps are available as the variables ssh_user, ssh_pass etc. 44d0672689266d3d63901f3a35fb7e0a2d96d6e39abeeps 2. Directly through factory.create_host, in which case we use 45d0672689266d3d63901f3a35fb7e0a2d96d6e39abeeps the same defaults as used in the server job to create a host. 46d0672689266d3d63901f3a35fb7e0a2d96d6e39abeeps 47d0672689266d3d63901f3a35fb7e0a2d96d6e39abeeps @returns: A tuple of parameters needed to create an ssh connection, ordered 48d0672689266d3d63901f3a35fb7e0a2d96d6e39abeeps as: ssh_user, ssh_pass, ssh_port, ssh_verbosity, ssh_options. 49d0672689266d3d63901f3a35fb7e0a2d96d6e39abeeps """ 50d0672689266d3d63901f3a35fb7e0a2d96d6e39abeeps g = globals() 51d0672689266d3d63901f3a35fb7e0a2d96d6e39abeeps return (g.get('ssh_user', DEFAULT_SSH_USER), 52d0672689266d3d63901f3a35fb7e0a2d96d6e39abeeps g.get('ssh_pass', DEFAULT_SSH_PASS), 53d0672689266d3d63901f3a35fb7e0a2d96d6e39abeeps g.get('ssh_port', DEFAULT_SSH_PORT), 54d0672689266d3d63901f3a35fb7e0a2d96d6e39abeeps g.get('ssh_verbosity_flag', DEFAULT_SSH_VERBOSITY), 55d0672689266d3d63901f3a35fb7e0a2d96d6e39abeeps g.get('ssh_options', DEFAULT_SSH_OPTIONS)) 56d0672689266d3d63901f3a35fb7e0a2d96d6e39abeeps 57d0672689266d3d63901f3a35fb7e0a2d96d6e39abeeps 58724b8a50f34b85dda9f1990e3e8c5b68edbdb37bSimran Basidef _detect_host(connectivity_class, hostname, **args): 59724b8a50f34b85dda9f1990e3e8c5b68edbdb37bSimran Basi """Detect host type. 60724b8a50f34b85dda9f1990e3e8c5b68edbdb37bSimran Basi 6146dadc9439355f72d394dcc4700902001bd797ffbeeps Goes through all the possible host classes, calling check_host with a 6246dadc9439355f72d394dcc4700902001bd797ffbeeps basic host object. Currently this is an ssh host, but theoretically it 6346dadc9439355f72d394dcc4700902001bd797ffbeeps can be any host object that the check_host method of appropriate host 6446dadc9439355f72d394dcc4700902001bd797ffbeeps type knows to use. 65724b8a50f34b85dda9f1990e3e8c5b68edbdb37bSimran Basi 66724b8a50f34b85dda9f1990e3e8c5b68edbdb37bSimran Basi @param connectivity_class: connectivity class to use to talk to the host 67724b8a50f34b85dda9f1990e3e8c5b68edbdb37bSimran Basi (ParamikoHost or SSHHost) 68724b8a50f34b85dda9f1990e3e8c5b68edbdb37bSimran Basi @param hostname: A string representing the host name of the device. 69724b8a50f34b85dda9f1990e3e8c5b68edbdb37bSimran Basi @param args: Args that will be passed to the constructor of 70724b8a50f34b85dda9f1990e3e8c5b68edbdb37bSimran Basi the host class. 71724b8a50f34b85dda9f1990e3e8c5b68edbdb37bSimran Basi 7246dadc9439355f72d394dcc4700902001bd797ffbeeps @returns: Class type of the first host class that returns True to the 7346dadc9439355f72d394dcc4700902001bd797ffbeeps check_host method. 74724b8a50f34b85dda9f1990e3e8c5b68edbdb37bSimran Basi """ 7546dadc9439355f72d394dcc4700902001bd797ffbeeps # TODO crbug.com/302026 (sbasi) - adjust this pathway for ADBHost in 7646dadc9439355f72d394dcc4700902001bd797ffbeeps # the future should a host require verify/repair. 7746dadc9439355f72d394dcc4700902001bd797ffbeeps with closing(connectivity_class(hostname, **args)) as host: 7846dadc9439355f72d394dcc4700902001bd797ffbeeps for host_module in host_types: 7946dadc9439355f72d394dcc4700902001bd797ffbeeps if host_module.check_host(host, timeout=10): 8046dadc9439355f72d394dcc4700902001bd797ffbeeps return host_module 8146dadc9439355f72d394dcc4700902001bd797ffbeeps 8246dadc9439355f72d394dcc4700902001bd797ffbeeps logging.warning('Unable to apply conventional host detection methods, ' 8346dadc9439355f72d394dcc4700902001bd797ffbeeps 'defaulting to chromeos host.') 8446dadc9439355f72d394dcc4700902001bd797ffbeeps return cros_host.CrosHost 85724b8a50f34b85dda9f1990e3e8c5b68edbdb37bSimran Basi 86724b8a50f34b85dda9f1990e3e8c5b68edbdb37bSimran Basi 870e72735393422ec11696d836f0756d96b0497380Gwendal Grignoudef _choose_connectivity_class(hostname, ssh_port): 882a5dc2568ec03419c46efe1752287e24d660a02fSimran Basi """Choose a connectivity class for this hostname. 892a5dc2568ec03419c46efe1752287e24d660a02fSimran Basi 902a5dc2568ec03419c46efe1752287e24d660a02fSimran Basi @param hostname: hostname that we need a connectivity class for. 910e72735393422ec11696d836f0756d96b0497380Gwendal Grignou @param ssh_port: SSH port to connect to the host. 922a5dc2568ec03419c46efe1752287e24d660a02fSimran Basi 932a5dc2568ec03419c46efe1752287e24d660a02fSimran Basi @returns a connectivity host class. 942a5dc2568ec03419c46efe1752287e24d660a02fSimran Basi """ 950e72735393422ec11696d836f0756d96b0497380Gwendal Grignou if (hostname == 'localhost' and ssh_port == DEFAULT_SSH_PORT): 962a5dc2568ec03419c46efe1752287e24d660a02fSimran Basi return local_host.LocalHost 972a5dc2568ec03419c46efe1752287e24d660a02fSimran Basi # by default assume we're using SSH support 982a5dc2568ec03419c46efe1752287e24d660a02fSimran Basi elif SSH_ENGINE == 'paramiko': 992a5dc2568ec03419c46efe1752287e24d660a02fSimran Basi # Not all systems have paramiko installed so only import paramiko host 1002a5dc2568ec03419c46efe1752287e24d660a02fSimran Basi # if the global_config settings call for it. 1012a5dc2568ec03419c46efe1752287e24d660a02fSimran Basi from autotest_lib.server.hosts import paramiko_host 1022a5dc2568ec03419c46efe1752287e24d660a02fSimran Basi return paramiko_host.ParamikoHost 1032a5dc2568ec03419c46efe1752287e24d660a02fSimran Basi elif SSH_ENGINE == 'raw_ssh': 1042a5dc2568ec03419c46efe1752287e24d660a02fSimran Basi return ssh_host.SSHHost 1052a5dc2568ec03419c46efe1752287e24d660a02fSimran Basi else: 1062a5dc2568ec03419c46efe1752287e24d660a02fSimran Basi raise error.AutoServError("Unknown SSH engine %s. Please verify the " 1072a5dc2568ec03419c46efe1752287e24d660a02fSimran Basi "value of the configuration key 'ssh_engine' " 1082a5dc2568ec03419c46efe1752287e24d660a02fSimran Basi "on autotest's global_config.ini file." % 1092a5dc2568ec03419c46efe1752287e24d660a02fSimran Basi SSH_ENGINE) 1102a5dc2568ec03419c46efe1752287e24d660a02fSimran Basi 1112a5dc2568ec03419c46efe1752287e24d660a02fSimran Basi 11285e864a0b6e00c2c2de0bbb698ff927822916a0eKevin Cheng# TODO(kevcheng): Update the creation method so it's not a research project 11385e864a0b6e00c2c2de0bbb698ff927822916a0eKevin Cheng# determining the class inheritance model. 1142a5dc2568ec03419c46efe1752287e24d660a02fSimran Basidef create_host(machine, host_class=None, connectivity_class=None, **args): 115d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng """Create a host object. 116d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng 117d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng This method mixes host classes that are needed into a new subclass 118d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng and creates a instance of the new class. 119d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng 1201bf60eb788365f083d0ee8045a6556f906149decSimran Basi @param machine: A dict representing the device under test or a String 1211bf60eb788365f083d0ee8045a6556f906149decSimran Basi representing the DUT hostname (for legacy caller support). 1221bf60eb788365f083d0ee8045a6556f906149decSimran Basi If it is a machine dict, the 'hostname' key is required. 1231bf60eb788365f083d0ee8045a6556f906149decSimran Basi Optional 'host_attributes' key will pipe in host_attributes 1241bf60eb788365f083d0ee8045a6556f906149decSimran Basi from the autoserv runtime or the AFE. 125a5522a35b55c7cac915cf0b4efc69ead32e26d35Simran Basi @param host_class: Host class to use, if None, will attempt to detect 126a5522a35b55c7cac915cf0b4efc69ead32e26d35Simran Basi the correct class. 1272a5dc2568ec03419c46efe1752287e24d660a02fSimran Basi @param connectivity_class: Connectivity class to use, if None will decide 1282a5dc2568ec03419c46efe1752287e24d660a02fSimran Basi based off of hostname and config settings. 129d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng @param args: Args that will be passed to the constructor of 130d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng the new host class. 131d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng 132d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng @returns: A host object which is an instance of the newly created 133d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng host class. 134d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng """ 1351bf60eb788365f083d0ee8045a6556f906149decSimran Basi hostname, host_attributes = server_utils.get_host_info_from_machine( 1361bf60eb788365f083d0ee8045a6556f906149decSimran Basi machine) 1371bf60eb788365f083d0ee8045a6556f906149decSimran Basi args['host_attributes'] = host_attributes 138724b8a50f34b85dda9f1990e3e8c5b68edbdb37bSimran Basi ssh_user, ssh_pass, ssh_port, ssh_verbosity_flag, ssh_options = \ 139724b8a50f34b85dda9f1990e3e8c5b68edbdb37bSimran Basi _get_host_arguments() 140724b8a50f34b85dda9f1990e3e8c5b68edbdb37bSimran Basi 1410e72735393422ec11696d836f0756d96b0497380Gwendal Grignou hostname, args['user'], args['password'], ssh_port = \ 142724b8a50f34b85dda9f1990e3e8c5b68edbdb37bSimran Basi server_utils.parse_machine(hostname, ssh_user, ssh_pass, ssh_port) 143724b8a50f34b85dda9f1990e3e8c5b68edbdb37bSimran Basi args['ssh_verbosity_flag'] = ssh_verbosity_flag 144724b8a50f34b85dda9f1990e3e8c5b68edbdb37bSimran Basi args['ssh_options'] = ssh_options 1450e72735393422ec11696d836f0756d96b0497380Gwendal Grignou args['port'] = ssh_port 146431010f31bceddcf92f36e1b82397b9a56ffee54Simran Basi 1472a5dc2568ec03419c46efe1752287e24d660a02fSimran Basi if not connectivity_class: 1480e72735393422ec11696d836f0756d96b0497380Gwendal Grignou connectivity_class = _choose_connectivity_class(hostname, ssh_port) 14914622bb8249c2200e5258ea6d40b3362229e90a7Simran Basi host_attributes = args.get('host_attributes', {}) 15014622bb8249c2200e5258ea6d40b3362229e90a7Simran Basi host_class = host_class or OS_HOST_DICT.get(host_attributes.get('os_type')) 15114622bb8249c2200e5258ea6d40b3362229e90a7Simran Basi if host_class: 15214622bb8249c2200e5258ea6d40b3362229e90a7Simran Basi classes = [host_class, connectivity_class] 15314622bb8249c2200e5258ea6d40b3362229e90a7Simran Basi else: 154a5522a35b55c7cac915cf0b4efc69ead32e26d35Simran Basi classes = [_detect_host(connectivity_class, hostname, **args), 155a5522a35b55c7cac915cf0b4efc69ead32e26d35Simran Basi connectivity_class] 156635b06f6c016fd5e4e14e98c471e92b1f435ac46jadmanski 157635b06f6c016fd5e4e14e98c471e92b1f435ac46jadmanski # create a custom host class for this machine and return an instance of it 158635b06f6c016fd5e4e14e98c471e92b1f435ac46jadmanski host_class = type("%s_host" % hostname, tuple(classes), {}) 159d60321a8b012aa6101bcb237341fab5bd2d25a04jadmanski host_instance = host_class(hostname, **args) 160d60321a8b012aa6101bcb237341fab5bd2d25a04jadmanski 161d60321a8b012aa6101bcb237341fab5bd2d25a04jadmanski # call job_start if this is the first time this host is being used 162d60321a8b012aa6101bcb237341fab5bd2d25a04jadmanski if hostname not in _started_hostnames: 163d60321a8b012aa6101bcb237341fab5bd2d25a04jadmanski host_instance.job_start() 164d60321a8b012aa6101bcb237341fab5bd2d25a04jadmanski _started_hostnames.add(hostname) 165d60321a8b012aa6101bcb237341fab5bd2d25a04jadmanski 166d60321a8b012aa6101bcb237341fab5bd2d25a04jadmanski return host_instance 167549beb41550bf954394ca8c3a611397c97745759Kevin Cheng 168549beb41550bf954394ca8c3a611397c97745759Kevin Cheng 16952c785e0ac4ed92f54a88255fe33f45c235b22e3Simran Basidef create_testbed(machine, **kwargs): 170549beb41550bf954394ca8c3a611397c97745759Kevin Cheng """Create the testbed object. 171549beb41550bf954394ca8c3a611397c97745759Kevin Cheng 17252c785e0ac4ed92f54a88255fe33f45c235b22e3Simran Basi @param machine: A dict representing the test bed under test or a String 17352c785e0ac4ed92f54a88255fe33f45c235b22e3Simran Basi representing the testbed hostname (for legacy caller 17452c785e0ac4ed92f54a88255fe33f45c235b22e3Simran Basi support). 17552c785e0ac4ed92f54a88255fe33f45c235b22e3Simran Basi If it is a machine dict, the 'hostname' key is required. 17652c785e0ac4ed92f54a88255fe33f45c235b22e3Simran Basi Optional 'host_attributes' key will pipe in host_attributes 17752c785e0ac4ed92f54a88255fe33f45c235b22e3Simran Basi from the autoserv runtime or the AFE. 178debeb68446967a4319e1d7f9868a8b9abafce944Simran Basi @param kwargs: Keyword args to pass to the testbed initialization. 179549beb41550bf954394ca8c3a611397c97745759Kevin Cheng 180549beb41550bf954394ca8c3a611397c97745759Kevin Cheng @returns: The testbed object with all associated host objects instantiated. 181549beb41550bf954394ca8c3a611397c97745759Kevin Cheng """ 18252c785e0ac4ed92f54a88255fe33f45c235b22e3Simran Basi hostname, host_attributes = server_utils.get_host_info_from_machine( 18352c785e0ac4ed92f54a88255fe33f45c235b22e3Simran Basi machine) 18452c785e0ac4ed92f54a88255fe33f45c235b22e3Simran Basi kwargs['host_attributes'] = host_attributes 1853b11181c9ea282d584663d362bcfe2d1b4f9ba51Kevin Cheng return testbed.TestBed(hostname, **kwargs) 1863b11181c9ea282d584663d362bcfe2d1b4f9ba51Kevin Cheng 1873b11181c9ea282d584663d362bcfe2d1b4f9ba51Kevin Cheng 1883b11181c9ea282d584663d362bcfe2d1b4f9ba51Kevin Chengdef create_target_machine(machine, **kwargs): 1893b11181c9ea282d584663d362bcfe2d1b4f9ba51Kevin Cheng """Create the target machine which could be a testbed or a *Host. 1903b11181c9ea282d584663d362bcfe2d1b4f9ba51Kevin Cheng 1913b11181c9ea282d584663d362bcfe2d1b4f9ba51Kevin Cheng @param machine: A dict representing the test bed under test or a String 1923b11181c9ea282d584663d362bcfe2d1b4f9ba51Kevin Cheng representing the testbed hostname (for legacy caller 1933b11181c9ea282d584663d362bcfe2d1b4f9ba51Kevin Cheng support). 1943b11181c9ea282d584663d362bcfe2d1b4f9ba51Kevin Cheng If it is a machine dict, the 'hostname' key is required. 1953b11181c9ea282d584663d362bcfe2d1b4f9ba51Kevin Cheng Optional 'host_attributes' key will pipe in host_attributes 1963b11181c9ea282d584663d362bcfe2d1b4f9ba51Kevin Cheng from the autoserv runtime or the AFE. 1973b11181c9ea282d584663d362bcfe2d1b4f9ba51Kevin Cheng @param kwargs: Keyword args to pass to the testbed initialization. 1983b11181c9ea282d584663d362bcfe2d1b4f9ba51Kevin Cheng 1993b11181c9ea282d584663d362bcfe2d1b4f9ba51Kevin Cheng @returns: The target machine to be used for verify/repair. 2003b11181c9ea282d584663d362bcfe2d1b4f9ba51Kevin Cheng """ 2013b11181c9ea282d584663d362bcfe2d1b4f9ba51Kevin Cheng # TODO(kevcheng): We'll want to have a smarter way of figuring out which 2023b11181c9ea282d584663d362bcfe2d1b4f9ba51Kevin Cheng # host to create (checking host labels). 2033b11181c9ea282d584663d362bcfe2d1b4f9ba51Kevin Cheng if server_utils.machine_is_testbed(machine): 2043b11181c9ea282d584663d362bcfe2d1b4f9ba51Kevin Cheng return create_testbed(machine, **kwargs) 2053b11181c9ea282d584663d362bcfe2d1b4f9ba51Kevin Cheng return create_host(machine, **kwargs) 206