factory.py revision debeb68446967a4319e1d7f9868a8b9abafce944
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
841c3b853b2d9b80c9efcc1a27112731b96d765e6eJ. Richard Barnettedef create_host(hostname, 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
90d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng    @param hostname: A string representing the host name of the device.
91a5522a35b55c7cac915cf0b4efc69ead32e26d35Simran Basi    @param host_class: Host class to use, if None, will attempt to detect
92a5522a35b55c7cac915cf0b4efc69ead32e26d35Simran Basi                       the correct class.
93d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng    @param args: Args that will be passed to the constructor of
94d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng                 the new host class.
95d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng
96d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng    @returns: A host object which is an instance of the newly created
97d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng              host class.
98d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng    """
99724b8a50f34b85dda9f1990e3e8c5b68edbdb37bSimran Basi    ssh_user, ssh_pass, ssh_port, ssh_verbosity_flag, ssh_options = \
100724b8a50f34b85dda9f1990e3e8c5b68edbdb37bSimran Basi            _get_host_arguments()
101724b8a50f34b85dda9f1990e3e8c5b68edbdb37bSimran Basi
102724b8a50f34b85dda9f1990e3e8c5b68edbdb37bSimran Basi    hostname, args['user'], args['password'], args['port'] = \
103724b8a50f34b85dda9f1990e3e8c5b68edbdb37bSimran Basi            server_utils.parse_machine(hostname, ssh_user, ssh_pass, ssh_port)
104724b8a50f34b85dda9f1990e3e8c5b68edbdb37bSimran Basi    args['ssh_verbosity_flag'] = ssh_verbosity_flag
105724b8a50f34b85dda9f1990e3e8c5b68edbdb37bSimran Basi    args['ssh_options'] = ssh_options
106431010f31bceddcf92f36e1b82397b9a56ffee54Simran Basi
107635b06f6c016fd5e4e14e98c471e92b1f435ac46jadmanski    # by default assume we're using SSH support
10855552bf4a54d847f2cc30b307b3636c2be2ad5a2mbligh    if SSH_ENGINE == 'paramiko':
10955552bf4a54d847f2cc30b307b3636c2be2ad5a2mbligh        from autotest_lib.server.hosts import paramiko_host
110724b8a50f34b85dda9f1990e3e8c5b68edbdb37bSimran Basi        connectivity_class = paramiko_host.ParamikoHost
11155552bf4a54d847f2cc30b307b3636c2be2ad5a2mbligh    elif SSH_ENGINE == 'raw_ssh':
112724b8a50f34b85dda9f1990e3e8c5b68edbdb37bSimran Basi        connectivity_class = ssh_host.SSHHost
11355552bf4a54d847f2cc30b307b3636c2be2ad5a2mbligh    else:
11455552bf4a54d847f2cc30b307b3636c2be2ad5a2mbligh        raise error.AutoServError("Unknown SSH engine %s. Please verify the "
11555552bf4a54d847f2cc30b307b3636c2be2ad5a2mbligh                                  "value of the configuration key 'ssh_engine' "
11655552bf4a54d847f2cc30b307b3636c2be2ad5a2mbligh                                  "on autotest's global_config.ini file." %
11755552bf4a54d847f2cc30b307b3636c2be2ad5a2mbligh                                  SSH_ENGINE)
118635b06f6c016fd5e4e14e98c471e92b1f435ac46jadmanski
119a5522a35b55c7cac915cf0b4efc69ead32e26d35Simran Basi    if not host_class:
120a5522a35b55c7cac915cf0b4efc69ead32e26d35Simran Basi        classes = [_detect_host(connectivity_class, hostname, **args),
121a5522a35b55c7cac915cf0b4efc69ead32e26d35Simran Basi                   connectivity_class]
122a5522a35b55c7cac915cf0b4efc69ead32e26d35Simran Basi    else:
123a5522a35b55c7cac915cf0b4efc69ead32e26d35Simran Basi        classes = [host_class, connectivity_class]
124635b06f6c016fd5e4e14e98c471e92b1f435ac46jadmanski
125635b06f6c016fd5e4e14e98c471e92b1f435ac46jadmanski    # create a custom host class for this machine and return an instance of it
126635b06f6c016fd5e4e14e98c471e92b1f435ac46jadmanski    host_class = type("%s_host" % hostname, tuple(classes), {})
127d60321a8b012aa6101bcb237341fab5bd2d25a04jadmanski    host_instance = host_class(hostname, **args)
128d60321a8b012aa6101bcb237341fab5bd2d25a04jadmanski
129d60321a8b012aa6101bcb237341fab5bd2d25a04jadmanski    # call job_start if this is the first time this host is being used
130d60321a8b012aa6101bcb237341fab5bd2d25a04jadmanski    if hostname not in _started_hostnames:
131d60321a8b012aa6101bcb237341fab5bd2d25a04jadmanski        host_instance.job_start()
132d60321a8b012aa6101bcb237341fab5bd2d25a04jadmanski        _started_hostnames.add(hostname)
133d60321a8b012aa6101bcb237341fab5bd2d25a04jadmanski
134d60321a8b012aa6101bcb237341fab5bd2d25a04jadmanski    return host_instance
135549beb41550bf954394ca8c3a611397c97745759Kevin Cheng
136549beb41550bf954394ca8c3a611397c97745759Kevin Cheng
137debeb68446967a4319e1d7f9868a8b9abafce944Simran Basidef create_testbed(hostname, **kwargs):
138549beb41550bf954394ca8c3a611397c97745759Kevin Cheng    """Create the testbed object.
139549beb41550bf954394ca8c3a611397c97745759Kevin Cheng
140549beb41550bf954394ca8c3a611397c97745759Kevin Cheng    @param hostname: The hostname of the test station for this testbed.
141debeb68446967a4319e1d7f9868a8b9abafce944Simran Basi    @param kwargs: Keyword args to pass to the testbed initialization.
142549beb41550bf954394ca8c3a611397c97745759Kevin Cheng
143549beb41550bf954394ca8c3a611397c97745759Kevin Cheng    @returns: The testbed object with all associated host objects instantiated.
144549beb41550bf954394ca8c3a611397c97745759Kevin Cheng    """
145debeb68446967a4319e1d7f9868a8b9abafce944Simran Basi    return testbed.TestBed(hostname, kwargs)
146