Alex Miller | 0516e4c | 2013-06-03 18:07:48 -0700 | [diff] [blame] | 1 | # Copyright (c) 2013 The Chromium OS Authors. All rights reserved. |
| 2 | # Use of this source code is governed by a BSD-style license that can be |
| 3 | # found in the LICENSE file. |
| 4 | |
| 5 | |
| 6 | import logging |
| 7 | |
| 8 | import common |
| 9 | from autotest_lib.frontend.afe.json_rpc import proxy |
| 10 | from autotest_lib.server import frontend |
| 11 | |
| 12 | |
| 13 | ### Constants for label prefixes |
| 14 | CROS_VERSION_PREFIX = 'cros-version' |
Fang Deng | dbc8632 | 2013-08-09 16:17:30 -0700 | [diff] [blame^] | 15 | FW_VERSION_PREFIX = 'fw-version' |
Alex Miller | 0516e4c | 2013-06-03 18:07:48 -0700 | [diff] [blame] | 16 | |
| 17 | |
| 18 | ### Helpers to convert value to label |
| 19 | def cros_version_to_label(image): |
| 20 | """ |
| 21 | Returns the proper label name for a ChromeOS build of |image|. |
| 22 | |
| 23 | @param image: A string of the form 'lumpy-release/R28-3993.0.0' |
| 24 | @returns: A string that is the appropriate label name. |
| 25 | |
| 26 | """ |
| 27 | return CROS_VERSION_PREFIX + ':' + image |
| 28 | |
| 29 | |
| 30 | # TODO(milleral): http://crbug.com/249555 |
| 31 | # Create some way to discover and register provisioning tests so that we don't |
| 32 | # need to hand-maintain a list of all of them. |
| 33 | _provision_types = { |
| 34 | CROS_VERSION_PREFIX:'provision_AutoUpdate', |
Fang Deng | dbc8632 | 2013-08-09 16:17:30 -0700 | [diff] [blame^] | 35 | FW_VERSION_PREFIX:'provision_FirmwareUpdate', |
Alex Miller | 0516e4c | 2013-06-03 18:07:48 -0700 | [diff] [blame] | 36 | } |
| 37 | |
| 38 | |
| 39 | def can_provision(label): |
| 40 | """ |
| 41 | Returns True if the label is a label that we recognize as something we |
| 42 | know how to provision. |
| 43 | |
| 44 | @param label: The label as a string. |
| 45 | @returns: True if there exists a test to provision the label. |
| 46 | |
| 47 | """ |
| 48 | return label.split(':')[0] in _provision_types |
| 49 | |
| 50 | |
| 51 | def provisioner_for(name): |
| 52 | """ |
| 53 | Returns the provisioning class associated with the given (string) name. |
| 54 | |
| 55 | @param name: The name of the provision type being requested. |
| 56 | @returns: The subclass of Provisioner that was requested. |
| 57 | @raises KeyError: If the name was not recognized as a provision type. |
| 58 | |
| 59 | """ |
| 60 | return _provision_types[name] |
| 61 | |
| 62 | |
| 63 | def filter_labels(labels): |
| 64 | """ |
| 65 | Filter a list of labels into two sets: those labels that we know how to |
| 66 | change and those that we don't. For the ones we know how to change, split |
| 67 | them apart into the name of configuration type and its value. |
| 68 | |
| 69 | @param labels: A list of strings of labels. |
| 70 | @returns: A tuple where the first element is a set of unprovisionable |
| 71 | labels, and the second element is a set of the provisionable |
| 72 | labels. |
| 73 | |
| 74 | >>> filter_labels(['bluetooth', 'cros-version:lumpy-release/R28-3993.0.0']) |
| 75 | (set(['bluetooth']), set(['cros-version:lumpy-release/R28-3993.0.0'])) |
| 76 | |
| 77 | """ |
| 78 | capabilities = set() |
| 79 | configurations = set() |
| 80 | |
| 81 | for label in labels: |
| 82 | if can_provision(label): |
| 83 | configurations.add(label) |
| 84 | else: |
| 85 | capabilities.add(label) |
| 86 | |
| 87 | return capabilities, configurations |
| 88 | |
| 89 | |
| 90 | def split_labels(labels): |
| 91 | """ |
| 92 | Split a list of labels into a dict mapping name to value. All labels must |
| 93 | be provisionable labels, or else a ValueError |
| 94 | |
| 95 | @param labels: list of strings of label names |
| 96 | @returns: A dict of where the key is the configuration name, and the value |
| 97 | is the configuration value. |
| 98 | @raises: ValueError if a label is not a provisionable label. |
| 99 | |
| 100 | >>> split_labels(['cros-version:lumpy-release/R28-3993.0.0']) |
| 101 | {'cros-version': 'lumpy-release/R28-3993.0.0'} |
| 102 | >>> split_labels(['bluetooth']) |
| 103 | Traceback (most recent call last): |
| 104 | ... |
| 105 | ValueError: Unprovisionable label bluetooth |
| 106 | |
| 107 | """ |
| 108 | configurations = dict() |
| 109 | |
| 110 | for label in labels: |
| 111 | if can_provision(label): |
| 112 | name, value = label.split(':', 1) |
| 113 | configurations[name] = value |
| 114 | else: |
| 115 | raise ValueError('Unprovisionable label %s' % label) |
| 116 | |
| 117 | return configurations |
| 118 | |
| 119 | |
| 120 | # This has been copied out of dynamic_suite's reimager.py, which will be killed |
| 121 | # off in a future CL. I'd prefer if this would go away by doing |
| 122 | # http://crbug.com/249424, so that labels are just automatically made when we |
| 123 | # try to add them to a host. |
| 124 | def ensure_label_exists(name): |
| 125 | """ |
| 126 | Ensure that a label called |name| exists in the autotest DB. |
| 127 | |
| 128 | @param name: the label to check for/create. |
| 129 | @raises ValidationError: There was an error in the response that was |
| 130 | not because the label already existed. |
| 131 | |
| 132 | """ |
| 133 | afe = frontend.AFE() |
| 134 | try: |
| 135 | afe.create_label(name=name) |
| 136 | except proxy.ValidationError as ve: |
| 137 | if ('name' in ve.problem_keys and |
| 138 | 'This value must be unique' in ve.problem_keys['name']): |
| 139 | logging.debug('Version label %s already exists', name) |
| 140 | else: |
| 141 | raise ve |