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 |
| 12 | |
| 13 | See topic_common.py for a High Level Design and Algorithm. |
| 14 | """ |
| 15 | |
Dan Shi | 25e1fd4 | 2014-12-19 14:36:42 -0800 | [diff] [blame] | 16 | import sys |
Jakob Juelich | 8b110ee | 2014-09-15 16:13:42 -0700 | [diff] [blame] | 17 | from autotest_lib.cli import topic_common, action_common |
| 18 | |
| 19 | |
| 20 | class shard(topic_common.atest): |
| 21 | """shard class |
| 22 | atest shard [create|delete|list] <options>""" |
| 23 | usage_action = '[create|delete|list]' |
| 24 | topic = msg_topic = 'shard' |
| 25 | msg_items = '<shards>' |
| 26 | |
| 27 | def __init__(self): |
| 28 | """Add to the parser the options common to all the |
| 29 | shard actions""" |
| 30 | super(shard, self).__init__() |
| 31 | |
| 32 | self.topic_parse_info = topic_common.item_parse_info( |
| 33 | attribute_name='shards', |
| 34 | use_leftover=True) |
| 35 | |
| 36 | |
| 37 | def get_items(self): |
| 38 | return self.shards |
| 39 | |
| 40 | |
| 41 | class shard_help(shard): |
| 42 | """Just here to get the atest logic working. |
| 43 | Usage is set by its parent""" |
| 44 | pass |
| 45 | |
| 46 | |
| 47 | class shard_list(action_common.atest_list, shard): |
MK Ryu | 5dfcc89 | 2015-07-16 15:34:04 -0700 | [diff] [blame] | 48 | """Class for running atest shard list""" |
Jakob Juelich | 8b110ee | 2014-09-15 16:13:42 -0700 | [diff] [blame] | 49 | |
| 50 | def execute(self): |
MK Ryu | 5dfcc89 | 2015-07-16 15:34:04 -0700 | [diff] [blame] | 51 | filters = {} |
| 52 | if self.shards: |
| 53 | filters['hostname__in'] = self.shards |
| 54 | return super(shard_list, self).execute(op='get_shards', |
| 55 | filters=filters) |
Jakob Juelich | 8b110ee | 2014-09-15 16:13:42 -0700 | [diff] [blame] | 56 | |
| 57 | |
| 58 | def warn_if_label_assigned_to_multiple_shards(self, results): |
| 59 | """Prints a warning if one label is assigned to multiple shards. |
| 60 | |
| 61 | This should never happen, but if it does, better be safe. |
| 62 | |
| 63 | @param results: Results as passed to output(). |
| 64 | """ |
| 65 | assigned_labels = set() |
| 66 | for line in results: |
| 67 | for label in line['labels']: |
| 68 | if label in assigned_labels: |
| 69 | sys.stderr.write('WARNING: label %s is assigned to ' |
| 70 | 'multiple shards.\n' |
| 71 | 'This will lead to unpredictable behavor ' |
| 72 | 'in which hosts and jobs will be assigned ' |
| 73 | 'to which shard.\n') |
| 74 | assigned_labels.add(label) |
| 75 | |
| 76 | |
| 77 | def output(self, results): |
| 78 | self.warn_if_label_assigned_to_multiple_shards(results) |
| 79 | super(shard_list, self).output(results, ['hostname', 'labels']) |
| 80 | |
| 81 | |
| 82 | class shard_create(action_common.atest_create, shard): |
| 83 | """Class for running atest shard create -l <label> <shard>""" |
| 84 | def __init__(self): |
| 85 | super(shard_create, self).__init__() |
MK Ryu | 5dfcc89 | 2015-07-16 15:34:04 -0700 | [diff] [blame] | 86 | self.parser.add_option('-l', '--labels', |
| 87 | help=('Assign LABELs to the SHARD. All jobs that ' |
| 88 | 'require one of the labels will be run on ' |
| 89 | 'the shard. List multiple labels separated ' |
| 90 | 'by a comma.'), |
Jakob Juelich | 8b110ee | 2014-09-15 16:13:42 -0700 | [diff] [blame] | 91 | type='string', |
MK Ryu | 5dfcc89 | 2015-07-16 15:34:04 -0700 | [diff] [blame] | 92 | metavar='LABELS') |
Jakob Juelich | 8b110ee | 2014-09-15 16:13:42 -0700 | [diff] [blame] | 93 | |
| 94 | |
| 95 | def parse(self): |
MK Ryu | 5dfcc89 | 2015-07-16 15:34:04 -0700 | [diff] [blame] | 96 | (options, leftover) = super(shard_create, self).parse( |
| 97 | req_items='shards') |
| 98 | if not options.labels: |
| 99 | print ('Must provide one or more labels separated by a comma ' |
| 100 | 'with -l <labels>') |
Jakob Juelich | 8b110ee | 2014-09-15 16:13:42 -0700 | [diff] [blame] | 101 | self.parser.print_help() |
| 102 | sys.exit(1) |
| 103 | self.data_item_key = 'hostname' |
MK Ryu | 5dfcc89 | 2015-07-16 15:34:04 -0700 | [diff] [blame] | 104 | self.data['labels'] = options.labels |
Jakob Juelich | 8b110ee | 2014-09-15 16:13:42 -0700 | [diff] [blame] | 105 | return (options, leftover) |
| 106 | |
| 107 | |
| 108 | class shard_delete(action_common.atest_delete, shard): |
| 109 | """Class for running atest shard delete <shards>""" |
Jakob Juelich | 8b110ee | 2014-09-15 16:13:42 -0700 | [diff] [blame] | 110 | |
| 111 | def parse(self): |
| 112 | (options, leftover) = super(shard_delete, self).parse() |
Jakob Juelich | 8b110ee | 2014-09-15 16:13:42 -0700 | [diff] [blame] | 113 | self.data_item_key = 'hostname' |
| 114 | return (options, leftover) |
| 115 | |
| 116 | |
| 117 | def execute(self, *args, **kwargs): |
Jakob Juelich | 8b110ee | 2014-09-15 16:13:42 -0700 | [diff] [blame] | 118 | print 'Please ensure the shard host is powered off.' |
| 119 | print ('Otherwise DUTs might be used by multiple shards at the same ' |
| 120 | 'time, which will lead to serious correctness problems.') |
Dan Shi | 25e1fd4 | 2014-12-19 14:36:42 -0800 | [diff] [blame] | 121 | return super(shard_delete, self).execute(*args, **kwargs) |