OLD | NEW |
(Empty) | |
| 1 # Copyright 2014 The Chromium 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 """ Utilities for dealing with builder names. This module obtains its attributes |
| 7 dynamically from builder_name_schema.json. """ |
| 8 |
| 9 |
| 10 import json |
| 11 import os |
| 12 |
| 13 |
| 14 # All of these global variables are filled in by _LoadSchema(). |
| 15 |
| 16 # The full schema. |
| 17 BUILDER_NAME_SCHEMA = None |
| 18 |
| 19 # Character which separates parts of a builder name. |
| 20 BUILDER_NAME_SEP = None |
| 21 |
| 22 # Builder roles. |
| 23 BUILDER_ROLE_CANARY = 'Canary' |
| 24 BUILDER_ROLE_BUILD = 'Build' |
| 25 BUILDER_ROLE_HOUSEKEEPER = 'Housekeeper' |
| 26 BUILDER_ROLE_PERF = 'Perf' |
| 27 BUILDER_ROLE_TEST = 'Test' |
| 28 BUILDER_ROLES = (BUILDER_ROLE_CANARY, |
| 29 BUILDER_ROLE_BUILD, |
| 30 BUILDER_ROLE_HOUSEKEEPER, |
| 31 BUILDER_ROLE_PERF, |
| 32 BUILDER_ROLE_TEST) |
| 33 |
| 34 # Suffix which distinguishes trybots from normal bots. |
| 35 TRYBOT_NAME_SUFFIX = None |
| 36 |
| 37 |
| 38 def _LoadSchema(): |
| 39 """ Load the builder naming schema from the JSON file. """ |
| 40 |
| 41 def _UnicodeToStr(obj): |
| 42 """ Convert all unicode strings in obj to Python strings. """ |
| 43 if isinstance(obj, unicode): |
| 44 return str(obj) |
| 45 elif isinstance(obj, dict): |
| 46 return dict(map(_UnicodeToStr, obj.iteritems())) |
| 47 elif isinstance(obj, list): |
| 48 return list(map(_UnicodeToStr, obj)) |
| 49 elif isinstance(obj, tuple): |
| 50 return tuple(map(_UnicodeToStr, obj)) |
| 51 else: |
| 52 return obj |
| 53 |
| 54 builder_name_json_filename = os.path.join( |
| 55 os.path.dirname(__file__), 'builder_name_schema.json') |
| 56 builder_name_schema_json = json.load(open(builder_name_json_filename)) |
| 57 |
| 58 global BUILDER_NAME_SCHEMA |
| 59 BUILDER_NAME_SCHEMA = _UnicodeToStr( |
| 60 builder_name_schema_json['builder_name_schema']) |
| 61 |
| 62 global BUILDER_NAME_SEP |
| 63 BUILDER_NAME_SEP = _UnicodeToStr( |
| 64 builder_name_schema_json['builder_name_sep']) |
| 65 |
| 66 global TRYBOT_NAME_SUFFIX |
| 67 TRYBOT_NAME_SUFFIX = _UnicodeToStr( |
| 68 builder_name_schema_json['trybot_name_suffix']) |
| 69 |
| 70 # Since the builder roles are dictionary keys, just assert that the global |
| 71 # variables above account for all of them. |
| 72 assert len(BUILDER_ROLES) == len(BUILDER_NAME_SCHEMA) |
| 73 for role in BUILDER_ROLES: |
| 74 assert role in BUILDER_NAME_SCHEMA |
| 75 |
| 76 |
| 77 _LoadSchema() |
| 78 |
| 79 |
| 80 def MakeBuilderName(role, extra_config=None, is_trybot=False, **kwargs): |
| 81 schema = BUILDER_NAME_SCHEMA.get(role) |
| 82 if not schema: |
| 83 raise ValueError('%s is not a recognized role.' % role) |
| 84 for k, v in kwargs.iteritems(): |
| 85 if BUILDER_NAME_SEP in v: |
| 86 raise ValueError('%s not allowed in %s.' % (BUILDER_NAME_SEP, v)) |
| 87 if not k in schema: |
| 88 raise ValueError('Schema does not contain "%s": %s' %(k, schema)) |
| 89 if extra_config and BUILDER_NAME_SEP in extra_config: |
| 90 raise ValueError('%s not allowed in %s.' % (BUILDER_NAME_SEP, |
| 91 extra_config)) |
| 92 name_parts = [role] |
| 93 name_parts.extend([kwargs[attribute] for attribute in schema]) |
| 94 if extra_config: |
| 95 name_parts.append(extra_config) |
| 96 if is_trybot: |
| 97 name_parts.append(TRYBOT_NAME_SUFFIX) |
| 98 return BUILDER_NAME_SEP.join(name_parts) |
| 99 |
| 100 |
| 101 def BuilderNameFromObject(obj, is_trybot=False): |
| 102 """Create a builder name based on properties of the given object. |
| 103 |
| 104 Args: |
| 105 obj: the object from which to create the builder name. The object must |
| 106 have as properties: |
| 107 - A valid builder role, as defined in the JSON file |
| 108 - All properties listed in the JSON file for that role |
| 109 - Optionally, an extra_config property |
| 110 is_trybot: bool; whether or not the builder is a trybot. |
| 111 Returns: |
| 112 string which combines the properties of the given object into a valid |
| 113 builder name. |
| 114 """ |
| 115 schema = BUILDER_NAME_SCHEMA.get(obj.role) |
| 116 if not schema: |
| 117 raise ValueError('%s is not a recognized role.' % obj.role) |
| 118 name_parts = [obj.role] |
| 119 for attr_name in schema: |
| 120 attr_val = getattr(obj, attr_name) |
| 121 name_parts.append(attr_val) |
| 122 extra_config = getattr(obj, 'extra_config', None) |
| 123 if extra_config: |
| 124 name_parts.append(extra_config) |
| 125 if is_trybot: |
| 126 name_parts.append(TRYBOT_NAME_SUFFIX) |
| 127 return BUILDER_NAME_SEP.join(name_parts) |
| 128 |
| 129 |
| 130 def IsTrybot(builder_name): |
| 131 """ Returns true if builder_name refers to a trybot (as opposed to a |
| 132 waterfall bot). """ |
| 133 return builder_name.endswith(TRYBOT_NAME_SUFFIX) |
| 134 |
| 135 |
| 136 def GetWaterfallBot(builder_name): |
| 137 """Returns the name of the waterfall bot for this builder. If it is not a |
| 138 trybot, builder_name is returned unchanged. If it is a trybot the name is |
| 139 returned without the trybot suffix.""" |
| 140 if not IsTrybot(builder_name): |
| 141 return builder_name |
| 142 return _WithoutSuffix(builder_name, BUILDER_NAME_SEP + TRYBOT_NAME_SUFFIX) |
| 143 |
| 144 |
| 145 def TrybotName(builder_name): |
| 146 """Returns the name of the trybot clone of this builder. |
| 147 |
| 148 If the given builder is a trybot, the name is returned unchanged. If not, the |
| 149 TRYBOT_NAME_SUFFIX is appended. |
| 150 """ |
| 151 if builder_name.endswith(TRYBOT_NAME_SUFFIX): |
| 152 return builder_name |
| 153 return builder_name + BUILDER_NAME_SEP + TRYBOT_NAME_SUFFIX |
| 154 |
| 155 |
| 156 def _WithoutSuffix(string, suffix): |
| 157 """ Returns a copy of string 'string', but with suffix 'suffix' removed. |
| 158 Raises ValueError if string does not end with suffix. """ |
| 159 if not string.endswith(suffix): |
| 160 raise ValueError('_WithoutSuffix: string %s does not end with suffix %s' % ( |
| 161 string, suffix)) |
| 162 return string[:-len(suffix)] |
| 163 |
| 164 |
| 165 def DictForBuilderName(builder_name): |
| 166 """Makes a dictionary containing details about the builder from its name.""" |
| 167 split_name = builder_name.split(BUILDER_NAME_SEP) |
| 168 |
| 169 def pop_front(): |
| 170 try: |
| 171 return split_name.pop(0) |
| 172 except: |
| 173 raise ValueError('Invalid builder name: %s' % builder_name) |
| 174 |
| 175 result = {'is_trybot': False} |
| 176 |
| 177 if split_name[-1] == TRYBOT_NAME_SUFFIX: |
| 178 result['is_trybot'] = True |
| 179 split_name.pop() |
| 180 |
| 181 if split_name[0] in BUILDER_NAME_SCHEMA.keys(): |
| 182 key_list = BUILDER_NAME_SCHEMA[split_name[0]] |
| 183 result['role'] = pop_front() |
| 184 for key in key_list: |
| 185 result[key] = pop_front() |
| 186 if split_name: |
| 187 result['extra_config'] = pop_front() |
| 188 if split_name: |
| 189 raise ValueError('Invalid builder name: %s' % builder_name) |
| 190 else: |
| 191 raise ValueError('Invalid builder name: %s' % builder_name) |
| 192 return result |
| 193 |
| 194 |
OLD | NEW |