factory.py revision 3b11181c9ea282d584663d362bcfe2d1b4f9ba51
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 872a5dc2568ec03419c46efe1752287e24d660a02fSimran Basidef _choose_connectivity_class(hostname): 882a5dc2568ec03419c46efe1752287e24d660a02fSimran Basi """Choose a connectivity class for this hostname. 892a5dc2568ec03419c46efe1752287e24d660a02fSimran Basi 902a5dc2568ec03419c46efe1752287e24d660a02fSimran Basi @param hostname: hostname that we need a connectivity class for. 912a5dc2568ec03419c46efe1752287e24d660a02fSimran Basi 922a5dc2568ec03419c46efe1752287e24d660a02fSimran Basi @returns a connectivity host class. 932a5dc2568ec03419c46efe1752287e24d660a02fSimran Basi """ 942a5dc2568ec03419c46efe1752287e24d660a02fSimran Basi if hostname == 'localhost': 952a5dc2568ec03419c46efe1752287e24d660a02fSimran Basi return local_host.LocalHost 962a5dc2568ec03419c46efe1752287e24d660a02fSimran Basi # by default assume we're using SSH support 972a5dc2568ec03419c46efe1752287e24d660a02fSimran Basi elif SSH_ENGINE == 'paramiko': 982a5dc2568ec03419c46efe1752287e24d660a02fSimran Basi # Not all systems have paramiko installed so only import paramiko host 992a5dc2568ec03419c46efe1752287e24d660a02fSimran Basi # if the global_config settings call for it. 1002a5dc2568ec03419c46efe1752287e24d660a02fSimran Basi from autotest_lib.server.hosts import paramiko_host 1012a5dc2568ec03419c46efe1752287e24d660a02fSimran Basi return paramiko_host.ParamikoHost 1022a5dc2568ec03419c46efe1752287e24d660a02fSimran Basi elif SSH_ENGINE == 'raw_ssh': 1032a5dc2568ec03419c46efe1752287e24d660a02fSimran Basi return ssh_host.SSHHost 1042a5dc2568ec03419c46efe1752287e24d660a02fSimran Basi else: 1052a5dc2568ec03419c46efe1752287e24d660a02fSimran Basi raise error.AutoServError("Unknown SSH engine %s. Please verify the " 1062a5dc2568ec03419c46efe1752287e24d660a02fSimran Basi "value of the configuration key 'ssh_engine' " 1072a5dc2568ec03419c46efe1752287e24d660a02fSimran Basi "on autotest's global_config.ini file." % 1082a5dc2568ec03419c46efe1752287e24d660a02fSimran Basi SSH_ENGINE) 1092a5dc2568ec03419c46efe1752287e24d660a02fSimran Basi 1102a5dc2568ec03419c46efe1752287e24d660a02fSimran Basi 11185e864a0b6e00c2c2de0bbb698ff927822916a0eKevin Cheng# TODO(kevcheng): Update the creation method so it's not a research project 11285e864a0b6e00c2c2de0bbb698ff927822916a0eKevin Cheng# determining the class inheritance model. 1132a5dc2568ec03419c46efe1752287e24d660a02fSimran Basidef create_host(machine, host_class=None, connectivity_class=None, **args): 114d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng """Create a host object. 115d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng 116d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng This method mixes host classes that are needed into a new subclass 117d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng and creates a instance of the new class. 118d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng 1191bf60eb788365f083d0ee8045a6556f906149decSimran Basi @param machine: A dict representing the device under test or a String 1201bf60eb788365f083d0ee8045a6556f906149decSimran Basi representing the DUT hostname (for legacy caller support). 1211bf60eb788365f083d0ee8045a6556f906149decSimran Basi If it is a machine dict, the 'hostname' key is required. 1221bf60eb788365f083d0ee8045a6556f906149decSimran Basi Optional 'host_attributes' key will pipe in host_attributes 1231bf60eb788365f083d0ee8045a6556f906149decSimran Basi from the autoserv runtime or the AFE. 124a5522a35b55c7cac915cf0b4efc69ead32e26d35Simran Basi @param host_class: Host class to use, if None, will attempt to detect 125a5522a35b55c7cac915cf0b4efc69ead32e26d35Simran Basi the correct class. 1262a5dc2568ec03419c46efe1752287e24d660a02fSimran Basi @param connectivity_class: Connectivity class to use, if None will decide 1272a5dc2568ec03419c46efe1752287e24d660a02fSimran Basi based off of hostname and config settings. 128d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng @param args: Args that will be passed to the constructor of 129d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng the new host class. 130d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng 131d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng @returns: A host object which is an instance of the newly created 132d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng host class. 133d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng """ 1341bf60eb788365f083d0ee8045a6556f906149decSimran Basi hostname, host_attributes = server_utils.get_host_info_from_machine( 1351bf60eb788365f083d0ee8045a6556f906149decSimran Basi machine) 1361bf60eb788365f083d0ee8045a6556f906149decSimran Basi args['host_attributes'] = host_attributes 137724b8a50f34b85dda9f1990e3e8c5b68edbdb37bSimran Basi ssh_user, ssh_pass, ssh_port, ssh_verbosity_flag, ssh_options = \ 138724b8a50f34b85dda9f1990e3e8c5b68edbdb37bSimran Basi _get_host_arguments() 139724b8a50f34b85dda9f1990e3e8c5b68edbdb37bSimran Basi 140724b8a50f34b85dda9f1990e3e8c5b68edbdb37bSimran Basi hostname, args['user'], args['password'], args['port'] = \ 141724b8a50f34b85dda9f1990e3e8c5b68edbdb37bSimran Basi server_utils.parse_machine(hostname, ssh_user, ssh_pass, ssh_port) 142724b8a50f34b85dda9f1990e3e8c5b68edbdb37bSimran Basi args['ssh_verbosity_flag'] = ssh_verbosity_flag 143724b8a50f34b85dda9f1990e3e8c5b68edbdb37bSimran Basi args['ssh_options'] = ssh_options 144431010f31bceddcf92f36e1b82397b9a56ffee54Simran Basi 1452a5dc2568ec03419c46efe1752287e24d660a02fSimran Basi if not connectivity_class: 1462a5dc2568ec03419c46efe1752287e24d660a02fSimran Basi connectivity_class = _choose_connectivity_class(hostname) 14714622bb8249c2200e5258ea6d40b3362229e90a7Simran Basi host_attributes = args.get('host_attributes', {}) 14814622bb8249c2200e5258ea6d40b3362229e90a7Simran Basi host_class = host_class or OS_HOST_DICT.get(host_attributes.get('os_type')) 14914622bb8249c2200e5258ea6d40b3362229e90a7Simran Basi if host_class: 15014622bb8249c2200e5258ea6d40b3362229e90a7Simran Basi classes = [host_class, connectivity_class] 15114622bb8249c2200e5258ea6d40b3362229e90a7Simran Basi else: 152a5522a35b55c7cac915cf0b4efc69ead32e26d35Simran Basi classes = [_detect_host(connectivity_class, hostname, **args), 153a5522a35b55c7cac915cf0b4efc69ead32e26d35Simran Basi connectivity_class] 154635b06f6c016fd5e4e14e98c471e92b1f435ac46jadmanski 155635b06f6c016fd5e4e14e98c471e92b1f435ac46jadmanski # create a custom host class for this machine and return an instance of it 156635b06f6c016fd5e4e14e98c471e92b1f435ac46jadmanski host_class = type("%s_host" % hostname, tuple(classes), {}) 157d60321a8b012aa6101bcb237341fab5bd2d25a04jadmanski host_instance = host_class(hostname, **args) 158d60321a8b012aa6101bcb237341fab5bd2d25a04jadmanski 159d60321a8b012aa6101bcb237341fab5bd2d25a04jadmanski # call job_start if this is the first time this host is being used 160d60321a8b012aa6101bcb237341fab5bd2d25a04jadmanski if hostname not in _started_hostnames: 161d60321a8b012aa6101bcb237341fab5bd2d25a04jadmanski host_instance.job_start() 162d60321a8b012aa6101bcb237341fab5bd2d25a04jadmanski _started_hostnames.add(hostname) 163d60321a8b012aa6101bcb237341fab5bd2d25a04jadmanski 164d60321a8b012aa6101bcb237341fab5bd2d25a04jadmanski return host_instance 165549beb41550bf954394ca8c3a611397c97745759Kevin Cheng 166549beb41550bf954394ca8c3a611397c97745759Kevin Cheng 16752c785e0ac4ed92f54a88255fe33f45c235b22e3Simran Basidef create_testbed(machine, **kwargs): 168549beb41550bf954394ca8c3a611397c97745759Kevin Cheng """Create the testbed object. 169549beb41550bf954394ca8c3a611397c97745759Kevin Cheng 17052c785e0ac4ed92f54a88255fe33f45c235b22e3Simran Basi @param machine: A dict representing the test bed under test or a String 17152c785e0ac4ed92f54a88255fe33f45c235b22e3Simran Basi representing the testbed hostname (for legacy caller 17252c785e0ac4ed92f54a88255fe33f45c235b22e3Simran Basi support). 17352c785e0ac4ed92f54a88255fe33f45c235b22e3Simran Basi If it is a machine dict, the 'hostname' key is required. 17452c785e0ac4ed92f54a88255fe33f45c235b22e3Simran Basi Optional 'host_attributes' key will pipe in host_attributes 17552c785e0ac4ed92f54a88255fe33f45c235b22e3Simran Basi from the autoserv runtime or the AFE. 176debeb68446967a4319e1d7f9868a8b9abafce944Simran Basi @param kwargs: Keyword args to pass to the testbed initialization. 177549beb41550bf954394ca8c3a611397c97745759Kevin Cheng 178549beb41550bf954394ca8c3a611397c97745759Kevin Cheng @returns: The testbed object with all associated host objects instantiated. 179549beb41550bf954394ca8c3a611397c97745759Kevin Cheng """ 18052c785e0ac4ed92f54a88255fe33f45c235b22e3Simran Basi hostname, host_attributes = server_utils.get_host_info_from_machine( 18152c785e0ac4ed92f54a88255fe33f45c235b22e3Simran Basi machine) 18252c785e0ac4ed92f54a88255fe33f45c235b22e3Simran Basi kwargs['host_attributes'] = host_attributes 1833b11181c9ea282d584663d362bcfe2d1b4f9ba51Kevin Cheng return testbed.TestBed(hostname, **kwargs) 1843b11181c9ea282d584663d362bcfe2d1b4f9ba51Kevin Cheng 1853b11181c9ea282d584663d362bcfe2d1b4f9ba51Kevin Cheng 1863b11181c9ea282d584663d362bcfe2d1b4f9ba51Kevin Chengdef create_target_machine(machine, **kwargs): 1873b11181c9ea282d584663d362bcfe2d1b4f9ba51Kevin Cheng """Create the target machine which could be a testbed or a *Host. 1883b11181c9ea282d584663d362bcfe2d1b4f9ba51Kevin Cheng 1893b11181c9ea282d584663d362bcfe2d1b4f9ba51Kevin Cheng @param machine: A dict representing the test bed under test or a String 1903b11181c9ea282d584663d362bcfe2d1b4f9ba51Kevin Cheng representing the testbed hostname (for legacy caller 1913b11181c9ea282d584663d362bcfe2d1b4f9ba51Kevin Cheng support). 1923b11181c9ea282d584663d362bcfe2d1b4f9ba51Kevin Cheng If it is a machine dict, the 'hostname' key is required. 1933b11181c9ea282d584663d362bcfe2d1b4f9ba51Kevin Cheng Optional 'host_attributes' key will pipe in host_attributes 1943b11181c9ea282d584663d362bcfe2d1b4f9ba51Kevin Cheng from the autoserv runtime or the AFE. 1953b11181c9ea282d584663d362bcfe2d1b4f9ba51Kevin Cheng @param kwargs: Keyword args to pass to the testbed initialization. 1963b11181c9ea282d584663d362bcfe2d1b4f9ba51Kevin Cheng 1973b11181c9ea282d584663d362bcfe2d1b4f9ba51Kevin Cheng @returns: The target machine to be used for verify/repair. 1983b11181c9ea282d584663d362bcfe2d1b4f9ba51Kevin Cheng """ 1993b11181c9ea282d584663d362bcfe2d1b4f9ba51Kevin Cheng # TODO(kevcheng): We'll want to have a smarter way of figuring out which 2003b11181c9ea282d584663d362bcfe2d1b4f9ba51Kevin Cheng # host to create (checking host labels). 2013b11181c9ea282d584663d362bcfe2d1b4f9ba51Kevin Cheng if server_utils.machine_is_testbed(machine): 2023b11181c9ea282d584663d362bcfe2d1b4f9ba51Kevin Cheng return create_testbed(machine, **kwargs) 2033b11181c9ea282d584663d362bcfe2d1b4f9ba51Kevin Cheng return create_host(machine, **kwargs) 204