blob: 95b676f6ac7b6cbbad69b63486c2e4b3505c769c [file] [log] [blame]
mblighbe630eb2008-08-01 16:41:48 +00001#
2# Copyright 2008 Google Inc. All Rights Reserved.
3
4"""
5The host module contains the objects and method used to
6manage a host in Autotest.
7
8The valid actions are:
9create: adds host(s)
10delete: deletes host(s)
11list: lists host(s)
12stat: displays host(s) information
13mod: modifies host(s)
14jobs: lists all jobs that ran on host(s)
15
16The common options are:
17-M|--mlist: file containing a list of machines
18
19
mblighbe630eb2008-08-01 16:41:48 +000020See topic_common.py for a High Level Design and Algorithm.
21
22"""
23
Jiaxi Luoc342f9f2014-05-19 16:22:03 -070024from autotest_lib.cli import action_common, topic_common
mblighaed47e82009-03-17 19:06:18 +000025from autotest_lib.client.common_lib import host_protections
mblighbe630eb2008-08-01 16:41:48 +000026
27
28class host(topic_common.atest):
29 """Host class
30 atest host [create|delete|list|stat|mod|jobs] <options>"""
31 usage_action = '[create|delete|list|stat|mod|jobs]'
32 topic = msg_topic = 'host'
33 msg_items = '<hosts>'
34
mblighaed47e82009-03-17 19:06:18 +000035 protections = host_protections.Protection.names
36
mblighbe630eb2008-08-01 16:41:48 +000037
38 def __init__(self):
39 """Add to the parser the options common to all the
40 host actions"""
41 super(host, self).__init__()
42
43 self.parser.add_option('-M', '--mlist',
44 help='File listing the machines',
45 type='string',
46 default=None,
47 metavar='MACHINE_FLIST')
48
mbligh9deeefa2009-05-01 23:11:08 +000049 self.topic_parse_info = topic_common.item_parse_info(
50 attribute_name='hosts',
51 filename_option='mlist',
52 use_leftover=True)
mblighbe630eb2008-08-01 16:41:48 +000053
54
55 def _parse_lock_options(self, options):
56 if options.lock and options.unlock:
57 self.invalid_syntax('Only specify one of '
58 '--lock and --unlock.')
59
60 if options.lock:
61 self.data['locked'] = True
62 self.messages.append('Locked host')
63 elif options.unlock:
64 self.data['locked'] = False
65 self.messages.append('Unlocked host')
66
67
68 def _cleanup_labels(self, labels, platform=None):
69 """Removes the platform label from the overall labels"""
70 if platform:
71 return [label for label in labels
72 if label != platform]
73 else:
74 try:
75 return [label for label in labels
76 if not label['platform']]
77 except TypeError:
78 # This is a hack - the server will soon
79 # do this, so all this code should be removed.
80 return labels
81
82
83 def get_items(self):
84 return self.hosts
85
86
87class host_help(host):
88 """Just here to get the atest logic working.
89 Usage is set by its parent"""
90 pass
91
92
93class host_list(action_common.atest_list, host):
94 """atest host list [--mlist <file>|<hosts>] [--label <label>]
mbligh536a5242008-10-18 14:35:54 +000095 [--status <status1,status2>] [--acl <ACL>] [--user <user>]"""
mblighbe630eb2008-08-01 16:41:48 +000096
97 def __init__(self):
98 super(host_list, self).__init__()
99
100 self.parser.add_option('-b', '--label',
mbligh6444c6b2008-10-27 20:55:13 +0000101 default='',
102 help='Only list hosts with all these labels '
103 '(comma separated)')
mblighbe630eb2008-08-01 16:41:48 +0000104 self.parser.add_option('-s', '--status',
mbligh6444c6b2008-10-27 20:55:13 +0000105 default='',
106 help='Only list hosts with any of these '
107 'statuses (comma separated)')
mbligh536a5242008-10-18 14:35:54 +0000108 self.parser.add_option('-a', '--acl',
mbligh6444c6b2008-10-27 20:55:13 +0000109 default='',
mbligh536a5242008-10-18 14:35:54 +0000110 help='Only list hosts within this ACL')
111 self.parser.add_option('-u', '--user',
mbligh6444c6b2008-10-27 20:55:13 +0000112 default='',
mbligh536a5242008-10-18 14:35:54 +0000113 help='Only list hosts available to this user')
mbligh70b9bf42008-11-18 15:00:46 +0000114 self.parser.add_option('-N', '--hostnames-only', help='Only return '
115 'hostnames for the machines queried.',
116 action='store_true')
mbligh91e0efd2009-02-26 01:02:16 +0000117 self.parser.add_option('--locked',
118 default=False,
119 help='Only list locked hosts',
120 action='store_true')
121 self.parser.add_option('--unlocked',
122 default=False,
123 help='Only list unlocked hosts',
124 action='store_true')
125
mblighbe630eb2008-08-01 16:41:48 +0000126
127
128 def parse(self):
129 """Consume the specific options"""
jamesrenc2863162010-07-12 21:20:51 +0000130 label_info = topic_common.item_parse_info(attribute_name='labels',
131 inline_option='label')
132
133 (options, leftover) = super(host_list, self).parse([label_info])
134
mblighbe630eb2008-08-01 16:41:48 +0000135 self.status = options.status
mbligh536a5242008-10-18 14:35:54 +0000136 self.acl = options.acl
137 self.user = options.user
mbligh70b9bf42008-11-18 15:00:46 +0000138 self.hostnames_only = options.hostnames_only
mbligh91e0efd2009-02-26 01:02:16 +0000139
140 if options.locked and options.unlocked:
141 self.invalid_syntax('--locked and --unlocked are '
142 'mutually exclusive')
143 self.locked = options.locked
144 self.unlocked = options.unlocked
mblighbe630eb2008-08-01 16:41:48 +0000145 return (options, leftover)
146
147
148 def execute(self):
149 filters = {}
150 check_results = {}
151 if self.hosts:
152 filters['hostname__in'] = self.hosts
153 check_results['hostname__in'] = 'hostname'
mbligh6444c6b2008-10-27 20:55:13 +0000154
155 if self.labels:
jamesrenc2863162010-07-12 21:20:51 +0000156 if len(self.labels) == 1:
mblighf703fb42009-01-30 00:35:05 +0000157 # This is needed for labels with wildcards (x86*)
jamesrenc2863162010-07-12 21:20:51 +0000158 filters['labels__name__in'] = self.labels
mblighf703fb42009-01-30 00:35:05 +0000159 check_results['labels__name__in'] = None
160 else:
jamesrenc2863162010-07-12 21:20:51 +0000161 filters['multiple_labels'] = self.labels
mblighf703fb42009-01-30 00:35:05 +0000162 check_results['multiple_labels'] = None
mbligh6444c6b2008-10-27 20:55:13 +0000163
mblighbe630eb2008-08-01 16:41:48 +0000164 if self.status:
mbligh6444c6b2008-10-27 20:55:13 +0000165 statuses = self.status.split(',')
166 statuses = [status.strip() for status in statuses
167 if status.strip()]
168
169 filters['status__in'] = statuses
mblighcd8eb972008-08-25 19:20:39 +0000170 check_results['status__in'] = None
mbligh6444c6b2008-10-27 20:55:13 +0000171
mbligh536a5242008-10-18 14:35:54 +0000172 if self.acl:
showardd9ac4452009-02-07 02:04:37 +0000173 filters['aclgroup__name'] = self.acl
174 check_results['aclgroup__name'] = None
mbligh536a5242008-10-18 14:35:54 +0000175 if self.user:
showardd9ac4452009-02-07 02:04:37 +0000176 filters['aclgroup__users__login'] = self.user
177 check_results['aclgroup__users__login'] = None
mbligh91e0efd2009-02-26 01:02:16 +0000178
179 if self.locked or self.unlocked:
180 filters['locked'] = self.locked
181 check_results['locked'] = None
182
mblighbe630eb2008-08-01 16:41:48 +0000183 return super(host_list, self).execute(op='get_hosts',
184 filters=filters,
185 check_results=check_results)
186
187
188 def output(self, results):
189 if results:
190 # Remove the platform from the labels.
191 for result in results:
192 result['labels'] = self._cleanup_labels(result['labels'],
193 result['platform'])
mbligh70b9bf42008-11-18 15:00:46 +0000194 if self.hostnames_only:
mblighdf75f8b2008-11-18 19:07:42 +0000195 self.print_list(results, key='hostname')
mbligh70b9bf42008-11-18 15:00:46 +0000196 else:
197 super(host_list, self).output(results, keys=['hostname', 'status',
198 'locked', 'platform', 'labels'])
mblighbe630eb2008-08-01 16:41:48 +0000199
200
201class host_stat(host):
202 """atest host stat --mlist <file>|<hosts>"""
203 usage_action = 'stat'
204
205 def execute(self):
206 results = []
207 # Convert wildcards into real host stats.
208 existing_hosts = []
209 for host in self.hosts:
210 if host.endswith('*'):
211 stats = self.execute_rpc('get_hosts',
212 hostname__startswith=host.rstrip('*'))
213 if len(stats) == 0:
214 self.failure('No hosts matching %s' % host, item=host,
215 what_failed='Failed to stat')
216 continue
217 else:
218 stats = self.execute_rpc('get_hosts', hostname=host)
219 if len(stats) == 0:
220 self.failure('Unknown host %s' % host, item=host,
221 what_failed='Failed to stat')
222 continue
223 existing_hosts.extend(stats)
224
225 for stat in existing_hosts:
226 host = stat['hostname']
227 # The host exists, these should succeed
228 acls = self.execute_rpc('get_acl_groups', hosts__hostname=host)
229
230 labels = self.execute_rpc('get_labels', host__hostname=host)
231 results.append ([[stat], acls, labels])
232 return results
233
234
235 def output(self, results):
236 for stats, acls, labels in results:
237 print '-'*5
238 self.print_fields(stats,
239 keys=['hostname', 'platform',
mblighe163b032008-10-18 14:30:27 +0000240 'status', 'locked', 'locked_by',
241 'lock_time', 'protection',])
mblighbe630eb2008-08-01 16:41:48 +0000242 self.print_by_ids(acls, 'ACLs', line_before=True)
243 labels = self._cleanup_labels(labels)
244 self.print_by_ids(labels, 'Labels', line_before=True)
245
246
247class host_jobs(host):
mbligh1494eca2008-08-13 21:24:22 +0000248 """atest host jobs [--max-query] --mlist <file>|<hosts>"""
mblighbe630eb2008-08-01 16:41:48 +0000249 usage_action = 'jobs'
250
mbligh6996fe82008-08-13 00:32:27 +0000251 def __init__(self):
252 super(host_jobs, self).__init__()
253 self.parser.add_option('-q', '--max-query',
254 help='Limits the number of results '
255 '(20 by default)',
256 type='int', default=20)
257
258
259 def parse(self):
260 """Consume the specific options"""
mbligh9deeefa2009-05-01 23:11:08 +0000261 (options, leftover) = super(host_jobs, self).parse()
mbligh6996fe82008-08-13 00:32:27 +0000262 self.max_queries = options.max_query
263 return (options, leftover)
264
265
mblighbe630eb2008-08-01 16:41:48 +0000266 def execute(self):
267 results = []
268 real_hosts = []
269 for host in self.hosts:
270 if host.endswith('*'):
271 stats = self.execute_rpc('get_hosts',
272 hostname__startswith=host.rstrip('*'))
273 if len(stats) == 0:
274 self.failure('No host matching %s' % host, item=host,
275 what_failed='Failed to stat')
276 [real_hosts.append(stat['hostname']) for stat in stats]
277 else:
278 real_hosts.append(host)
279
280 for host in real_hosts:
281 queue_entries = self.execute_rpc('get_host_queue_entries',
mbligh6996fe82008-08-13 00:32:27 +0000282 host__hostname=host,
mbligh1494eca2008-08-13 21:24:22 +0000283 query_limit=self.max_queries,
showard6958c722009-09-23 20:03:16 +0000284 sort_by=['-job__id'])
mblighbe630eb2008-08-01 16:41:48 +0000285 jobs = []
286 for entry in queue_entries:
287 job = {'job_id': entry['job']['id'],
288 'job_owner': entry['job']['owner'],
289 'job_name': entry['job']['name'],
290 'status': entry['status']}
291 jobs.append(job)
292 results.append((host, jobs))
293 return results
294
295
296 def output(self, results):
297 for host, jobs in results:
298 print '-'*5
299 print 'Hostname: %s' % host
300 self.print_table(jobs, keys_header=['job_id',
showardbe0d8692009-08-20 23:42:44 +0000301 'job_owner',
302 'job_name',
303 'status'])
mblighbe630eb2008-08-01 16:41:48 +0000304
305
306class host_mod(host):
showardbe0d8692009-08-20 23:42:44 +0000307 """atest host mod --lock|--unlock|--protection
mblighbe630eb2008-08-01 16:41:48 +0000308 --mlist <file>|<hosts>"""
309 usage_action = 'mod'
310
311 def __init__(self):
312 """Add the options specific to the mod action"""
313 self.data = {}
314 self.messages = []
315 super(host_mod, self).__init__()
mblighbe630eb2008-08-01 16:41:48 +0000316 self.parser.add_option('-l', '--lock',
317 help='Lock hosts',
318 action='store_true')
319 self.parser.add_option('-u', '--unlock',
320 help='Unlock hosts',
321 action='store_true')
mblighe163b032008-10-18 14:30:27 +0000322 self.parser.add_option('-p', '--protection', type='choice',
mblighaed47e82009-03-17 19:06:18 +0000323 help=('Set the protection level on a host. '
324 'Must be one of: %s' %
325 ', '.join('"%s"' % p
326 for p in self.protections)),
327 choices=self.protections)
mblighbe630eb2008-08-01 16:41:48 +0000328
329
330 def parse(self):
331 """Consume the specific options"""
332 (options, leftover) = super(host_mod, self).parse()
333
334 self._parse_lock_options(options)
335
mblighe163b032008-10-18 14:30:27 +0000336 if options.protection:
337 self.data['protection'] = options.protection
showardbe0d8692009-08-20 23:42:44 +0000338 self.messages.append('Protection set to "%s"' % options.protection)
mblighe163b032008-10-18 14:30:27 +0000339
mblighbe630eb2008-08-01 16:41:48 +0000340 if len(self.data) == 0:
341 self.invalid_syntax('No modification requested')
342 return (options, leftover)
343
344
345 def execute(self):
346 successes = []
347 for host in self.hosts:
mbligh6eca4602009-01-07 16:48:50 +0000348 try:
349 res = self.execute_rpc('modify_host', item=host,
350 id=host, **self.data)
351 # TODO: Make the AFE return True or False,
352 # especially for lock
mblighbe630eb2008-08-01 16:41:48 +0000353 successes.append(host)
mbligh6eca4602009-01-07 16:48:50 +0000354 except topic_common.CliError, full_error:
355 # Already logged by execute_rpc()
356 pass
357
mblighbe630eb2008-08-01 16:41:48 +0000358 return successes
359
360
361 def output(self, hosts):
362 for msg in self.messages:
363 self.print_wrapped(msg, hosts)
364
365
mblighbe630eb2008-08-01 16:41:48 +0000366class host_create(host):
367 """atest host create [--lock|--unlock --platform <arch>
368 --labels <labels>|--blist <label_file>
369 --acls <acls>|--alist <acl_file>
mblighaed47e82009-03-17 19:06:18 +0000370 --protection <protection_type>
mblighbe630eb2008-08-01 16:41:48 +0000371 --mlist <mach_file>] <hosts>"""
372 usage_action = 'create'
373
374 def __init__(self):
375 self.messages = []
376 super(host_create, self).__init__()
377 self.parser.add_option('-l', '--lock',
378 help='Create the hosts as locked',
mbligh719e14a2008-12-04 01:17:08 +0000379 action='store_true', default=False)
mblighbe630eb2008-08-01 16:41:48 +0000380 self.parser.add_option('-u', '--unlock',
381 help='Create the hosts as '
382 'unlocked (default)',
383 action='store_true')
384 self.parser.add_option('-t', '--platform',
385 help='Sets the platform label')
386 self.parser.add_option('-b', '--labels',
387 help='Comma separated list of labels')
388 self.parser.add_option('-B', '--blist',
389 help='File listing the labels',
390 type='string',
391 metavar='LABEL_FLIST')
392 self.parser.add_option('-a', '--acls',
393 help='Comma separated list of ACLs')
394 self.parser.add_option('-A', '--alist',
395 help='File listing the acls',
396 type='string',
397 metavar='ACL_FLIST')
mblighaed47e82009-03-17 19:06:18 +0000398 self.parser.add_option('-p', '--protection', type='choice',
399 help=('Set the protection level on a host. '
400 'Must be one of: %s' %
401 ', '.join('"%s"' % p
402 for p in self.protections)),
403 choices=self.protections)
mblighbe630eb2008-08-01 16:41:48 +0000404
405
406 def parse(self):
mbligh9deeefa2009-05-01 23:11:08 +0000407 label_info = topic_common.item_parse_info(attribute_name='labels',
408 inline_option='labels',
409 filename_option='blist')
410 acl_info = topic_common.item_parse_info(attribute_name='acls',
411 inline_option='acls',
412 filename_option='alist')
413
414 (options, leftover) = super(host_create, self).parse([label_info,
415 acl_info],
416 req_items='hosts')
mblighbe630eb2008-08-01 16:41:48 +0000417
418 self._parse_lock_options(options)
mbligh719e14a2008-12-04 01:17:08 +0000419 self.locked = options.lock
mblighbe630eb2008-08-01 16:41:48 +0000420 self.platform = getattr(options, 'platform', None)
mblighaed47e82009-03-17 19:06:18 +0000421 if options.protection:
422 self.data['protection'] = options.protection
mblighbe630eb2008-08-01 16:41:48 +0000423 return (options, leftover)
424
425
426 def _execute_add_one_host(self, host):
mbligh719e14a2008-12-04 01:17:08 +0000427 # Always add the hosts as locked to avoid the host
mblighef87cad2009-03-11 18:18:10 +0000428 # being picked up by the scheduler before it's ACL'ed
mbligh719e14a2008-12-04 01:17:08 +0000429 self.data['locked'] = True
mblighbe630eb2008-08-01 16:41:48 +0000430 self.execute_rpc('add_host', hostname=host,
431 status="Ready", **self.data)
432
433 # Now add the platform label
434 labels = self.labels[:]
435 if self.platform:
436 labels.append(self.platform)
437 if len (labels):
438 self.execute_rpc('host_add_labels', id=host, labels=labels)
439
440
Jiaxi Luoc342f9f2014-05-19 16:22:03 -0700441 def _execute_add_hosts(self):
442 successful_hosts = self.site_create_hosts_hook()
443
444 if successful_hosts:
445 for acl in self.acls:
446 self.execute_rpc('acl_group_add_hosts',
447 id=acl,
448 hosts=successful_hosts)
449
450 if not self.locked:
451 for host in successful_hosts:
452 self.execute_rpc('modify_host', id=host, locked=False)
453 return successful_hosts
454
455
mblighbe630eb2008-08-01 16:41:48 +0000456 def execute(self):
457 # We need to check if these labels & ACLs exist,
458 # and create them if not.
459 if self.platform:
460 self.check_and_create_items('get_labels', 'add_label',
461 [self.platform],
462 platform=True)
463
464 if self.labels:
465 self.check_and_create_items('get_labels', 'add_label',
466 self.labels,
467 platform=False)
468
469 if self.acls:
470 self.check_and_create_items('get_acl_groups',
471 'add_acl_group',
472 self.acls)
473
Jiaxi Luoc342f9f2014-05-19 16:22:03 -0700474 return self._execute_add_hosts()
mblighbe630eb2008-08-01 16:41:48 +0000475
476
477 def site_create_hosts_hook(self):
Jiaxi Luoc342f9f2014-05-19 16:22:03 -0700478 successful_hosts = []
mblighbe630eb2008-08-01 16:41:48 +0000479 for host in self.hosts:
480 try:
481 self._execute_add_one_host(host)
Jiaxi Luoc342f9f2014-05-19 16:22:03 -0700482 successful_hosts.append(host)
mblighbe630eb2008-08-01 16:41:48 +0000483 except topic_common.CliError:
484 pass
485
Jiaxi Luoc342f9f2014-05-19 16:22:03 -0700486 return successful_hosts
mblighbe630eb2008-08-01 16:41:48 +0000487
488
489 def output(self, hosts):
490 self.print_wrapped('Added host', hosts)
491
492
493class host_delete(action_common.atest_delete, host):
494 """atest host delete [--mlist <mach_file>] <hosts>"""
495 pass