factory.py revision e5f7ae44738af9f6138103e7c57c26b415486ce8
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
7c6136e92bb5aef1acda979fd303fc855cf704434jadmanskifrom autotest_lib.server import autotest, utils as server_utils
80ca40e28b664c76d62909b1a344738dfb6f7e936Fang Dengfrom autotest_lib.server.hosts import site_factory, cros_host, ssh_host, serial
9e5f7ae44738af9f6138103e7c57c26b415486ce8Simran Basifrom autotest_lib.server.hosts import moblab_host, sonic_host
10431010f31bceddcf92f36e1b82397b9a56ffee54Simran Basifrom autotest_lib.server.hosts import adb_host, logfile_monitor
11431010f31bceddcf92f36e1b82397b9a56ffee54Simran Basi
12e48bcfb879c943df72a866e3db8b2df8e930b2c1mbligh
13d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng
14e48bcfb879c943df72a866e3db8b2df8e930b2c1mblighDEFAULT_FOLLOW_PATH = '/var/log/kern.log'
15e48bcfb879c943df72a866e3db8b2df8e930b2c1mblighDEFAULT_PATTERNS_PATH = 'console_patterns'
1655552bf4a54d847f2cc30b307b3636c2be2ad5a2mblighSSH_ENGINE = global_config.global_config.get_config_value('AUTOSERV',
1755552bf4a54d847f2cc30b307b3636c2be2ad5a2mbligh                                                          'ssh_engine',
1855552bf4a54d847f2cc30b307b3636c2be2ad5a2mbligh                                                          type=str)
19d0672689266d3d63901f3a35fb7e0a2d96d6e39abeeps
20d0672689266d3d63901f3a35fb7e0a2d96d6e39abeeps# Default ssh options used in creating a host.
21d0672689266d3d63901f3a35fb7e0a2d96d6e39abeepsDEFAULT_SSH_USER = 'root'
22d0672689266d3d63901f3a35fb7e0a2d96d6e39abeepsDEFAULT_SSH_PASS = ''
23d0672689266d3d63901f3a35fb7e0a2d96d6e39abeepsDEFAULT_SSH_PORT = 22
24d0672689266d3d63901f3a35fb7e0a2d96d6e39abeepsDEFAULT_SSH_VERBOSITY = ''
25d0672689266d3d63901f3a35fb7e0a2d96d6e39abeepsDEFAULT_SSH_OPTIONS = ''
26d0672689266d3d63901f3a35fb7e0a2d96d6e39abeeps
27d60321a8b012aa6101bcb237341fab5bd2d25a04jadmanski# for tracking which hostnames have already had job_start called
28d60321a8b012aa6101bcb237341fab5bd2d25a04jadmanski_started_hostnames = set()
29d60321a8b012aa6101bcb237341fab5bd2d25a04jadmanski
3046dadc9439355f72d394dcc4700902001bd797ffbeeps# A list of all the possible host types, ordered according to frequency of
3146dadc9439355f72d394dcc4700902001bd797ffbeeps# host types in the lab, so the more common hosts don't incur a repeated ssh
3246dadc9439355f72d394dcc4700902001bd797ffbeeps# overhead in checking for less common host types.
33e5f7ae44738af9f6138103e7c57c26b415486ce8Simran Basihost_types = [cros_host.CrosHost, moblab_host.MoblabHost, sonic_host.SonicHost,
34e5f7ae44738af9f6138103e7c57c26b415486ce8Simran Basi              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
87e48bcfb879c943df72a866e3db8b2df8e930b2c1mblighdef create_host(
883693fc8d1136f17c47bac3e4a2faeb525301e05aDale Curtis    hostname, auto_monitor=False, follow_paths=None, pattern_paths=None,
89724b8a50f34b85dda9f1990e3e8c5b68edbdb37bSimran Basi    netconsole=False, **args):
90d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng    """Create a host object.
91d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng
92d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng    This method mixes host classes that are needed into a new subclass
93d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng    and creates a instance of the new class.
94d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng
95d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng    @param hostname: A string representing the host name of the device.
96d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng    @param auto_monitor: A boolean value, if True, will try to mix
97d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng                         SerialHost in. If the host supports use as SerialHost,
98d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng                         will not mix in LogfileMonitorMixin anymore.
99d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng                         If the host doesn't support it, will
100d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng                         fall back to direct demesg logging and mix
101d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng                         LogfileMonitorMixin in.
102d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng    @param follow_paths: A list, passed to LogfileMonitorMixin,
103d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng                         remote paths to monitor.
104d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng    @param pattern_paths: A list, passed to LogfileMonitorMixin,
105d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng                          local paths to alert pattern definition files.
106d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng    @param netconsole: A boolean value, if True, will mix NetconsoleHost in.
107d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng    @param args: Args that will be passed to the constructor of
108d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng                 the new host class.
109431010f31bceddcf92f36e1b82397b9a56ffee54Simran Basi    @param adb: If True creates an instance of adb_host not cros_host.
110d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng
111d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng    @returns: A host object which is an instance of the newly created
112d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng              host class.
113d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng    """
11496667caad23538696f737100803e68a1c1ecf357Fang Deng
115724b8a50f34b85dda9f1990e3e8c5b68edbdb37bSimran Basi    ssh_user, ssh_pass, ssh_port, ssh_verbosity_flag, ssh_options = \
116724b8a50f34b85dda9f1990e3e8c5b68edbdb37bSimran Basi            _get_host_arguments()
117724b8a50f34b85dda9f1990e3e8c5b68edbdb37bSimran Basi
118724b8a50f34b85dda9f1990e3e8c5b68edbdb37bSimran Basi    hostname, args['user'], args['password'], args['port'] = \
119724b8a50f34b85dda9f1990e3e8c5b68edbdb37bSimran Basi            server_utils.parse_machine(hostname, ssh_user, ssh_pass, ssh_port)
120724b8a50f34b85dda9f1990e3e8c5b68edbdb37bSimran Basi    args['ssh_verbosity_flag'] = ssh_verbosity_flag
121724b8a50f34b85dda9f1990e3e8c5b68edbdb37bSimran Basi    args['ssh_options'] = ssh_options
122431010f31bceddcf92f36e1b82397b9a56ffee54Simran Basi
123635b06f6c016fd5e4e14e98c471e92b1f435ac46jadmanski    # by default assume we're using SSH support
12455552bf4a54d847f2cc30b307b3636c2be2ad5a2mbligh    if SSH_ENGINE == 'paramiko':
12555552bf4a54d847f2cc30b307b3636c2be2ad5a2mbligh        from autotest_lib.server.hosts import paramiko_host
126724b8a50f34b85dda9f1990e3e8c5b68edbdb37bSimran Basi        connectivity_class = paramiko_host.ParamikoHost
12755552bf4a54d847f2cc30b307b3636c2be2ad5a2mbligh    elif SSH_ENGINE == 'raw_ssh':
128724b8a50f34b85dda9f1990e3e8c5b68edbdb37bSimran Basi        connectivity_class = ssh_host.SSHHost
12955552bf4a54d847f2cc30b307b3636c2be2ad5a2mbligh    else:
13055552bf4a54d847f2cc30b307b3636c2be2ad5a2mbligh        raise error.AutoServError("Unknown SSH engine %s. Please verify the "
13155552bf4a54d847f2cc30b307b3636c2be2ad5a2mbligh                                  "value of the configuration key 'ssh_engine' "
13255552bf4a54d847f2cc30b307b3636c2be2ad5a2mbligh                                  "on autotest's global_config.ini file." %
13355552bf4a54d847f2cc30b307b3636c2be2ad5a2mbligh                                  SSH_ENGINE)
134635b06f6c016fd5e4e14e98c471e92b1f435ac46jadmanski
135724b8a50f34b85dda9f1990e3e8c5b68edbdb37bSimran Basi    classes = [_detect_host(connectivity_class, hostname, **args),
136724b8a50f34b85dda9f1990e3e8c5b68edbdb37bSimran Basi               connectivity_class]
137c6136e92bb5aef1acda979fd303fc855cf704434jadmanski    # by default mix in run_test support
138c6136e92bb5aef1acda979fd303fc855cf704434jadmanski    classes.append(autotest.AutotestHostMixin)
139c6136e92bb5aef1acda979fd303fc855cf704434jadmanski
140cb4aac7441db2fa1880d06ba57b1ab354fdbc18ajadmanski    # if the user really wants to use netconsole, let them
141cb4aac7441db2fa1880d06ba57b1ab354fdbc18ajadmanski    if netconsole:
142cb4aac7441db2fa1880d06ba57b1ab354fdbc18ajadmanski        classes.append(netconsole.NetconsoleHost)
143cb4aac7441db2fa1880d06ba57b1ab354fdbc18ajadmanski
14454f90af69daac8a781678f0ccd42d6728d5d81d1jadmanski    if auto_monitor:
14554f90af69daac8a781678f0ccd42d6728d5d81d1jadmanski        # use serial console support if it's available
14654f90af69daac8a781678f0ccd42d6728d5d81d1jadmanski        conmux_args = {}
14754f90af69daac8a781678f0ccd42d6728d5d81d1jadmanski        for key in ("conmux_server", "conmux_attach"):
14854f90af69daac8a781678f0ccd42d6728d5d81d1jadmanski            if key in args:
14954f90af69daac8a781678f0ccd42d6728d5d81d1jadmanski                conmux_args[key] = args[key]
15054f90af69daac8a781678f0ccd42d6728d5d81d1jadmanski        if serial.SerialHost.host_is_supported(hostname, **conmux_args):
15154f90af69daac8a781678f0ccd42d6728d5d81d1jadmanski            classes.append(serial.SerialHost)
152635b06f6c016fd5e4e14e98c471e92b1f435ac46jadmanski        else:
153af959a4e2909cd0013cc58d22ca4527570995319jadmanski            # no serial available, fall back to direct dmesg logging
154af959a4e2909cd0013cc58d22ca4527570995319jadmanski            if follow_paths is None:
155af959a4e2909cd0013cc58d22ca4527570995319jadmanski                follow_paths = [DEFAULT_FOLLOW_PATH]
15654f90af69daac8a781678f0ccd42d6728d5d81d1jadmanski            else:
157af959a4e2909cd0013cc58d22ca4527570995319jadmanski                follow_paths = list(follow_paths) + [DEFAULT_FOLLOW_PATH]
158e48bcfb879c943df72a866e3db8b2df8e930b2c1mbligh
159af959a4e2909cd0013cc58d22ca4527570995319jadmanski            if pattern_paths is None:
160af959a4e2909cd0013cc58d22ca4527570995319jadmanski                pattern_paths = [DEFAULT_PATTERNS_PATH]
161af959a4e2909cd0013cc58d22ca4527570995319jadmanski            else:
162af959a4e2909cd0013cc58d22ca4527570995319jadmanski                pattern_paths = (
163af959a4e2909cd0013cc58d22ca4527570995319jadmanski                    list(pattern_paths) + [DEFAULT_PATTERNS_PATH])
164e48bcfb879c943df72a866e3db8b2df8e930b2c1mbligh
165af959a4e2909cd0013cc58d22ca4527570995319jadmanski            logfile_monitor_class = logfile_monitor.NewLogfileMonitorMixin(
166af959a4e2909cd0013cc58d22ca4527570995319jadmanski                follow_paths, pattern_paths)
167af959a4e2909cd0013cc58d22ca4527570995319jadmanski            classes.append(logfile_monitor_class)
168e48bcfb879c943df72a866e3db8b2df8e930b2c1mbligh
169e48bcfb879c943df72a866e3db8b2df8e930b2c1mbligh    elif follow_paths:
170e48bcfb879c943df72a866e3db8b2df8e930b2c1mbligh        logfile_monitor_class = logfile_monitor.NewLogfileMonitorMixin(
171e48bcfb879c943df72a866e3db8b2df8e930b2c1mbligh            follow_paths, pattern_paths)
172e48bcfb879c943df72a866e3db8b2df8e930b2c1mbligh        classes.append(logfile_monitor_class)
173635b06f6c016fd5e4e14e98c471e92b1f435ac46jadmanski
174635b06f6c016fd5e4e14e98c471e92b1f435ac46jadmanski    # do any site-specific processing of the classes list
175d60321a8b012aa6101bcb237341fab5bd2d25a04jadmanski    site_factory.postprocess_classes(classes, hostname,
176ea90226c42c24de00839d2c68a04909b233e9a4fjadmanski                                     auto_monitor=auto_monitor, **args)
177635b06f6c016fd5e4e14e98c471e92b1f435ac46jadmanski
178635b06f6c016fd5e4e14e98c471e92b1f435ac46jadmanski    # create a custom host class for this machine and return an instance of it
179635b06f6c016fd5e4e14e98c471e92b1f435ac46jadmanski    host_class = type("%s_host" % hostname, tuple(classes), {})
180d60321a8b012aa6101bcb237341fab5bd2d25a04jadmanski    host_instance = host_class(hostname, **args)
181d60321a8b012aa6101bcb237341fab5bd2d25a04jadmanski
182d60321a8b012aa6101bcb237341fab5bd2d25a04jadmanski    # call job_start if this is the first time this host is being used
183d60321a8b012aa6101bcb237341fab5bd2d25a04jadmanski    if hostname not in _started_hostnames:
184d60321a8b012aa6101bcb237341fab5bd2d25a04jadmanski        host_instance.job_start()
185d60321a8b012aa6101bcb237341fab5bd2d25a04jadmanski        _started_hostnames.add(hostname)
186d60321a8b012aa6101bcb237341fab5bd2d25a04jadmanski
187d60321a8b012aa6101bcb237341fab5bd2d25a04jadmanski    return host_instance
188