factory.py revision 46dadc9439355f72d394dcc4700902001bd797ff
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
946dadc9439355f72d394dcc4700902001bd797ffbeepsfrom autotest_lib.server.hosts import eureka_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.
3346dadc9439355f72d394dcc4700902001bd797ffbeepshost_types = [cros_host.CrosHost, eureka_host.EurekaHost, adb_host.ADBHost,]
3446dadc9439355f72d394dcc4700902001bd797ffbeeps
35d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng
36d0672689266d3d63901f3a35fb7e0a2d96d6e39abeepsdef _get_host_arguments():
37d0672689266d3d63901f3a35fb7e0a2d96d6e39abeeps    """Returns parameters needed to ssh into a host.
38d0672689266d3d63901f3a35fb7e0a2d96d6e39abeeps
39d0672689266d3d63901f3a35fb7e0a2d96d6e39abeeps    There are currently 2 use cases for creating a host.
40d0672689266d3d63901f3a35fb7e0a2d96d6e39abeeps    1. Through the server_job, in which case the server_job injects
41d0672689266d3d63901f3a35fb7e0a2d96d6e39abeeps       the appropriate ssh parameters into our name space and they
42d0672689266d3d63901f3a35fb7e0a2d96d6e39abeeps       are available as the variables ssh_user, ssh_pass etc.
43d0672689266d3d63901f3a35fb7e0a2d96d6e39abeeps    2. Directly through factory.create_host, in which case we use
44d0672689266d3d63901f3a35fb7e0a2d96d6e39abeeps       the same defaults as used in the server job to create a host.
45d0672689266d3d63901f3a35fb7e0a2d96d6e39abeeps
46d0672689266d3d63901f3a35fb7e0a2d96d6e39abeeps    @returns: A tuple of parameters needed to create an ssh connection, ordered
47d0672689266d3d63901f3a35fb7e0a2d96d6e39abeeps              as: ssh_user, ssh_pass, ssh_port, ssh_verbosity, ssh_options.
48d0672689266d3d63901f3a35fb7e0a2d96d6e39abeeps    """
49d0672689266d3d63901f3a35fb7e0a2d96d6e39abeeps    g = globals()
50d0672689266d3d63901f3a35fb7e0a2d96d6e39abeeps    return (g.get('ssh_user', DEFAULT_SSH_USER),
51d0672689266d3d63901f3a35fb7e0a2d96d6e39abeeps            g.get('ssh_pass', DEFAULT_SSH_PASS),
52d0672689266d3d63901f3a35fb7e0a2d96d6e39abeeps            g.get('ssh_port', DEFAULT_SSH_PORT),
53d0672689266d3d63901f3a35fb7e0a2d96d6e39abeeps            g.get('ssh_verbosity_flag', DEFAULT_SSH_VERBOSITY),
54d0672689266d3d63901f3a35fb7e0a2d96d6e39abeeps            g.get('ssh_options', DEFAULT_SSH_OPTIONS))
55d0672689266d3d63901f3a35fb7e0a2d96d6e39abeeps
56d0672689266d3d63901f3a35fb7e0a2d96d6e39abeeps
57724b8a50f34b85dda9f1990e3e8c5b68edbdb37bSimran Basidef _detect_host(connectivity_class, hostname, **args):
58724b8a50f34b85dda9f1990e3e8c5b68edbdb37bSimran Basi    """Detect host type.
59724b8a50f34b85dda9f1990e3e8c5b68edbdb37bSimran Basi
6046dadc9439355f72d394dcc4700902001bd797ffbeeps    Goes through all the possible host classes, calling check_host with a
6146dadc9439355f72d394dcc4700902001bd797ffbeeps    basic host object. Currently this is an ssh host, but theoretically it
6246dadc9439355f72d394dcc4700902001bd797ffbeeps    can be any host object that the check_host method of appropriate host
6346dadc9439355f72d394dcc4700902001bd797ffbeeps    type knows to use.
64724b8a50f34b85dda9f1990e3e8c5b68edbdb37bSimran Basi
65724b8a50f34b85dda9f1990e3e8c5b68edbdb37bSimran Basi    @param connectivity_class: connectivity class to use to talk to the host
66724b8a50f34b85dda9f1990e3e8c5b68edbdb37bSimran Basi                               (ParamikoHost or SSHHost)
67724b8a50f34b85dda9f1990e3e8c5b68edbdb37bSimran Basi    @param hostname: A string representing the host name of the device.
68724b8a50f34b85dda9f1990e3e8c5b68edbdb37bSimran Basi    @param args: Args that will be passed to the constructor of
69724b8a50f34b85dda9f1990e3e8c5b68edbdb37bSimran Basi                 the host class.
70724b8a50f34b85dda9f1990e3e8c5b68edbdb37bSimran Basi
7146dadc9439355f72d394dcc4700902001bd797ffbeeps    @returns: Class type of the first host class that returns True to the
7246dadc9439355f72d394dcc4700902001bd797ffbeeps              check_host method.
73724b8a50f34b85dda9f1990e3e8c5b68edbdb37bSimran Basi    """
7446dadc9439355f72d394dcc4700902001bd797ffbeeps    # TODO crbug.com/302026 (sbasi) - adjust this pathway for ADBHost in
7546dadc9439355f72d394dcc4700902001bd797ffbeeps    # the future should a host require verify/repair.
7646dadc9439355f72d394dcc4700902001bd797ffbeeps    with closing(connectivity_class(hostname, **args)) as host:
7746dadc9439355f72d394dcc4700902001bd797ffbeeps        for host_module in host_types:
7846dadc9439355f72d394dcc4700902001bd797ffbeeps            if host_module.check_host(host, timeout=10):
7946dadc9439355f72d394dcc4700902001bd797ffbeeps                return host_module
8046dadc9439355f72d394dcc4700902001bd797ffbeeps
8146dadc9439355f72d394dcc4700902001bd797ffbeeps    logging.warning('Unable to apply conventional host detection methods, '
8246dadc9439355f72d394dcc4700902001bd797ffbeeps                    'defaulting to chromeos host.')
8346dadc9439355f72d394dcc4700902001bd797ffbeeps    return cros_host.CrosHost
84724b8a50f34b85dda9f1990e3e8c5b68edbdb37bSimran Basi
85724b8a50f34b85dda9f1990e3e8c5b68edbdb37bSimran Basi
86e48bcfb879c943df72a866e3db8b2df8e930b2c1mblighdef create_host(
873693fc8d1136f17c47bac3e4a2faeb525301e05aDale Curtis    hostname, auto_monitor=False, follow_paths=None, pattern_paths=None,
88724b8a50f34b85dda9f1990e3e8c5b68edbdb37bSimran Basi    netconsole=False, **args):
89d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng    """Create a host object.
90d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng
91d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng    This method mixes host classes that are needed into a new subclass
92d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng    and creates a instance of the new class.
93d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng
94d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng    @param hostname: A string representing the host name of the device.
95d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng    @param auto_monitor: A boolean value, if True, will try to mix
96d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng                         SerialHost in. If the host supports use as SerialHost,
97d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng                         will not mix in LogfileMonitorMixin anymore.
98d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng                         If the host doesn't support it, will
99d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng                         fall back to direct demesg logging and mix
100d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng                         LogfileMonitorMixin in.
101d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng    @param follow_paths: A list, passed to LogfileMonitorMixin,
102d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng                         remote paths to monitor.
103d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng    @param pattern_paths: A list, passed to LogfileMonitorMixin,
104d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng                          local paths to alert pattern definition files.
105d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng    @param netconsole: A boolean value, if True, will mix NetconsoleHost in.
106d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng    @param args: Args that will be passed to the constructor of
107d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng                 the new host class.
108431010f31bceddcf92f36e1b82397b9a56ffee54Simran Basi    @param adb: If True creates an instance of adb_host not cros_host.
109d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng
110d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng    @returns: A host object which is an instance of the newly created
111d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng              host class.
112d1c2b73b7a1cbc74701ddc2b6bbbb3a162ace047Fang Deng    """
11396667caad23538696f737100803e68a1c1ecf357Fang Deng
114724b8a50f34b85dda9f1990e3e8c5b68edbdb37bSimran Basi    ssh_user, ssh_pass, ssh_port, ssh_verbosity_flag, ssh_options = \
115724b8a50f34b85dda9f1990e3e8c5b68edbdb37bSimran Basi            _get_host_arguments()
116724b8a50f34b85dda9f1990e3e8c5b68edbdb37bSimran Basi
117724b8a50f34b85dda9f1990e3e8c5b68edbdb37bSimran Basi    hostname, args['user'], args['password'], args['port'] = \
118724b8a50f34b85dda9f1990e3e8c5b68edbdb37bSimran Basi            server_utils.parse_machine(hostname, ssh_user, ssh_pass, ssh_port)
119724b8a50f34b85dda9f1990e3e8c5b68edbdb37bSimran Basi    args['ssh_verbosity_flag'] = ssh_verbosity_flag
120724b8a50f34b85dda9f1990e3e8c5b68edbdb37bSimran Basi    args['ssh_options'] = ssh_options
121431010f31bceddcf92f36e1b82397b9a56ffee54Simran Basi
122635b06f6c016fd5e4e14e98c471e92b1f435ac46jadmanski    # by default assume we're using SSH support
12355552bf4a54d847f2cc30b307b3636c2be2ad5a2mbligh    if SSH_ENGINE == 'paramiko':
12455552bf4a54d847f2cc30b307b3636c2be2ad5a2mbligh        from autotest_lib.server.hosts import paramiko_host
125724b8a50f34b85dda9f1990e3e8c5b68edbdb37bSimran Basi        connectivity_class = paramiko_host.ParamikoHost
12655552bf4a54d847f2cc30b307b3636c2be2ad5a2mbligh    elif SSH_ENGINE == 'raw_ssh':
127724b8a50f34b85dda9f1990e3e8c5b68edbdb37bSimran Basi        connectivity_class = ssh_host.SSHHost
12855552bf4a54d847f2cc30b307b3636c2be2ad5a2mbligh    else:
12955552bf4a54d847f2cc30b307b3636c2be2ad5a2mbligh        raise error.AutoServError("Unknown SSH engine %s. Please verify the "
13055552bf4a54d847f2cc30b307b3636c2be2ad5a2mbligh                                  "value of the configuration key 'ssh_engine' "
13155552bf4a54d847f2cc30b307b3636c2be2ad5a2mbligh                                  "on autotest's global_config.ini file." %
13255552bf4a54d847f2cc30b307b3636c2be2ad5a2mbligh                                  SSH_ENGINE)
133635b06f6c016fd5e4e14e98c471e92b1f435ac46jadmanski
134724b8a50f34b85dda9f1990e3e8c5b68edbdb37bSimran Basi    classes = [_detect_host(connectivity_class, hostname, **args),
135724b8a50f34b85dda9f1990e3e8c5b68edbdb37bSimran Basi               connectivity_class]
136c6136e92bb5aef1acda979fd303fc855cf704434jadmanski    # by default mix in run_test support
137c6136e92bb5aef1acda979fd303fc855cf704434jadmanski    classes.append(autotest.AutotestHostMixin)
138c6136e92bb5aef1acda979fd303fc855cf704434jadmanski
139cb4aac7441db2fa1880d06ba57b1ab354fdbc18ajadmanski    # if the user really wants to use netconsole, let them
140cb4aac7441db2fa1880d06ba57b1ab354fdbc18ajadmanski    if netconsole:
141cb4aac7441db2fa1880d06ba57b1ab354fdbc18ajadmanski        classes.append(netconsole.NetconsoleHost)
142cb4aac7441db2fa1880d06ba57b1ab354fdbc18ajadmanski
14354f90af69daac8a781678f0ccd42d6728d5d81d1jadmanski    if auto_monitor:
14454f90af69daac8a781678f0ccd42d6728d5d81d1jadmanski        # use serial console support if it's available
14554f90af69daac8a781678f0ccd42d6728d5d81d1jadmanski        conmux_args = {}
14654f90af69daac8a781678f0ccd42d6728d5d81d1jadmanski        for key in ("conmux_server", "conmux_attach"):
14754f90af69daac8a781678f0ccd42d6728d5d81d1jadmanski            if key in args:
14854f90af69daac8a781678f0ccd42d6728d5d81d1jadmanski                conmux_args[key] = args[key]
14954f90af69daac8a781678f0ccd42d6728d5d81d1jadmanski        if serial.SerialHost.host_is_supported(hostname, **conmux_args):
15054f90af69daac8a781678f0ccd42d6728d5d81d1jadmanski            classes.append(serial.SerialHost)
151635b06f6c016fd5e4e14e98c471e92b1f435ac46jadmanski        else:
152af959a4e2909cd0013cc58d22ca4527570995319jadmanski            # no serial available, fall back to direct dmesg logging
153af959a4e2909cd0013cc58d22ca4527570995319jadmanski            if follow_paths is None:
154af959a4e2909cd0013cc58d22ca4527570995319jadmanski                follow_paths = [DEFAULT_FOLLOW_PATH]
15554f90af69daac8a781678f0ccd42d6728d5d81d1jadmanski            else:
156af959a4e2909cd0013cc58d22ca4527570995319jadmanski                follow_paths = list(follow_paths) + [DEFAULT_FOLLOW_PATH]
157e48bcfb879c943df72a866e3db8b2df8e930b2c1mbligh
158af959a4e2909cd0013cc58d22ca4527570995319jadmanski            if pattern_paths is None:
159af959a4e2909cd0013cc58d22ca4527570995319jadmanski                pattern_paths = [DEFAULT_PATTERNS_PATH]
160af959a4e2909cd0013cc58d22ca4527570995319jadmanski            else:
161af959a4e2909cd0013cc58d22ca4527570995319jadmanski                pattern_paths = (
162af959a4e2909cd0013cc58d22ca4527570995319jadmanski                    list(pattern_paths) + [DEFAULT_PATTERNS_PATH])
163e48bcfb879c943df72a866e3db8b2df8e930b2c1mbligh
164af959a4e2909cd0013cc58d22ca4527570995319jadmanski            logfile_monitor_class = logfile_monitor.NewLogfileMonitorMixin(
165af959a4e2909cd0013cc58d22ca4527570995319jadmanski                follow_paths, pattern_paths)
166af959a4e2909cd0013cc58d22ca4527570995319jadmanski            classes.append(logfile_monitor_class)
167e48bcfb879c943df72a866e3db8b2df8e930b2c1mbligh
168e48bcfb879c943df72a866e3db8b2df8e930b2c1mbligh    elif follow_paths:
169e48bcfb879c943df72a866e3db8b2df8e930b2c1mbligh        logfile_monitor_class = logfile_monitor.NewLogfileMonitorMixin(
170e48bcfb879c943df72a866e3db8b2df8e930b2c1mbligh            follow_paths, pattern_paths)
171e48bcfb879c943df72a866e3db8b2df8e930b2c1mbligh        classes.append(logfile_monitor_class)
172635b06f6c016fd5e4e14e98c471e92b1f435ac46jadmanski
173635b06f6c016fd5e4e14e98c471e92b1f435ac46jadmanski    # do any site-specific processing of the classes list
174d60321a8b012aa6101bcb237341fab5bd2d25a04jadmanski    site_factory.postprocess_classes(classes, hostname,
175ea90226c42c24de00839d2c68a04909b233e9a4fjadmanski                                     auto_monitor=auto_monitor, **args)
176635b06f6c016fd5e4e14e98c471e92b1f435ac46jadmanski
177635b06f6c016fd5e4e14e98c471e92b1f435ac46jadmanski    # create a custom host class for this machine and return an instance of it
178635b06f6c016fd5e4e14e98c471e92b1f435ac46jadmanski    host_class = type("%s_host" % hostname, tuple(classes), {})
179d60321a8b012aa6101bcb237341fab5bd2d25a04jadmanski    host_instance = host_class(hostname, **args)
180d60321a8b012aa6101bcb237341fab5bd2d25a04jadmanski
181d60321a8b012aa6101bcb237341fab5bd2d25a04jadmanski    # call job_start if this is the first time this host is being used
182d60321a8b012aa6101bcb237341fab5bd2d25a04jadmanski    if hostname not in _started_hostnames:
183d60321a8b012aa6101bcb237341fab5bd2d25a04jadmanski        host_instance.job_start()
184d60321a8b012aa6101bcb237341fab5bd2d25a04jadmanski        _started_hostnames.add(hostname)
185d60321a8b012aa6101bcb237341fab5bd2d25a04jadmanski
186d60321a8b012aa6101bcb237341fab5bd2d25a04jadmanski    return host_instance
187