factory.py revision 1bf60eb788365f083d0ee8045a6556f906149dec
1d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng"""Provides a factory method to create a host object."""
2d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng
346dadc9439355f72d394dcc4700902001bd797ffbeepsimport logging
45189dd5255f3cd8d68026b081367c32d135dd9b6Aviv Keshetfrom contextlib import closing
546dadc9439355f72d394dcc4700902001bd797ffbeeps
6c5947faa755945f81537c6c33c322dccacac0adeAviv Keshetfrom autotest_lib.client.common_lib import error, global_config
71c3b853b2d9b80c9efcc1a27112731b96d765e6eJ. Richard Barnettefrom autotest_lib.server import utils as server_utils
81c3b853b2d9b80c9efcc1a27112731b96d765e6eJ. Richard Barnettefrom autotest_lib.server.hosts import cros_host, ssh_host
9e5f7ae44738af9f6138103e7c57c26b415486ce8Simran Basifrom autotest_lib.server.hosts import moblab_host, sonic_host
10549beb41550bf954394ca8c3a611397c97745759Kevin Chengfrom autotest_lib.server.hosts import adb_host, testbed
11e48bcfb879c943df72a866e3db8b2df8e930b2c1mbligh
12d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng
1355552bf4a54d847f2cc30b307b3636c2be2ad5a2mblighSSH_ENGINE = global_config.global_config.get_config_value('AUTOSERV',
1455552bf4a54d847f2cc30b307b3636c2be2ad5a2mbligh                                                          'ssh_engine',
1555552bf4a54d847f2cc30b307b3636c2be2ad5a2mbligh                                                          type=str)
16d0672689266d3d63901f3a35fb7e0a2d96d6e39abeeps
17d0672689266d3d63901f3a35fb7e0a2d96d6e39abeeps# Default ssh options used in creating a host.
18d0672689266d3d63901f3a35fb7e0a2d96d6e39abeepsDEFAULT_SSH_USER = 'root'
19d0672689266d3d63901f3a35fb7e0a2d96d6e39abeepsDEFAULT_SSH_PASS = ''
20d0672689266d3d63901f3a35fb7e0a2d96d6e39abeepsDEFAULT_SSH_PORT = 22
21d0672689266d3d63901f3a35fb7e0a2d96d6e39abeepsDEFAULT_SSH_VERBOSITY = ''
22d0672689266d3d63901f3a35fb7e0a2d96d6e39abeepsDEFAULT_SSH_OPTIONS = ''
23d0672689266d3d63901f3a35fb7e0a2d96d6e39abeeps
24d60321a8b012aa6101bcb237341fab5bd2d25a04jadmanski# for tracking which hostnames have already had job_start called
25d60321a8b012aa6101bcb237341fab5bd2d25a04jadmanski_started_hostnames = set()
26d60321a8b012aa6101bcb237341fab5bd2d25a04jadmanski
2746dadc9439355f72d394dcc4700902001bd797ffbeeps# A list of all the possible host types, ordered according to frequency of
2846dadc9439355f72d394dcc4700902001bd797ffbeeps# host types in the lab, so the more common hosts don't incur a repeated ssh
2946dadc9439355f72d394dcc4700902001bd797ffbeeps# overhead in checking for less common host types.
30e5f7ae44738af9f6138103e7c57c26b415486ce8Simran Basihost_types = [cros_host.CrosHost, moblab_host.MoblabHost, sonic_host.SonicHost,
31e5f7ae44738af9f6138103e7c57c26b415486ce8Simran Basi              adb_host.ADBHost,]
3246dadc9439355f72d394dcc4700902001bd797ffbeeps
33d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng
34d0672689266d3d63901f3a35fb7e0a2d96d6e39abeepsdef _get_host_arguments():
35d0672689266d3d63901f3a35fb7e0a2d96d6e39abeeps    """Returns parameters needed to ssh into a host.
36d0672689266d3d63901f3a35fb7e0a2d96d6e39abeeps
37d0672689266d3d63901f3a35fb7e0a2d96d6e39abeeps    There are currently 2 use cases for creating a host.
38d0672689266d3d63901f3a35fb7e0a2d96d6e39abeeps    1. Through the server_job, in which case the server_job injects
39d0672689266d3d63901f3a35fb7e0a2d96d6e39abeeps       the appropriate ssh parameters into our name space and they
40d0672689266d3d63901f3a35fb7e0a2d96d6e39abeeps       are available as the variables ssh_user, ssh_pass etc.
41d0672689266d3d63901f3a35fb7e0a2d96d6e39abeeps    2. Directly through factory.create_host, in which case we use
42d0672689266d3d63901f3a35fb7e0a2d96d6e39abeeps       the same defaults as used in the server job to create a host.
43d0672689266d3d63901f3a35fb7e0a2d96d6e39abeeps
44d0672689266d3d63901f3a35fb7e0a2d96d6e39abeeps    @returns: A tuple of parameters needed to create an ssh connection, ordered
45d0672689266d3d63901f3a35fb7e0a2d96d6e39abeeps              as: ssh_user, ssh_pass, ssh_port, ssh_verbosity, ssh_options.
46d0672689266d3d63901f3a35fb7e0a2d96d6e39abeeps    """
47d0672689266d3d63901f3a35fb7e0a2d96d6e39abeeps    g = globals()
48d0672689266d3d63901f3a35fb7e0a2d96d6e39abeeps    return (g.get('ssh_user', DEFAULT_SSH_USER),
49d0672689266d3d63901f3a35fb7e0a2d96d6e39abeeps            g.get('ssh_pass', DEFAULT_SSH_PASS),
50d0672689266d3d63901f3a35fb7e0a2d96d6e39abeeps            g.get('ssh_port', DEFAULT_SSH_PORT),
51d0672689266d3d63901f3a35fb7e0a2d96d6e39abeeps            g.get('ssh_verbosity_flag', DEFAULT_SSH_VERBOSITY),
52d0672689266d3d63901f3a35fb7e0a2d96d6e39abeeps            g.get('ssh_options', DEFAULT_SSH_OPTIONS))
53d0672689266d3d63901f3a35fb7e0a2d96d6e39abeeps
54d0672689266d3d63901f3a35fb7e0a2d96d6e39abeeps
55724b8a50f34b85dda9f1990e3e8c5b68edbdb37bSimran Basidef _detect_host(connectivity_class, hostname, **args):
56724b8a50f34b85dda9f1990e3e8c5b68edbdb37bSimran Basi    """Detect host type.
57724b8a50f34b85dda9f1990e3e8c5b68edbdb37bSimran Basi
5846dadc9439355f72d394dcc4700902001bd797ffbeeps    Goes through all the possible host classes, calling check_host with a
5946dadc9439355f72d394dcc4700902001bd797ffbeeps    basic host object. Currently this is an ssh host, but theoretically it
6046dadc9439355f72d394dcc4700902001bd797ffbeeps    can be any host object that the check_host method of appropriate host
6146dadc9439355f72d394dcc4700902001bd797ffbeeps    type knows to use.
62724b8a50f34b85dda9f1990e3e8c5b68edbdb37bSimran Basi
63724b8a50f34b85dda9f1990e3e8c5b68edbdb37bSimran Basi    @param connectivity_class: connectivity class to use to talk to the host
64724b8a50f34b85dda9f1990e3e8c5b68edbdb37bSimran Basi                               (ParamikoHost or SSHHost)
65724b8a50f34b85dda9f1990e3e8c5b68edbdb37bSimran Basi    @param hostname: A string representing the host name of the device.
66724b8a50f34b85dda9f1990e3e8c5b68edbdb37bSimran Basi    @param args: Args that will be passed to the constructor of
67724b8a50f34b85dda9f1990e3e8c5b68edbdb37bSimran Basi                 the host class.
68724b8a50f34b85dda9f1990e3e8c5b68edbdb37bSimran Basi
6946dadc9439355f72d394dcc4700902001bd797ffbeeps    @returns: Class type of the first host class that returns True to the
7046dadc9439355f72d394dcc4700902001bd797ffbeeps              check_host method.
71724b8a50f34b85dda9f1990e3e8c5b68edbdb37bSimran Basi    """
7246dadc9439355f72d394dcc4700902001bd797ffbeeps    # TODO crbug.com/302026 (sbasi) - adjust this pathway for ADBHost in
7346dadc9439355f72d394dcc4700902001bd797ffbeeps    # the future should a host require verify/repair.
7446dadc9439355f72d394dcc4700902001bd797ffbeeps    with closing(connectivity_class(hostname, **args)) as host:
7546dadc9439355f72d394dcc4700902001bd797ffbeeps        for host_module in host_types:
7646dadc9439355f72d394dcc4700902001bd797ffbeeps            if host_module.check_host(host, timeout=10):
7746dadc9439355f72d394dcc4700902001bd797ffbeeps                return host_module
7846dadc9439355f72d394dcc4700902001bd797ffbeeps
7946dadc9439355f72d394dcc4700902001bd797ffbeeps    logging.warning('Unable to apply conventional host detection methods, '
8046dadc9439355f72d394dcc4700902001bd797ffbeeps                    'defaulting to chromeos host.')
8146dadc9439355f72d394dcc4700902001bd797ffbeeps    return cros_host.CrosHost
82724b8a50f34b85dda9f1990e3e8c5b68edbdb37bSimran Basi
83724b8a50f34b85dda9f1990e3e8c5b68edbdb37bSimran Basi
841bf60eb788365f083d0ee8045a6556f906149decSimran Basidef create_host(machine, host_class=None, **args):
85d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng    """Create a host object.
86d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng
87d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng    This method mixes host classes that are needed into a new subclass
88d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng    and creates a instance of the new class.
89d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng
901bf60eb788365f083d0ee8045a6556f906149decSimran Basi    @param machine: A dict representing the device under test or a String
911bf60eb788365f083d0ee8045a6556f906149decSimran Basi                    representing the DUT hostname (for legacy caller support).
921bf60eb788365f083d0ee8045a6556f906149decSimran Basi                    If it is a machine dict, the 'hostname' key is required.
931bf60eb788365f083d0ee8045a6556f906149decSimran Basi                    Optional 'host_attributes' key will pipe in host_attributes
941bf60eb788365f083d0ee8045a6556f906149decSimran Basi                    from the autoserv runtime or the AFE.
95a5522a35b55c7cac915cf0b4efc69ead32e26d35Simran Basi    @param host_class: Host class to use, if None, will attempt to detect
96a5522a35b55c7cac915cf0b4efc69ead32e26d35Simran Basi                       the correct class.
97d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng    @param args: Args that will be passed to the constructor of
98d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng                 the new host class.
99d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng
100d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng    @returns: A host object which is an instance of the newly created
101d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng              host class.
102d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng    """
1031bf60eb788365f083d0ee8045a6556f906149decSimran Basi    hostname, host_attributes = server_utils.get_host_info_from_machine(
1041bf60eb788365f083d0ee8045a6556f906149decSimran Basi            machine)
1051bf60eb788365f083d0ee8045a6556f906149decSimran Basi    args['host_attributes'] = host_attributes
106724b8a50f34b85dda9f1990e3e8c5b68edbdb37bSimran Basi    ssh_user, ssh_pass, ssh_port, ssh_verbosity_flag, ssh_options = \
107724b8a50f34b85dda9f1990e3e8c5b68edbdb37bSimran Basi            _get_host_arguments()
108724b8a50f34b85dda9f1990e3e8c5b68edbdb37bSimran Basi
109724b8a50f34b85dda9f1990e3e8c5b68edbdb37bSimran Basi    hostname, args['user'], args['password'], args['port'] = \
110724b8a50f34b85dda9f1990e3e8c5b68edbdb37bSimran Basi            server_utils.parse_machine(hostname, ssh_user, ssh_pass, ssh_port)
111724b8a50f34b85dda9f1990e3e8c5b68edbdb37bSimran Basi    args['ssh_verbosity_flag'] = ssh_verbosity_flag
112724b8a50f34b85dda9f1990e3e8c5b68edbdb37bSimran Basi    args['ssh_options'] = ssh_options
113431010f31bceddcf92f36e1b82397b9a56ffee54Simran Basi
114635b06f6c016fd5e4e14e98c471e92b1f435ac46jadmanski    # by default assume we're using SSH support
11555552bf4a54d847f2cc30b307b3636c2be2ad5a2mbligh    if SSH_ENGINE == 'paramiko':
11655552bf4a54d847f2cc30b307b3636c2be2ad5a2mbligh        from autotest_lib.server.hosts import paramiko_host
117724b8a50f34b85dda9f1990e3e8c5b68edbdb37bSimran Basi        connectivity_class = paramiko_host.ParamikoHost
11855552bf4a54d847f2cc30b307b3636c2be2ad5a2mbligh    elif SSH_ENGINE == 'raw_ssh':
119724b8a50f34b85dda9f1990e3e8c5b68edbdb37bSimran Basi        connectivity_class = ssh_host.SSHHost
12055552bf4a54d847f2cc30b307b3636c2be2ad5a2mbligh    else:
12155552bf4a54d847f2cc30b307b3636c2be2ad5a2mbligh        raise error.AutoServError("Unknown SSH engine %s. Please verify the "
12255552bf4a54d847f2cc30b307b3636c2be2ad5a2mbligh                                  "value of the configuration key 'ssh_engine' "
12355552bf4a54d847f2cc30b307b3636c2be2ad5a2mbligh                                  "on autotest's global_config.ini file." %
12455552bf4a54d847f2cc30b307b3636c2be2ad5a2mbligh                                  SSH_ENGINE)
125635b06f6c016fd5e4e14e98c471e92b1f435ac46jadmanski
126a5522a35b55c7cac915cf0b4efc69ead32e26d35Simran Basi    if not host_class:
127a5522a35b55c7cac915cf0b4efc69ead32e26d35Simran Basi        classes = [_detect_host(connectivity_class, hostname, **args),
128a5522a35b55c7cac915cf0b4efc69ead32e26d35Simran Basi                   connectivity_class]
129a5522a35b55c7cac915cf0b4efc69ead32e26d35Simran Basi    else:
130a5522a35b55c7cac915cf0b4efc69ead32e26d35Simran Basi        classes = [host_class, connectivity_class]
131635b06f6c016fd5e4e14e98c471e92b1f435ac46jadmanski
132635b06f6c016fd5e4e14e98c471e92b1f435ac46jadmanski    # create a custom host class for this machine and return an instance of it
133635b06f6c016fd5e4e14e98c471e92b1f435ac46jadmanski    host_class = type("%s_host" % hostname, tuple(classes), {})
134d60321a8b012aa6101bcb237341fab5bd2d25a04jadmanski    host_instance = host_class(hostname, **args)
135d60321a8b012aa6101bcb237341fab5bd2d25a04jadmanski
136d60321a8b012aa6101bcb237341fab5bd2d25a04jadmanski    # call job_start if this is the first time this host is being used
137d60321a8b012aa6101bcb237341fab5bd2d25a04jadmanski    if hostname not in _started_hostnames:
138d60321a8b012aa6101bcb237341fab5bd2d25a04jadmanski        host_instance.job_start()
139d60321a8b012aa6101bcb237341fab5bd2d25a04jadmanski        _started_hostnames.add(hostname)
140d60321a8b012aa6101bcb237341fab5bd2d25a04jadmanski
141d60321a8b012aa6101bcb237341fab5bd2d25a04jadmanski    return host_instance
142549beb41550bf954394ca8c3a611397c97745759Kevin Cheng
143549beb41550bf954394ca8c3a611397c97745759Kevin Cheng
144debeb68446967a4319e1d7f9868a8b9abafce944Simran Basidef create_testbed(hostname, **kwargs):
145549beb41550bf954394ca8c3a611397c97745759Kevin Cheng    """Create the testbed object.
146549beb41550bf954394ca8c3a611397c97745759Kevin Cheng
147549beb41550bf954394ca8c3a611397c97745759Kevin Cheng    @param hostname: The hostname of the test station for this testbed.
148debeb68446967a4319e1d7f9868a8b9abafce944Simran Basi    @param kwargs: Keyword args to pass to the testbed initialization.
149549beb41550bf954394ca8c3a611397c97745759Kevin Cheng
150549beb41550bf954394ca8c3a611397c97745759Kevin Cheng    @returns: The testbed object with all associated host objects instantiated.
151549beb41550bf954394ca8c3a611397c97745759Kevin Cheng    """
152debeb68446967a4319e1d7f9868a8b9abafce944Simran Basi    return testbed.TestBed(hostname, kwargs)
153