blob: 7493419729569b20fd3b1b3f3e789d78c1654d08 [file] [log] [blame]
Jakob Juelich8b110ee2014-09-15 16:13:42 -07001#
2# Copyright 2008 Google Inc. All Rights Reserved.
3
4"""
5The shard module contains the objects and methods used to
6manage shards in Autotest.
7
8The valid actions are:
9create: creates shard
10remove: deletes shard(s)
11list: lists shards with label
Shuqian Zhaob8b13d82016-08-04 09:35:17 -070012add_boards: add boards to a given shard
Jakob Juelich8b110ee2014-09-15 16:13:42 -070013
14See topic_common.py for a High Level Design and Algorithm.
15"""
16
Dan Shi25e1fd42014-12-19 14:36:42 -080017import sys
Jakob Juelich8b110ee2014-09-15 16:13:42 -070018from autotest_lib.cli import topic_common, action_common
19
20
21class shard(topic_common.atest):
22 """shard class
Shuqian Zhaob8b13d82016-08-04 09:35:17 -070023 atest shard [create|delete|list|add_boards] <options>"""
24 usage_action = '[create|delete|list|add_boards]'
Jakob Juelich8b110ee2014-09-15 16:13:42 -070025 topic = msg_topic = 'shard'
26 msg_items = '<shards>'
27
28 def __init__(self):
29 """Add to the parser the options common to all the
30 shard actions"""
31 super(shard, self).__init__()
32
33 self.topic_parse_info = topic_common.item_parse_info(
34 attribute_name='shards',
35 use_leftover=True)
36
37
38 def get_items(self):
39 return self.shards
40
41
42class shard_help(shard):
43 """Just here to get the atest logic working.
44 Usage is set by its parent"""
45 pass
46
47
48class shard_list(action_common.atest_list, shard):
MK Ryu5dfcc892015-07-16 15:34:04 -070049 """Class for running atest shard list"""
Jakob Juelich8b110ee2014-09-15 16:13:42 -070050
51 def execute(self):
MK Ryu5dfcc892015-07-16 15:34:04 -070052 filters = {}
53 if self.shards:
54 filters['hostname__in'] = self.shards
55 return super(shard_list, self).execute(op='get_shards',
56 filters=filters)
Jakob Juelich8b110ee2014-09-15 16:13:42 -070057
58
59 def warn_if_label_assigned_to_multiple_shards(self, results):
60 """Prints a warning if one label is assigned to multiple shards.
61
62 This should never happen, but if it does, better be safe.
63
64 @param results: Results as passed to output().
65 """
66 assigned_labels = set()
67 for line in results:
68 for label in line['labels']:
69 if label in assigned_labels:
70 sys.stderr.write('WARNING: label %s is assigned to '
71 'multiple shards.\n'
72 'This will lead to unpredictable behavor '
73 'in which hosts and jobs will be assigned '
74 'to which shard.\n')
75 assigned_labels.add(label)
76
77
78 def output(self, results):
79 self.warn_if_label_assigned_to_multiple_shards(results)
80 super(shard_list, self).output(results, ['hostname', 'labels'])
81
82
83class shard_create(action_common.atest_create, shard):
84 """Class for running atest shard create -l <label> <shard>"""
85 def __init__(self):
86 super(shard_create, self).__init__()
MK Ryu5dfcc892015-07-16 15:34:04 -070087 self.parser.add_option('-l', '--labels',
88 help=('Assign LABELs to the SHARD. All jobs that '
89 'require one of the labels will be run on '
90 'the shard. List multiple labels separated '
91 'by a comma.'),
Jakob Juelich8b110ee2014-09-15 16:13:42 -070092 type='string',
MK Ryu5dfcc892015-07-16 15:34:04 -070093 metavar='LABELS')
Jakob Juelich8b110ee2014-09-15 16:13:42 -070094
95
96 def parse(self):
MK Ryu5dfcc892015-07-16 15:34:04 -070097 (options, leftover) = super(shard_create, self).parse(
98 req_items='shards')
99 if not options.labels:
100 print ('Must provide one or more labels separated by a comma '
101 'with -l <labels>')
Jakob Juelich8b110ee2014-09-15 16:13:42 -0700102 self.parser.print_help()
103 sys.exit(1)
104 self.data_item_key = 'hostname'
MK Ryu5dfcc892015-07-16 15:34:04 -0700105 self.data['labels'] = options.labels
Jakob Juelich8b110ee2014-09-15 16:13:42 -0700106 return (options, leftover)
107
108
Shuqian Zhaob8b13d82016-08-04 09:35:17 -0700109class shard_add_boards(shard_create):
110 """Class for running atest shard add_boards -l <label> <shard>"""
111 usage_action = 'add_boards'
112 op_action = 'add_boards'
113 msg_done = 'Added boards for'
114
115 def execute(self):
116 """Running the rpc to add boards to the target shard.
117
118 Returns:
119 A tuple, 1st element is the target shard. 2nd element is the list of
120 boards labels to be added to the shard.
121 """
122 target_shard = self.shards[0]
123 self.data[self.data_item_key] = target_shard
124 super(shard_add_boards, self).execute_rpc(op='add_board_to_shard',
125 item=target_shard,
126 **self.data)
127 return (target_shard, self.data['labels'])
128
129
Jakob Juelich8b110ee2014-09-15 16:13:42 -0700130class shard_delete(action_common.atest_delete, shard):
131 """Class for running atest shard delete <shards>"""
Jakob Juelich8b110ee2014-09-15 16:13:42 -0700132
133 def parse(self):
134 (options, leftover) = super(shard_delete, self).parse()
Jakob Juelich8b110ee2014-09-15 16:13:42 -0700135 self.data_item_key = 'hostname'
136 return (options, leftover)
137
138
139 def execute(self, *args, **kwargs):
Jakob Juelich8b110ee2014-09-15 16:13:42 -0700140 print 'Please ensure the shard host is powered off.'
141 print ('Otherwise DUTs might be used by multiple shards at the same '
142 'time, which will lead to serious correctness problems.')
Dan Shi25e1fd42014-12-19 14:36:42 -0800143 return super(shard_delete, self).execute(*args, **kwargs)