Jakob Juelich | 8b110ee | 2014-09-15 16:13:42 -0700 | [diff] [blame] | 1 | # |
| 2 | # Copyright 2008 Google Inc. All Rights Reserved. |
| 3 | |
| 4 | """ |
| 5 | The shard module contains the objects and methods used to |
| 6 | manage shards in Autotest. |
| 7 | |
| 8 | The valid actions are: |
| 9 | create: creates shard |
| 10 | remove: deletes shard(s) |
| 11 | list: lists shards with label |
Shuqian Zhao | b8b13d8 | 2016-08-04 09:35:17 -0700 | [diff] [blame] | 12 | add_boards: add boards to a given shard |
Jakob Juelich | 8b110ee | 2014-09-15 16:13:42 -0700 | [diff] [blame] | 13 | |
| 14 | See topic_common.py for a High Level Design and Algorithm. |
| 15 | """ |
| 16 | |
Dan Shi | 25e1fd4 | 2014-12-19 14:36:42 -0800 | [diff] [blame] | 17 | import sys |
Jakob Juelich | 8b110ee | 2014-09-15 16:13:42 -0700 | [diff] [blame] | 18 | from autotest_lib.cli import topic_common, action_common |
| 19 | |
| 20 | |
| 21 | class shard(topic_common.atest): |
| 22 | """shard class |
Shuqian Zhao | b8b13d8 | 2016-08-04 09:35:17 -0700 | [diff] [blame] | 23 | atest shard [create|delete|list|add_boards] <options>""" |
| 24 | usage_action = '[create|delete|list|add_boards]' |
Jakob Juelich | 8b110ee | 2014-09-15 16:13:42 -0700 | [diff] [blame] | 25 | 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 | |
| 42 | class shard_help(shard): |
| 43 | """Just here to get the atest logic working. |
| 44 | Usage is set by its parent""" |
| 45 | pass |
| 46 | |
| 47 | |
| 48 | class shard_list(action_common.atest_list, shard): |
MK Ryu | 5dfcc89 | 2015-07-16 15:34:04 -0700 | [diff] [blame] | 49 | """Class for running atest shard list""" |
Jakob Juelich | 8b110ee | 2014-09-15 16:13:42 -0700 | [diff] [blame] | 50 | |
| 51 | def execute(self): |
MK Ryu | 5dfcc89 | 2015-07-16 15:34:04 -0700 | [diff] [blame] | 52 | 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 Juelich | 8b110ee | 2014-09-15 16:13:42 -0700 | [diff] [blame] | 57 | |
| 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 | |
| 83 | class 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 Ryu | 5dfcc89 | 2015-07-16 15:34:04 -0700 | [diff] [blame] | 87 | 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 Juelich | 8b110ee | 2014-09-15 16:13:42 -0700 | [diff] [blame] | 92 | type='string', |
MK Ryu | 5dfcc89 | 2015-07-16 15:34:04 -0700 | [diff] [blame] | 93 | metavar='LABELS') |
Jakob Juelich | 8b110ee | 2014-09-15 16:13:42 -0700 | [diff] [blame] | 94 | |
| 95 | |
| 96 | def parse(self): |
MK Ryu | 5dfcc89 | 2015-07-16 15:34:04 -0700 | [diff] [blame] | 97 | (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 Juelich | 8b110ee | 2014-09-15 16:13:42 -0700 | [diff] [blame] | 102 | self.parser.print_help() |
| 103 | sys.exit(1) |
| 104 | self.data_item_key = 'hostname' |
MK Ryu | 5dfcc89 | 2015-07-16 15:34:04 -0700 | [diff] [blame] | 105 | self.data['labels'] = options.labels |
Jakob Juelich | 8b110ee | 2014-09-15 16:13:42 -0700 | [diff] [blame] | 106 | return (options, leftover) |
| 107 | |
| 108 | |
Shuqian Zhao | b8b13d8 | 2016-08-04 09:35:17 -0700 | [diff] [blame] | 109 | class 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 Juelich | 8b110ee | 2014-09-15 16:13:42 -0700 | [diff] [blame] | 130 | class shard_delete(action_common.atest_delete, shard): |
| 131 | """Class for running atest shard delete <shards>""" |
Jakob Juelich | 8b110ee | 2014-09-15 16:13:42 -0700 | [diff] [blame] | 132 | |
| 133 | def parse(self): |
| 134 | (options, leftover) = super(shard_delete, self).parse() |
Jakob Juelich | 8b110ee | 2014-09-15 16:13:42 -0700 | [diff] [blame] | 135 | self.data_item_key = 'hostname' |
| 136 | return (options, leftover) |
| 137 | |
| 138 | |
| 139 | def execute(self, *args, **kwargs): |
Jakob Juelich | 8b110ee | 2014-09-15 16:13:42 -0700 | [diff] [blame] | 140 | 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 Shi | 25e1fd4 | 2014-12-19 14:36:42 -0800 | [diff] [blame] | 143 | return super(shard_delete, self).execute(*args, **kwargs) |