factory.py revision 46dadc9439355f72d394dcc4700902001bd797ff
1"""Provides a factory method to create a host object.""" 2 3import logging 4from contextlib import closing 5 6from autotest_lib.client.common_lib import error, global_config 7from autotest_lib.server import autotest, utils as server_utils 8from autotest_lib.server.hosts import site_factory, cros_host, ssh_host, serial 9from autotest_lib.server.hosts import eureka_host 10from autotest_lib.server.hosts import adb_host, logfile_monitor 11 12 13 14DEFAULT_FOLLOW_PATH = '/var/log/kern.log' 15DEFAULT_PATTERNS_PATH = 'console_patterns' 16SSH_ENGINE = global_config.global_config.get_config_value('AUTOSERV', 17 'ssh_engine', 18 type=str) 19 20# Default ssh options used in creating a host. 21DEFAULT_SSH_USER = 'root' 22DEFAULT_SSH_PASS = '' 23DEFAULT_SSH_PORT = 22 24DEFAULT_SSH_VERBOSITY = '' 25DEFAULT_SSH_OPTIONS = '' 26 27# for tracking which hostnames have already had job_start called 28_started_hostnames = set() 29 30# A list of all the possible host types, ordered according to frequency of 31# host types in the lab, so the more common hosts don't incur a repeated ssh 32# overhead in checking for less common host types. 33host_types = [cros_host.CrosHost, eureka_host.EurekaHost, adb_host.ADBHost,] 34 35 36def _get_host_arguments(): 37 """Returns parameters needed to ssh into a host. 38 39 There are currently 2 use cases for creating a host. 40 1. Through the server_job, in which case the server_job injects 41 the appropriate ssh parameters into our name space and they 42 are available as the variables ssh_user, ssh_pass etc. 43 2. Directly through factory.create_host, in which case we use 44 the same defaults as used in the server job to create a host. 45 46 @returns: A tuple of parameters needed to create an ssh connection, ordered 47 as: ssh_user, ssh_pass, ssh_port, ssh_verbosity, ssh_options. 48 """ 49 g = globals() 50 return (g.get('ssh_user', DEFAULT_SSH_USER), 51 g.get('ssh_pass', DEFAULT_SSH_PASS), 52 g.get('ssh_port', DEFAULT_SSH_PORT), 53 g.get('ssh_verbosity_flag', DEFAULT_SSH_VERBOSITY), 54 g.get('ssh_options', DEFAULT_SSH_OPTIONS)) 55 56 57def _detect_host(connectivity_class, hostname, **args): 58 """Detect host type. 59 60 Goes through all the possible host classes, calling check_host with a 61 basic host object. Currently this is an ssh host, but theoretically it 62 can be any host object that the check_host method of appropriate host 63 type knows to use. 64 65 @param connectivity_class: connectivity class to use to talk to the host 66 (ParamikoHost or SSHHost) 67 @param hostname: A string representing the host name of the device. 68 @param args: Args that will be passed to the constructor of 69 the host class. 70 71 @returns: Class type of the first host class that returns True to the 72 check_host method. 73 """ 74 # TODO crbug.com/302026 (sbasi) - adjust this pathway for ADBHost in 75 # the future should a host require verify/repair. 76 with closing(connectivity_class(hostname, **args)) as host: 77 for host_module in host_types: 78 if host_module.check_host(host, timeout=10): 79 return host_module 80 81 logging.warning('Unable to apply conventional host detection methods, ' 82 'defaulting to chromeos host.') 83 return cros_host.CrosHost 84 85 86def create_host( 87 hostname, auto_monitor=False, follow_paths=None, pattern_paths=None, 88 netconsole=False, **args): 89 """Create a host object. 90 91 This method mixes host classes that are needed into a new subclass 92 and creates a instance of the new class. 93 94 @param hostname: A string representing the host name of the device. 95 @param auto_monitor: A boolean value, if True, will try to mix 96 SerialHost in. If the host supports use as SerialHost, 97 will not mix in LogfileMonitorMixin anymore. 98 If the host doesn't support it, will 99 fall back to direct demesg logging and mix 100 LogfileMonitorMixin in. 101 @param follow_paths: A list, passed to LogfileMonitorMixin, 102 remote paths to monitor. 103 @param pattern_paths: A list, passed to LogfileMonitorMixin, 104 local paths to alert pattern definition files. 105 @param netconsole: A boolean value, if True, will mix NetconsoleHost in. 106 @param args: Args that will be passed to the constructor of 107 the new host class. 108 @param adb: If True creates an instance of adb_host not cros_host. 109 110 @returns: A host object which is an instance of the newly created 111 host class. 112 """ 113 114 ssh_user, ssh_pass, ssh_port, ssh_verbosity_flag, ssh_options = \ 115 _get_host_arguments() 116 117 hostname, args['user'], args['password'], args['port'] = \ 118 server_utils.parse_machine(hostname, ssh_user, ssh_pass, ssh_port) 119 args['ssh_verbosity_flag'] = ssh_verbosity_flag 120 args['ssh_options'] = ssh_options 121 122 # by default assume we're using SSH support 123 if SSH_ENGINE == 'paramiko': 124 from autotest_lib.server.hosts import paramiko_host 125 connectivity_class = paramiko_host.ParamikoHost 126 elif SSH_ENGINE == 'raw_ssh': 127 connectivity_class = ssh_host.SSHHost 128 else: 129 raise error.AutoServError("Unknown SSH engine %s. Please verify the " 130 "value of the configuration key 'ssh_engine' " 131 "on autotest's global_config.ini file." % 132 SSH_ENGINE) 133 134 classes = [_detect_host(connectivity_class, hostname, **args), 135 connectivity_class] 136 # by default mix in run_test support 137 classes.append(autotest.AutotestHostMixin) 138 139 # if the user really wants to use netconsole, let them 140 if netconsole: 141 classes.append(netconsole.NetconsoleHost) 142 143 if auto_monitor: 144 # use serial console support if it's available 145 conmux_args = {} 146 for key in ("conmux_server", "conmux_attach"): 147 if key in args: 148 conmux_args[key] = args[key] 149 if serial.SerialHost.host_is_supported(hostname, **conmux_args): 150 classes.append(serial.SerialHost) 151 else: 152 # no serial available, fall back to direct dmesg logging 153 if follow_paths is None: 154 follow_paths = [DEFAULT_FOLLOW_PATH] 155 else: 156 follow_paths = list(follow_paths) + [DEFAULT_FOLLOW_PATH] 157 158 if pattern_paths is None: 159 pattern_paths = [DEFAULT_PATTERNS_PATH] 160 else: 161 pattern_paths = ( 162 list(pattern_paths) + [DEFAULT_PATTERNS_PATH]) 163 164 logfile_monitor_class = logfile_monitor.NewLogfileMonitorMixin( 165 follow_paths, pattern_paths) 166 classes.append(logfile_monitor_class) 167 168 elif follow_paths: 169 logfile_monitor_class = logfile_monitor.NewLogfileMonitorMixin( 170 follow_paths, pattern_paths) 171 classes.append(logfile_monitor_class) 172 173 # do any site-specific processing of the classes list 174 site_factory.postprocess_classes(classes, hostname, 175 auto_monitor=auto_monitor, **args) 176 177 # create a custom host class for this machine and return an instance of it 178 host_class = type("%s_host" % hostname, tuple(classes), {}) 179 host_instance = host_class(hostname, **args) 180 181 # call job_start if this is the first time this host is being used 182 if hostname not in _started_hostnames: 183 host_instance.job_start() 184 _started_hostnames.add(hostname) 185 186 return host_instance 187