1# -*- coding: utf-8 -*- 2# Copyright 2015 The Chromium OS Authors. All rights reserved. 3# Use of this source code is governed by a BSD-style license that can be 4# found in the LICENSE file. 5 6"""Configuration options for various cbuildbot builders.""" 7 8from __future__ import print_function 9 10import copy 11import itertools 12import json 13import numbers 14import os 15import re 16 17from autotest_lib.utils.frozen_chromite.lib import constants 18from autotest_lib.utils.frozen_chromite.lib import osutils 19from autotest_lib.utils.frozen_chromite.utils import memoize 20 21GS_PATH_DEFAULT = 'default' # Means gs://chromeos-image-archive/ + bot_id 22 23# Contains the valid build config suffixes. 24CONFIG_TYPE_RELEASE = 'release' 25CONFIG_TYPE_FULL = 'full' 26CONFIG_TYPE_FIRMWARE = 'firmware' 27CONFIG_TYPE_FACTORY = 'factory' 28CONFIG_TYPE_TOOLCHAIN = 'toolchain' 29 30# DISPLAY labels are used to group related builds together in the GE UI. 31 32DISPLAY_LABEL_TRYJOB = 'tryjob' 33DISPLAY_LABEL_INCREMENATAL = 'incremental' 34DISPLAY_LABEL_FULL = 'full' 35DISPLAY_LABEL_CHROME_INFORMATIONAL = 'chrome_informational' 36DISPLAY_LABEL_INFORMATIONAL = 'informational' 37DISPLAY_LABEL_RELEASE = 'release' 38DISPLAY_LABEL_CHROME_PFQ = 'chrome_pfq' 39DISPLAY_LABEL_MST_ANDROID_PFQ = 'mst_android_pfq' 40DISPLAY_LABEL_VMMST_ANDROID_PFQ = 'vmmst_android_pfq' 41DISPLAY_LABEL_PI_ANDROID_PFQ = 'pi_android_pfq' 42DISPLAY_LABEL_QT_ANDROID_PFQ = 'qt_android_pfq' 43DISPLAY_LABEL_RVC_ANDROID_PFQ = 'rvc_android_pfq' 44DISPLAY_LABEL_VMRVC_ANDROID_PFQ = 'vmrvc_android_pfq' 45DISPLAY_LABEL_FIRMWARE = 'firmware' 46DISPLAY_LABEL_FACTORY = 'factory' 47DISPLAY_LABEL_TOOLCHAIN = 'toolchain' 48DISPLAY_LABEL_UTILITY = 'utility' 49DISPLAY_LABEL_PRODUCTION_TRYJOB = 'production_tryjob' 50 51# This list of constants should be kept in sync with GoldenEye code. 52ALL_DISPLAY_LABEL = { 53 DISPLAY_LABEL_TRYJOB, 54 DISPLAY_LABEL_INCREMENATAL, 55 DISPLAY_LABEL_FULL, 56 DISPLAY_LABEL_CHROME_INFORMATIONAL, 57 DISPLAY_LABEL_INFORMATIONAL, 58 DISPLAY_LABEL_RELEASE, 59 DISPLAY_LABEL_CHROME_PFQ, 60 DISPLAY_LABEL_MST_ANDROID_PFQ, 61 DISPLAY_LABEL_VMMST_ANDROID_PFQ, 62 DISPLAY_LABEL_PI_ANDROID_PFQ, 63 DISPLAY_LABEL_QT_ANDROID_PFQ, 64 DISPLAY_LABEL_RVC_ANDROID_PFQ, 65 DISPLAY_LABEL_VMRVC_ANDROID_PFQ, 66 DISPLAY_LABEL_FIRMWARE, 67 DISPLAY_LABEL_FACTORY, 68 DISPLAY_LABEL_TOOLCHAIN, 69 DISPLAY_LABEL_UTILITY, 70 DISPLAY_LABEL_PRODUCTION_TRYJOB, 71} 72 73# These values must be kept in sync with the ChromeOS LUCI builders. 74# 75# https://chrome-internal.googlesource.com/chromeos/ 76# infra/config/+/refs/heads/master/luci/cr-buildbucket.cfg 77LUCI_BUILDER_FACTORY = 'Factory' 78LUCI_BUILDER_FULL = 'Full' 79LUCI_BUILDER_INCREMENTAL = 'Incremental' 80LUCI_BUILDER_INFORMATIONAL = 'Informational' 81LUCI_BUILDER_INFRA = 'Infra' 82LUCI_BUILDER_LEGACY_RELEASE = 'LegacyRelease' 83LUCI_BUILDER_PFQ = 'PFQ' 84LUCI_BUILDER_RAPID = 'Rapid' 85LUCI_BUILDER_RELEASE = 'Release' 86LUCI_BUILDER_STAGING = 'Staging' 87LUCI_BUILDER_TRY = 'Try' 88 89ALL_LUCI_BUILDER = { 90 LUCI_BUILDER_FACTORY, 91 LUCI_BUILDER_FULL, 92 LUCI_BUILDER_INCREMENTAL, 93 LUCI_BUILDER_INFORMATIONAL, 94 LUCI_BUILDER_INFRA, 95 LUCI_BUILDER_LEGACY_RELEASE, 96 LUCI_BUILDER_PFQ, 97 LUCI_BUILDER_RAPID, 98 LUCI_BUILDER_RELEASE, 99 LUCI_BUILDER_STAGING, 100 LUCI_BUILDER_TRY, 101} 102 103 104def isTryjobConfig(build_config): 105 """Is a given build config a tryjob config, or a production config? 106 107 Args: 108 build_config: A fully populated instance of BuildConfig. 109 110 Returns: 111 Boolean. True if it's a tryjob config. 112 """ 113 return build_config.luci_builder in [LUCI_BUILDER_RAPID, LUCI_BUILDER_TRY] 114 115# In the Json, this special build config holds the default values for all 116# other configs. 117DEFAULT_BUILD_CONFIG = '_default' 118 119# Constants for config template file 120CONFIG_TEMPLATE_BOARDS = 'boards' 121CONFIG_TEMPLATE_NAME = 'name' 122CONFIG_TEMPLATE_EXPERIMENTAL = 'experimental' 123CONFIG_TEMPLATE_LEADER_BOARD = 'leader_board' 124CONFIG_TEMPLATE_BOARD_GROUP = 'board_group' 125CONFIG_TEMPLATE_BUILDER = 'builder' 126CONFIG_TEMPLATE_RELEASE = 'RELEASE' 127CONFIG_TEMPLATE_CONFIGS = 'configs' 128CONFIG_TEMPLATE_ARCH = 'arch' 129CONFIG_TEMPLATE_RELEASE_BRANCH = 'release_branch' 130CONFIG_TEMPLATE_REFERENCE_BOARD_NAME = 'reference_board_name' 131CONFIG_TEMPLATE_MODELS = 'models' 132CONFIG_TEMPLATE_MODEL_NAME = 'name' 133CONFIG_TEMPLATE_MODEL_BOARD_NAME = 'board_name' 134CONFIG_TEMPLATE_MODEL_TEST_SUITES = 'test_suites' 135CONFIG_TEMPLATE_MODEL_CQ_TEST_ENABLED = 'cq_test_enabled' 136 137CONFIG_X86_INTERNAL = 'X86_INTERNAL' 138CONFIG_X86_EXTERNAL = 'X86_EXTERNAL' 139CONFIG_ARM_INTERNAL = 'ARM_INTERNAL' 140CONFIG_ARM_EXTERNAL = 'ARM_EXTERNAL' 141 142 143def IsCanaryMaster(builder_run): 144 """Returns True if this build type is master-release""" 145 return (builder_run.config.build_type == constants.CANARY_TYPE 146 and builder_run.config.master 147 and builder_run.manifest_branch == 'master') 148 149 150def IsPFQType(b_type): 151 """Returns True if this build type is a PFQ.""" 152 return b_type in (constants.PFQ_TYPE, constants.ANDROID_PFQ_TYPE) 153 154 155def IsCanaryType(b_type): 156 """Returns True if this build type is a Canary.""" 157 return b_type == constants.CANARY_TYPE 158 159 160def IsMasterAndroidPFQ(config): 161 """Returns True if this build is master Android PFQ type.""" 162 return config.build_type == constants.ANDROID_PFQ_TYPE and config.master 163 164 165def GetHWTestEnv(builder_run_config, model_config=None, suite_config=None): 166 """Return the env of a suite to run for a given build/model. 167 168 Args: 169 builder_run_config: The BuildConfig object inside a BuilderRun object. 170 model_config: A ModelTestConfig object to test against. 171 suite_config: A HWTestConfig object to test against. 172 173 Returns: 174 A string variable to indiate the hwtest environment. 175 """ 176 enable_suite = True if suite_config is None else suite_config.enable_skylab 177 enable_model = True if model_config is None else model_config.enable_skylab 178 if (builder_run_config.enable_skylab_hw_tests and enable_suite 179 and enable_model): 180 return constants.ENV_SKYLAB 181 182 return constants.ENV_AUTOTEST 183 184 185class AttrDict(dict): 186 """Dictionary with 'attribute' access. 187 188 This is identical to a dictionary, except that string keys can be addressed as 189 read-only attributes. 190 """ 191 192 def __getattr__(self, name): 193 """Support attribute-like access to each dict entry.""" 194 if name in self: 195 return self[name] 196 197 # Super class (dict) has no __getattr__ method, so use __getattribute__. 198 return super(AttrDict, self).__getattribute__(name) 199 200 201class BuildConfig(AttrDict): 202 """Dictionary of explicit configuration settings for a cbuildbot config 203 204 Each dictionary entry is in turn a dictionary of config_param->value. 205 206 See DefaultSettings for details on known configurations, and their 207 documentation. 208 """ 209 210 def deepcopy(self): 211 """Create a deep copy of this object. 212 213 This is a specialized version of copy.deepcopy() for BuildConfig objects. It 214 speeds up deep copies by 10x because we know in advance what is stored 215 inside a BuildConfig object and don't have to do as much introspection. This 216 function is called a lot during setup of the config objects so optimizing it 217 makes a big difference. (It saves seconds off the load time of this module!) 218 """ 219 result = BuildConfig(self) 220 221 # Here is where we handle all values that need deepcopy instead of shallow. 222 for k, v in result.items(): 223 if v is not None: 224 if k == 'child_configs': 225 result[k] = [x.deepcopy() for x in v] 226 elif k in ('vm_tests', 'vm_tests_override', 'hw_tests', 227 'hw_tests_override', 'tast_vm_tests'): 228 result[k] = [copy.copy(x) for x in v] 229 # type(v) is faster than isinstance. 230 elif type(v) is list: # pylint: disable=unidiomatic-typecheck 231 result[k] = v[:] 232 233 return result 234 235 def apply(self, *args, **kwargs): 236 """Apply changes to this BuildConfig. 237 238 Note: If an override is callable, it will be called and passed the prior 239 value for the given key (or None) to compute the new value. 240 241 Args: 242 args: Dictionaries or templates to update this config with. 243 kwargs: Settings to inject; see DefaultSettings for valid values. 244 245 Returns: 246 self after changes are applied. 247 """ 248 inherits = list(args) 249 inherits.append(kwargs) 250 251 for update_config in inherits: 252 for name, value in update_config.items(): 253 if callable(value): 254 # If we are applying to a fixed value, we resolve to a fixed value. 255 # Otherwise, we save off a callable to apply later, perhaps with 256 # nested callables (IE: we curry them). This allows us to use 257 # callables in templates, and apply templates to each other and still 258 # get the expected result when we use them later on. 259 # 260 # Delaying the resolution of callables is safe, because "Add()" always 261 # applies against the default, which has fixed values for everything. 262 263 if name in self: 264 # apply it to the current value. 265 if callable(self[name]): 266 # If we have no fixed value to resolve with, stack the callables. 267 def stack(new_callable, old_callable): 268 """Helper method to isolate namespace for closure.""" 269 return lambda fixed: new_callable( 270 old_callable(fixed)) 271 272 self[name] = stack(value, self[name]) 273 else: 274 # If the current value was a fixed value, apply the callable. 275 self[name] = value(self[name]) 276 else: 277 # If we had no value to apply it to, save it for later. 278 self[name] = value 279 280 elif name == '_template': 281 # We never apply _template. You have to set it through Add. 282 pass 283 284 else: 285 # Simple values overwrite whatever we do or don't have. 286 self[name] = value 287 288 return self 289 290 def derive(self, *args, **kwargs): 291 """Create a new config derived from this one. 292 293 Note: If an override is callable, it will be called and passed the prior 294 value for the given key (or None) to compute the new value. 295 296 Args: 297 args: Mapping instances to mixin. 298 kwargs: Settings to inject; see DefaultSettings for valid values. 299 300 Returns: 301 A new _config instance. 302 """ 303 return self.deepcopy().apply(*args, **kwargs) 304 305 def AddSlave(self, slave): 306 """Assign slave config(s) to a build master. 307 308 A helper for adding slave configs to a master config. 309 """ 310 assert self.master 311 if self['slave_configs'] is None: 312 self['slave_configs'] = [] 313 self.slave_configs.append(slave.name) 314 self.slave_configs.sort() 315 316 def AddSlaves(self, slaves): 317 """Assign slave config(s) to a build master. 318 319 A helper for adding slave configs to a master config. 320 """ 321 assert self.master 322 if self['slave_configs'] is None: 323 self['slave_configs'] = [] 324 self.slave_configs.extend(slave_config.name for slave_config in slaves) 325 self.slave_configs.sort() 326 327 328class VMTestConfig(object): 329 """Config object for virtual machine tests suites. 330 331 Attributes: 332 test_type: Test type to be run. 333 test_suite: Test suite to be run in VMTest. 334 timeout: Number of seconds to wait before timing out waiting for 335 results. 336 retry: Whether we should retry tests that fail in a suite run. 337 max_retries: Integer, maximum job retries allowed at suite level. 338 None for no max. 339 warn_only: Boolean, failure on VM tests warns only. 340 use_ctest: Use the old ctest code path rather than the new chromite one. 341 """ 342 DEFAULT_TEST_TIMEOUT = 90 * 60 343 344 def __init__(self, 345 test_type, 346 test_suite=None, 347 timeout=DEFAULT_TEST_TIMEOUT, 348 retry=False, 349 max_retries=constants.VM_TEST_MAX_RETRIES, 350 warn_only=False, 351 use_ctest=True): 352 """Constructor -- see members above.""" 353 self.test_type = test_type 354 self.test_suite = test_suite 355 self.timeout = timeout 356 self.retry = retry 357 self.max_retries = max_retries 358 self.warn_only = warn_only 359 self.use_ctest = use_ctest 360 361 def __eq__(self, other): 362 return self.__dict__ == other.__dict__ 363 364 365class GCETestConfig(object): 366 """Config object for GCE tests suites. 367 368 Attributes: 369 test_type: Test type to be run. 370 test_suite: Test suite to be run in GCETest. 371 timeout: Number of seconds to wait before timing out waiting for 372 results. 373 use_ctest: Use the old ctest code path rather than the new chromite one. 374 """ 375 DEFAULT_TEST_TIMEOUT = 60 * 60 376 377 def __init__(self, 378 test_type, 379 test_suite=None, 380 timeout=DEFAULT_TEST_TIMEOUT, 381 use_ctest=True): 382 """Constructor -- see members above.""" 383 self.test_type = test_type 384 self.test_suite = test_suite 385 self.timeout = timeout 386 self.use_ctest = use_ctest 387 388 def __eq__(self, other): 389 return self.__dict__ == other.__dict__ 390 391 392class TastVMTestConfig(object): 393 """Config object for a Tast virtual-machine-based test suite. 394 395 Attributes: 396 name: String containing short human-readable name describing test suite. 397 test_exprs: List of string expressions describing which tests to run; this 398 is passed directly to the 'tast run' command. See 399 https://goo.gl/UPNEgT for info about test expressions. 400 timeout: Number of seconds to wait before timing out waiting for 401 results. 402 """ 403 DEFAULT_TEST_TIMEOUT = 60 * 60 404 405 def __init__(self, suite_name, test_exprs, timeout=DEFAULT_TEST_TIMEOUT): 406 """Constructor -- see members above.""" 407 # This is an easy mistake to make and results in confusing errors later when 408 # a list of one-character strings gets passed to the tast command. 409 if not isinstance(test_exprs, list): 410 raise TypeError('test_exprs must be list of strings') 411 self.suite_name = suite_name 412 self.test_exprs = test_exprs 413 self.timeout = timeout 414 415 def __eq__(self, other): 416 return self.__dict__ == other.__dict__ 417 418 419class MoblabVMTestConfig(object): 420 """Config object for moblab tests suites. 421 422 Attributes: 423 test_type: Test type to be run. 424 timeout: Number of seconds to wait before timing out waiting for 425 results. 426 """ 427 DEFAULT_TEST_TIMEOUT = 60 * 60 428 429 def __init__(self, test_type, timeout=DEFAULT_TEST_TIMEOUT): 430 """Constructor -- see members above.""" 431 self.test_type = test_type 432 self.timeout = timeout 433 434 def __eq__(self, other): 435 return self.__dict__ == other.__dict__ 436 437 438class ModelTestConfig(object): 439 """Model specific config that controls which test suites are executed. 440 441 Attributes: 442 name: The name of the model that will be tested (matches model label) 443 lab_board_name: The name of the board in the lab (matches board label) 444 test_suites: List of hardware test suites that will be executed. 445 """ 446 447 def __init__(self, 448 name, 449 lab_board_name, 450 test_suites=None, 451 enable_skylab=True): 452 """Constructor -- see members above.""" 453 self.name = name 454 self.lab_board_name = lab_board_name 455 self.test_suites = test_suites 456 self.enable_skylab = enable_skylab 457 458 def __eq__(self, other): 459 return self.__dict__ == other.__dict__ 460 461 462class HWTestConfig(object): 463 """Config object for hardware tests suites. 464 465 Attributes: 466 suite: Name of the test suite to run. 467 timeout: Number of seconds to wait before timing out waiting for 468 results. 469 pool: Pool to use for hw testing. 470 blocking: Setting this to true requires that this suite must PASS for suites 471 scheduled after it to run. This also means any suites that are 472 scheduled before a blocking one are also blocking ones scheduled 473 after. This should be used when you want some suites to block 474 whether or not others should run e.g. only run longer-running 475 suites if some core ones pass first. 476 477 Note, if you want multiple suites to block other suites but run 478 in parallel, you should only mark the last one scheduled as 479 blocking (it effectively serves as a thread/process join). 480 async: Fire-and-forget suite. 481 warn_only: Failure on HW tests warns only (does not generate error). 482 critical: Usually we consider structural failures here as OK. 483 priority: Priority at which tests in the suite will be scheduled in 484 the hw lab. 485 file_bugs: Should we file bugs if a test fails in a suite run. 486 minimum_duts: minimum number of DUTs required for testing in the hw lab. 487 retry: Whether we should retry tests that fail in a suite run. 488 max_retries: Integer, maximum job retries allowed at suite level. 489 None for no max. 490 suite_min_duts: Preferred minimum duts. Lab will prioritize on getting such 491 number of duts even if the suite is competing with 492 other suites that have higher priority. 493 suite_args: Arguments passed to the suite. This should be a dict 494 representing keyword arguments. The value is marshalled 495 using repr(), so the dict values should be basic types. 496 quota_account: The quotascheduler account to use for all tests in this 497 suite. 498 499 Some combinations of member settings are invalid: 500 * A suite config may not specify both blocking and async. 501 * A suite config may not specify both warn_only and critical. 502 """ 503 _MINUTE = 60 504 _HOUR = 60 * _MINUTE 505 _DAY = 24 * _HOUR 506 # CTS timeout ~ 2 * expected runtime in case other tests are using the CTS 507 # pool. 508 # Must not exceed the buildbucket build timeout set at 509 # https://chrome-internal.googlesource.com/chromeos/infra/config/+/8f12edac54383831aaed9ed1819ef909a66ecc97/testplatform/main.star#90 510 CTS_QUAL_HW_TEST_TIMEOUT = int(1 * _DAY + 18 * _HOUR) 511 # GTS runs faster than CTS. But to avoid starving GTS by CTS we set both 512 # timeouts equal. 513 GTS_QUAL_HW_TEST_TIMEOUT = CTS_QUAL_HW_TEST_TIMEOUT 514 SHARED_HW_TEST_TIMEOUT = int(3.0 * _HOUR) 515 PALADIN_HW_TEST_TIMEOUT = int(2.0 * _HOUR) 516 BRANCHED_HW_TEST_TIMEOUT = int(10.0 * _HOUR) 517 518 # TODO(jrbarnette) Async HW test phases complete within seconds. 519 # however, the tests they start can require hours to complete. 520 # Chromite code doesn't distinguish "timeout for Autotest" from 521 # timeout in the builder. This is WRONG WRONG WRONG. But, until 522 # there's a better fix, we'll allow these phases hours to fail. 523 ASYNC_HW_TEST_TIMEOUT = int(250.0 * _MINUTE) 524 525 def __init__(self, 526 suite, 527 pool=constants.HWTEST_QUOTA_POOL, 528 timeout=SHARED_HW_TEST_TIMEOUT, 529 warn_only=False, 530 critical=False, 531 blocking=False, 532 file_bugs=False, 533 priority=constants.HWTEST_BUILD_PRIORITY, 534 retry=True, 535 max_retries=constants.HWTEST_MAX_RETRIES, 536 minimum_duts=0, 537 suite_min_duts=0, 538 suite_args=None, 539 offload_failures_only=False, 540 enable_skylab=True, 541 quota_account=constants.HWTEST_QUOTA_ACCOUNT_BVT, 542 **kwargs): 543 """Constructor -- see members above.""" 544 # Python 3.7+ made async a reserved keyword. 545 asynchronous = kwargs.pop('async', False) 546 setattr(self, 'async', asynchronous) 547 assert not kwargs, 'Excess kwargs found: %s' % (kwargs, ) 548 549 assert not asynchronous or not blocking, '%s is async and blocking' % suite 550 assert not warn_only or not critical 551 self.suite = suite 552 self.pool = pool 553 self.timeout = timeout 554 self.blocking = blocking 555 self.warn_only = warn_only 556 self.critical = critical 557 self.file_bugs = file_bugs 558 self.priority = priority 559 self.retry = retry 560 self.max_retries = max_retries 561 self.minimum_duts = minimum_duts 562 self.suite_min_duts = suite_min_duts 563 self.suite_args = suite_args 564 self.offload_failures_only = offload_failures_only 565 # Usually whether to run in skylab is controlled by 'enable_skylab_hw_test' 566 # in build config. But for some particular suites, we want to exclude them 567 # from Skylab even if the build config is migrated to Skylab. 568 self.enable_skylab = enable_skylab 569 self.quota_account = quota_account 570 571 def _SetCommonBranchedValues(self): 572 """Set the common values for branched builds.""" 573 self.timeout = max(HWTestConfig.BRANCHED_HW_TEST_TIMEOUT, self.timeout) 574 575 # Set minimum_duts default to 0, which means that lab will not check the 576 # number of available duts to meet the minimum requirement before creating 577 # a suite job for branched build. 578 self.minimum_duts = 0 579 580 def SetBranchedValuesForSkylab(self): 581 """Set suite values for branched builds for skylab.""" 582 self._SetCommonBranchedValues() 583 584 if (constants.SKYLAB_HWTEST_PRIORITIES_MAP[self.priority] < 585 constants.SKYLAB_HWTEST_PRIORITIES_MAP[ 586 constants.HWTEST_DEFAULT_PRIORITY]): 587 self.priority = constants.HWTEST_DEFAULT_PRIORITY 588 589 def SetBranchedValues(self): 590 """Changes the HW Test timeout/priority values to branched values.""" 591 self._SetCommonBranchedValues() 592 593 # Only reduce priority if it's lower. 594 new_priority = constants.HWTEST_PRIORITIES_MAP[ 595 constants.HWTEST_DEFAULT_PRIORITY] 596 if isinstance(self.priority, numbers.Integral): 597 self.priority = min(self.priority, new_priority) 598 elif constants.HWTEST_PRIORITIES_MAP[self.priority] > new_priority: 599 self.priority = new_priority 600 601 @property 602 def timeout_mins(self): 603 return self.timeout // 60 604 605 def __eq__(self, other): 606 return self.__dict__ == other.__dict__ 607 608 609class NotificationConfig(object): 610 """Config object for defining notification settings. 611 612 Attributes: 613 email: Email address that receives failure notifications. 614 threshold: Number of consecutive failures that should occur in order to 615 be notified. This number should be greater than or equal to 1. If 616 none is specified, default is 1. 617 template: Email template luci-notify should use when sending the email 618 notification. If none is specified, uses the default template. 619 """ 620 DEFAULT_TEMPLATE = 'legacy_release' 621 DEFAULT_THRESHOLD = 1 622 623 def __init__(self, 624 email, 625 threshold=DEFAULT_THRESHOLD, 626 template=DEFAULT_TEMPLATE): 627 """Constructor -- see members above.""" 628 self.email = email 629 self.threshold = threshold 630 self.template = template 631 self.threshold = threshold 632 633 @property 634 def email_notify(self): 635 return {'email': self.email, 'template': self.template} 636 637 def __eq__(self, other): 638 return self.__dict__ == other.__dict__ 639 640 641def DefaultSettings(): 642 # Enumeration of valid settings; any/all config settings must be in this. 643 # All settings must be documented. 644 return dict( 645 # The name of the template we inherit settings from. 646 _template=None, 647 648 # The name of the config. 649 name=None, 650 651 # A list of boards to build. 652 boards=None, 653 654 # A list of ModelTestConfig objects that represent all of the models 655 # supported by a given unified build and their corresponding test config. 656 models=[], 657 658 # This value defines what part of the Golden Eye UI is responsible for 659 # displaying builds of this build config. The value is required, and 660 # must be in ALL_DISPLAY_LABEL. 661 # TODO: Make the value required after crbug.com/776955 is finished. 662 display_label=None, 663 664 # This defines which LUCI Builder to use. It must match an entry in: 665 # 666 # https://chrome-internal.git.corp.google.com/chromeos/ 667 # manifest-internal/+/infra/config/cr-buildbucket.cfg 668 # 669 luci_builder=LUCI_BUILDER_LEGACY_RELEASE, 670 671 # The profile of the variant to set up and build. 672 profile=None, 673 674 # This bot pushes changes to the overlays. 675 master=False, 676 677 # A basic_builder is a special configuration which does not perform tests 678 # or mutate external config. 679 basic_builder=False, 680 681 # If this bot triggers slave builds, this will contain a list of 682 # slave config names. 683 slave_configs=None, 684 685 # If False, this flag indicates that the CQ should not check whether 686 # this bot passed or failed. Set this to False if you are setting up a 687 # new bot. Once the bot is on the waterfall and is consistently green, 688 # mark the builder as important=True. 689 important=True, 690 691 # If True, build config should always be run as if --debug was set 692 # on the cbuildbot command line. This is different from 'important' 693 # and is usually correlated with tryjob build configs. 694 debug=False, 695 696 # If True, use the debug instance of CIDB instead of prod. 697 debug_cidb=False, 698 699 # Timeout for the build as a whole (in seconds). 700 build_timeout=(5 * 60 + 30) * 60, 701 702 # A list of NotificationConfig objects describing who to notify of builder 703 # failures. 704 notification_configs=[], 705 706 # An integer. If this builder fails this many times consecutively, send 707 # an alert email to the recipients health_alert_recipients. This does 708 # not apply to tryjobs. This feature is similar to the ERROR_WATERMARK 709 # feature of upload_symbols, and it may make sense to merge the features 710 # at some point. 711 health_threshold=0, 712 713 # List of email addresses to send health alerts to for this builder. It 714 # supports automatic email address lookup for the following sheriff 715 # types: 716 # 'tree': tree sheriffs 717 # 'chrome': chrome gardeners 718 health_alert_recipients=[], 719 720 # Whether this is an internal build config. 721 internal=False, 722 723 # Whether this is a branched build config. Used for pfq logic. 724 branch=False, 725 726 # The name of the manifest to use. E.g., to use the buildtools manifest, 727 # specify 'buildtools'. 728 manifest=constants.DEFAULT_MANIFEST, 729 730 # emerge use flags to use while setting up the board, building packages, 731 # making images, etc. 732 useflags=[], 733 734 # Set the variable CHROMEOS_OFFICIAL for the build. Known to affect 735 # parallel_emerge, cros_set_lsb_release, and chromeos_version.sh. See 736 # bug chromium-os:14649 737 chromeos_official=False, 738 739 # Use binary packages for building the toolchain. (emerge --getbinpkg) 740 usepkg_toolchain=True, 741 742 # Use binary packages for build_packages and setup_board. 743 usepkg_build_packages=True, 744 745 # Does this profile need to sync chrome? If None, we guess based on 746 # other factors. If True/False, we always do that. 747 sync_chrome=None, 748 749 # Use the newest ebuilds for all the toolchain packages. 750 latest_toolchain=False, 751 752 # This is only valid when latest_toolchain is True. If you set this to a 753 # commit-ish, the gcc ebuild will use it to build the toolchain 754 # compiler. 755 gcc_githash=None, 756 757 # Wipe and replace the board inside the chroot. 758 board_replace=False, 759 760 # Wipe and replace chroot, but not source. 761 chroot_replace=True, 762 763 # Create the chroot on a loopback-mounted chroot.img instead of a bare 764 # directory. Required for snapshots; otherwise optional. 765 chroot_use_image=True, 766 767 # Uprevs the local ebuilds to build new changes since last stable. 768 # build. If master then also pushes these changes on success. Note that 769 # we uprev on just about every bot config because it gives us a more 770 # deterministic build system (the tradeoff being that some bots build 771 # from source more frequently than if they never did an uprev). This way 772 # the release/factory/etc... builders will pick up changes that devs 773 # pushed before it runs, but after the corresponding PFQ bot ran (which 774 # is what creates+uploads binpkgs). The incremental bots are about the 775 # only ones that don't uprev because they mimic the flow a developer 776 # goes through on their own local systems. 777 uprev=True, 778 779 # Select what overlays to look at for revving and prebuilts. This can be 780 # any constants.VALID_OVERLAYS. 781 overlays=constants.PUBLIC_OVERLAYS, 782 783 # Select what overlays to push at. This should be a subset of overlays 784 # for the particular builder. Must be None if not a master. There 785 # should only be one master bot pushing changes to each overlay per 786 # branch. 787 push_overlays=None, 788 789 # Uprev Android, values of 'latest_release', or None. 790 android_rev=None, 791 792 # Which Android branch build do we try to uprev from. 793 android_import_branch=None, 794 795 # Android package name. 796 android_package=None, 797 798 # Uprev Chrome, values of 'tot', 'stable_release', or None. 799 chrome_rev=None, 800 801 # Exit the builder right after checking compilation. 802 # TODO(mtennant): Should be something like "compile_check_only". 803 compilecheck=False, 804 805 # If True, run DebugInfoTest stage. 806 debuginfo_test=False, 807 808 # Runs the tests that the signer would run. This should only be set if 809 # 'recovery' is in images. 810 signer_tests=False, 811 812 # Runs unittests for packages. 813 unittests=True, 814 815 # A list of the packages to blacklist from unittests. 816 unittest_blacklist=[], 817 818 # Generates AFDO data. Will capture a profile of chrome using a hwtest 819 # to run a predetermined set of benchmarks. 820 # FIXME(tcwang): Keep this config during transition to async AFDO 821 afdo_generate=False, 822 823 # Generates AFDO data asynchronously. Will capture a profile of chrome 824 # using a hwtest to run a predetermined set of benchmarks. 825 afdo_generate_async=False, 826 827 # Verify and publish kernel profiles. 828 kernel_afdo_verify=False, 829 830 # Verify and publish chrome profiles. 831 chrome_afdo_verify=False, 832 833 # Generate Chrome orderfile. Will build Chrome with C3 ordering and 834 # generate an orderfile for uploading as a result. 835 orderfile_generate=False, 836 837 # Verify unvetted Chrome orderfile. Will use the most recent unvetted 838 # orderfile and build Chrome. Upload the orderfile to vetted bucket 839 # as a result. 840 orderfile_verify=False, 841 842 # Generates AFDO data, builds the minimum amount of artifacts and 843 # assumes a non-distributed builder (i.e.: the whole process in a single 844 # builder). 845 afdo_generate_min=False, 846 847 # Update the Chrome ebuild with the AFDO profile info. 848 afdo_update_chrome_ebuild=False, 849 850 # Update the kernel ebuild with the AFDO profile info. 851 afdo_update_kernel_ebuild=False, 852 853 # Uses AFDO data. The Chrome build will be optimized using the AFDO 854 # profile information found in Chrome's source tree. 855 afdo_use=True, 856 857 # A list of VMTestConfig objects to run by default. 858 vm_tests=[ 859 VMTestConfig(constants.VM_SUITE_TEST_TYPE, 860 test_suite='smoke'), 861 VMTestConfig(constants.SIMPLE_AU_TEST_TYPE) 862 ], 863 864 # A list of all VMTestConfig objects to use if VM Tests are forced on 865 # (--vmtest command line or trybot). None means no override. 866 vm_tests_override=None, 867 868 # If true, in addition to upload vm test result to artifact folder, report 869 # results to other dashboard as well. 870 vm_test_report_to_dashboards=False, 871 872 # The number of times to run the VMTest stage. If this is >1, then we 873 # will run the stage this many times, stopping if we encounter any 874 # failures. 875 vm_test_runs=1, 876 877 # If True, run SkylabHWTestStage instead of HWTestStage for suites that 878 # use pools other than pool:cts. 879 enable_skylab_hw_tests=False, 880 881 # If set, this is the URL of the bug justifying why hw_tests are disabled 882 # on a builder that should always have hw_tests. 883 hw_tests_disabled_bug='', 884 885 # If True, run SkylabHWTestStage instead of HWTestStage for suites that 886 # use pool:cts. 887 enable_skylab_cts_hw_tests=False, 888 889 # A list of HWTestConfig objects to run. 890 hw_tests=[], 891 892 # A list of all HWTestConfig objects to use if HW Tests are forced on 893 # (--hwtest command line or trybot). None means no override. 894 hw_tests_override=None, 895 896 # If true, uploads artifacts for hw testing. Upload payloads for test 897 # image if the image is built. If not, dev image is used and then base 898 # image. 899 upload_hw_test_artifacts=True, 900 901 # If true, uploads individual image tarballs. 902 upload_standalone_images=True, 903 904 # A list of GCETestConfig objects to use. Currently only some lakitu 905 # builders run gce tests. 906 gce_tests=[], 907 908 # Whether to run CPEExport stage. This stage generates portage depgraph 909 # data that is used for bugs reporting (see go/why-cpeexport). Only 910 # release builders should run this stage. 911 run_cpeexport=False, 912 913 # Whether to run BuildConfigsExport stage. This stage generates build 914 # configs (see crbug.com/974795 project). Only release builders should 915 # run this stage. 916 run_build_configs_export=False, 917 918 # A list of TastVMTestConfig objects describing Tast-based test suites 919 # that should be run in a VM. 920 tast_vm_tests=[], 921 922 # Default to not run moblab tests. Currently the blessed moblab board runs 923 # these tests. 924 moblab_vm_tests=[], 925 926 # List of patterns for portage packages for which stripped binpackages 927 # should be uploaded to GS. The patterns are used to search for packages 928 # via `equery list`. 929 upload_stripped_packages=[ 930 # Used by SimpleChrome workflow. 931 'chromeos-base/chromeos-chrome', 932 'sys-kernel/*kernel*', 933 ], 934 935 # Google Storage path to offload files to. 936 # None - No upload 937 # GS_PATH_DEFAULT - 'gs://chromeos-image-archive/' + bot_id 938 # value - Upload to explicit path 939 gs_path=GS_PATH_DEFAULT, 940 941 # TODO(sosa): Deprecate binary. 942 # Type of builder. Check constants.VALID_BUILD_TYPES. 943 build_type=constants.PFQ_TYPE, 944 945 # Whether to schedule test suites by suite_scheduler. Generally only 946 # True for "release" builders. 947 suite_scheduling=False, 948 949 # The class name used to build this config. See the modules in 950 # cbuildbot / builders/*_builders.py for possible values. This should 951 # be the name in string form -- e.g. "simple_builders.SimpleBuilder" to 952 # get the SimpleBuilder class in the simple_builders module. If not 953 # specified, we'll fallback to legacy probing behavior until everyone 954 # has been converted (see the scripts/cbuildbot.py file for details). 955 builder_class_name=None, 956 957 # List of images we want to build -- see build_image for more details. 958 images=['test'], 959 960 # Image from which we will build update payloads. Must either be None 961 # or name one of the images in the 'images' list, above. 962 payload_image=None, 963 964 # Whether to build a netboot image. 965 factory_install_netboot=True, 966 967 # Whether to build the factory toolkit. 968 factory_toolkit=True, 969 970 # Whether to build factory packages in BuildPackages. 971 factory=True, 972 973 # Flag to control if all packages for the target are built. If disabled 974 # and unittests are enabled, the unit tests and their dependencies 975 # will still be built during the testing stage. 976 build_packages=True, 977 978 # Tuple of specific packages we want to build. Most configs won't 979 # specify anything here and instead let build_packages calculate. 980 packages=[], 981 982 # Do we push a final release image to chromeos-images. 983 push_image=False, 984 985 # Do we upload debug symbols. 986 upload_symbols=False, 987 988 # Whether we upload a hwqual tarball. 989 hwqual=False, 990 991 # Run a stage that generates release payloads for signed images. 992 paygen=False, 993 994 # If the paygen stage runs, generate tests, and schedule auto-tests for 995 # them. 996 paygen_skip_testing=False, 997 998 # If the paygen stage runs, don't generate any delta payloads. This is 999 # only done if deltas are broken for a given board. 1000 paygen_skip_delta_payloads=False, 1001 1002 # Run a stage that generates and uploads package CPE information. 1003 cpe_export=True, 1004 1005 # Run a stage that generates and uploads debug symbols. 1006 debug_symbols=True, 1007 1008 # Do not package the debug symbols in the binary package. The debug 1009 # symbols will be in an archive with the name cpv.debug.tbz2 in 1010 # /build/${BOARD}/packages and uploaded with the prebuilt. 1011 separate_debug_symbols=True, 1012 1013 # Include *.debug files for debugging core files with gdb in debug.tgz. 1014 # These are very large. This option only has an effect if debug_symbols 1015 # and archive are set. 1016 archive_build_debug=False, 1017 1018 # Run a stage that archives build and test artifacts for developer 1019 # consumption. 1020 archive=True, 1021 1022 # Git repository URL for our manifests. 1023 # https://chromium.googlesource.com/chromiumos/manifest 1024 # https://chrome-internal.googlesource.com/chromeos/manifest-internal 1025 manifest_repo_url=None, 1026 1027 # Whether we are using the manifest_version repo that stores per-build 1028 # manifests. 1029 manifest_version=False, 1030 1031 # Use a different branch of the project manifest for the build. 1032 manifest_branch=None, 1033 1034 # LKGM for ChromeOS generated for Chrome builds that are blessed from 1035 # canary runs. 1036 use_chrome_lkgm=False, 1037 1038 # Upload prebuilts for this build. Valid values are PUBLIC, PRIVATE, or 1039 # False. 1040 prebuilts=False, 1041 1042 # Use SDK as opposed to building the chroot from source. 1043 use_sdk=True, 1044 1045 # The description string to print out for config when user runs --list. 1046 description=None, 1047 1048 # Boolean that enables parameter --git-sync for upload_prebuilts. 1049 git_sync=False, 1050 1051 # A list of the child config groups, if applicable. See the AddGroup 1052 # method. 1053 child_configs=[], 1054 1055 # Whether this config belongs to a config group. 1056 grouped=False, 1057 1058 # layout of build_image resulting image. See 1059 # scripts/build_library/legacy_disk_layout.json or 1060 # overlay-<board>/scripts/disk_layout.json for possible values. 1061 disk_layout=None, 1062 1063 # If enabled, run the PatchChanges stage. Enabled by default. Can be 1064 # overridden by the --nopatch flag. 1065 postsync_patch=True, 1066 1067 # Reexec into the buildroot after syncing. Enabled by default. 1068 postsync_reexec=True, 1069 1070 # Run the binhost_test stage. Only makes sense for builders that have no 1071 # boards. 1072 binhost_test=False, 1073 1074 # If specified, it is passed on to the PushImage script as '--sign-types' 1075 # commandline argument. Must be either None or a list of image types. 1076 sign_types=None, 1077 1078 # TODO(sosa): Collapse to one option. 1079 # ========== Dev installer prebuilts options ======================= 1080 1081 # Upload prebuilts for this build to this bucket. If it equals None the 1082 # default buckets are used. 1083 binhost_bucket=None, 1084 1085 # Parameter --key for upload_prebuilts. If it equals None, the default 1086 # values are used, which depend on the build type. 1087 binhost_key=None, 1088 1089 # Parameter --binhost-base-url for upload_prebuilts. If it equals None, 1090 # the default value is used. 1091 binhost_base_url=None, 1092 1093 # Upload dev installer prebuilts. 1094 dev_installer_prebuilts=False, 1095 1096 # Enable rootfs verification on the image. 1097 rootfs_verification=True, 1098 1099 # Build the Chrome SDK. 1100 chrome_sdk=False, 1101 1102 # If chrome_sdk is set to True, this determines whether we attempt to 1103 # build Chrome itself with the generated SDK. 1104 chrome_sdk_build_chrome=True, 1105 1106 # If chrome_sdk is set to True, this determines whether we use goma to 1107 # build chrome. 1108 chrome_sdk_goma=True, 1109 1110 # Run image tests. This should only be set if 'base' is in our list of 1111 # images. 1112 image_test=False, 1113 1114 # ================================================================== 1115 # Workspace related options. 1116 1117 # Which branch should WorkspaceSyncStage checkout, if run. 1118 workspace_branch=None, 1119 1120 # ================================================================== 1121 # The documentation associated with the config. 1122 doc=None, 1123 1124 # ================================================================== 1125 # The goma related options. 1126 1127 # Which goma client to use. 1128 goma_client_type=None, 1129 1130 # Try to use goma to build all packages. 1131 build_all_with_goma=False, 1132 1133 # This is a LUCI Scheduler schedule string. Setting this will create 1134 # a LUCI Scheduler for this build on swarming (not buildbot). 1135 # See: https://goo.gl/VxSzFf 1136 schedule=None, 1137 1138 # This is the list of git repos which can trigger this build in swarming. 1139 # Implies that schedule is set, to "triggered". 1140 # The format is of the form: 1141 # [ (<git repo url>, (<ref1>, <ref2>, …)), 1142 # …] 1143 triggered_gitiles=None, 1144 1145 # If true, skip package retries in BuildPackages step. 1146 nobuildretry=False, 1147 1148 # Attempt to run this build on the same bot each time it builds. 1149 # This is only meaningful for slave builds run on swarming. This 1150 # should only be used with LUCI Builders that use a reserved 1151 # role to avoid having bots stolen by other builds while 1152 # waiting on a new master build. 1153 build_affinity=False, 1154 ) 1155 1156 1157def GerritInstanceParameters(name, instance): 1158 param_names = [ 1159 '_GOB_INSTANCE', '_GERRIT_INSTANCE', '_GOB_HOST', '_GERRIT_HOST', 1160 '_GOB_URL', '_GERRIT_URL' 1161 ] 1162 1163 gob_instance = instance 1164 gerrit_instance = '%s-review' % instance 1165 gob_host = constants.GOB_HOST % gob_instance 1166 gerrit_host = constants.GOB_HOST % gerrit_instance 1167 gob_url = 'https://%s' % gob_host 1168 gerrit_url = 'https://%s' % gerrit_host 1169 1170 params = [ 1171 gob_instance, gerrit_instance, gob_host, gerrit_host, gob_url, 1172 gerrit_url 1173 ] 1174 1175 return dict([('%s%s' % (name, pn), p) 1176 for pn, p in zip(param_names, params)]) 1177 1178 1179def DefaultSiteParameters(): 1180 # Enumeration of valid site parameters; any/all site parameters must be here. 1181 # All site parameters should be documented. 1182 default_site_params = {} 1183 1184 manifest_project = 'chromiumos/manifest' 1185 manifest_int_project = 'chromeos/manifest-internal' 1186 external_remote = 'cros' 1187 internal_remote = 'cros-internal' 1188 chromium_remote = 'chromium' 1189 chrome_remote = 'chrome' 1190 aosp_remote = 'aosp' 1191 weave_remote = 'weave' 1192 1193 internal_change_prefix = 'chrome-internal:' 1194 external_change_prefix = 'chromium:' 1195 1196 # Gerrit instance site parameters. 1197 default_site_params.update(GerritInstanceParameters( 1198 'EXTERNAL', 'chromium')) 1199 default_site_params.update( 1200 GerritInstanceParameters('INTERNAL', 'chrome-internal')) 1201 default_site_params.update(GerritInstanceParameters('AOSP', 'android')) 1202 default_site_params.update(GerritInstanceParameters('WEAVE', 'weave')) 1203 1204 default_site_params.update( 1205 # Parameters to define which manifests to use. 1206 MANIFEST_PROJECT=manifest_project, 1207 MANIFEST_INT_PROJECT=manifest_int_project, 1208 MANIFEST_PROJECTS=(manifest_project, manifest_int_project), 1209 MANIFEST_URL=os.path.join(default_site_params['EXTERNAL_GOB_URL'], 1210 manifest_project), 1211 MANIFEST_INT_URL=os.path.join( 1212 default_site_params['INTERNAL_GERRIT_URL'], 1213 manifest_int_project), 1214 1215 # CrOS remotes specified in the manifests. 1216 EXTERNAL_REMOTE=external_remote, 1217 INTERNAL_REMOTE=internal_remote, 1218 GOB_REMOTES={ 1219 default_site_params['EXTERNAL_GOB_INSTANCE']: 1220 external_remote, 1221 default_site_params['INTERNAL_GOB_INSTANCE']: 1222 internal_remote, 1223 }, 1224 CHROMIUM_REMOTE=chromium_remote, 1225 CHROME_REMOTE=chrome_remote, 1226 AOSP_REMOTE=aosp_remote, 1227 WEAVE_REMOTE=weave_remote, 1228 1229 # Only remotes listed in CROS_REMOTES are considered branchable. 1230 # CROS_REMOTES and BRANCHABLE_PROJECTS must be kept in sync. 1231 GERRIT_HOSTS={ 1232 external_remote: 1233 default_site_params['EXTERNAL_GERRIT_HOST'], 1234 internal_remote: 1235 default_site_params['INTERNAL_GERRIT_HOST'], 1236 aosp_remote: default_site_params['AOSP_GERRIT_HOST'], 1237 weave_remote: default_site_params['WEAVE_GERRIT_HOST'], 1238 }, 1239 CROS_REMOTES={ 1240 external_remote: default_site_params['EXTERNAL_GOB_URL'], 1241 internal_remote: default_site_params['INTERNAL_GOB_URL'], 1242 aosp_remote: default_site_params['AOSP_GOB_URL'], 1243 weave_remote: default_site_params['WEAVE_GOB_URL'], 1244 }, 1245 GIT_REMOTES={ 1246 chromium_remote: default_site_params['EXTERNAL_GOB_URL'], 1247 chrome_remote: default_site_params['INTERNAL_GOB_URL'], 1248 external_remote: default_site_params['EXTERNAL_GOB_URL'], 1249 internal_remote: default_site_params['INTERNAL_GOB_URL'], 1250 aosp_remote: default_site_params['AOSP_GOB_URL'], 1251 weave_remote: default_site_params['WEAVE_GOB_URL'], 1252 }, 1253 1254 # Prefix to distinguish internal and external changes. This is used 1255 # when a user specifies a patch with "-g", when generating a key for 1256 # a patch to use in our PatchCache, and when displaying a custom 1257 # string for the patch. 1258 INTERNAL_CHANGE_PREFIX=internal_change_prefix, 1259 EXTERNAL_CHANGE_PREFIX=external_change_prefix, 1260 CHANGE_PREFIX={ 1261 external_remote: external_change_prefix, 1262 internal_remote: internal_change_prefix, 1263 }, 1264 1265 # List of remotes that are okay to include in the external manifest. 1266 EXTERNAL_REMOTES=( 1267 external_remote, 1268 chromium_remote, 1269 aosp_remote, 1270 weave_remote, 1271 ), 1272 1273 # Mapping 'remote name' -> regexp that matches names of repositories on 1274 # that remote that can be branched when creating CrOS branch. 1275 # Branching script will actually create a new git ref when branching 1276 # these projects. It won't attempt to create a git ref for other projects 1277 # that may be mentioned in a manifest. If a remote is missing from this 1278 # dictionary, all projects on that remote are considered to not be 1279 # branchable. 1280 BRANCHABLE_PROJECTS={ 1281 external_remote: r'(chromiumos|aosp)/(.+)', 1282 internal_remote: r'chromeos/(.+)', 1283 }, 1284 1285 # Additional parameters used to filter manifests, create modified 1286 # manifests, and to branch manifests. 1287 MANIFEST_VERSIONS_GOB_URL=( 1288 '%s/chromiumos/manifest-versions' % 1289 default_site_params['EXTERNAL_GOB_URL']), 1290 MANIFEST_VERSIONS_GOB_URL_TEST=( 1291 '%s/chromiumos/manifest-versions-test' % 1292 default_site_params['EXTERNAL_GOB_URL']), 1293 MANIFEST_VERSIONS_INT_GOB_URL=( 1294 '%s/chromeos/manifest-versions' % 1295 default_site_params['INTERNAL_GOB_URL']), 1296 MANIFEST_VERSIONS_INT_GOB_URL_TEST=( 1297 '%s/chromeos/manifest-versions-test' % 1298 default_site_params['INTERNAL_GOB_URL']), 1299 MANIFEST_VERSIONS_GS_URL='gs://chromeos-manifest-versions', 1300 1301 # Standard directories under buildroot for cloning these repos. 1302 EXTERNAL_MANIFEST_VERSIONS_PATH='manifest-versions', 1303 INTERNAL_MANIFEST_VERSIONS_PATH='manifest-versions-internal', 1304 1305 # GS URL in which to archive build artifacts. 1306 ARCHIVE_URL='gs://chromeos-image-archive', 1307 ) 1308 1309 return default_site_params 1310 1311 1312class SiteConfig(dict): 1313 """This holds a set of named BuildConfig values.""" 1314 1315 def __init__(self, defaults=None, templates=None): 1316 """Init. 1317 1318 Args: 1319 defaults: Dictionary of key value pairs to use as BuildConfig values. 1320 All BuildConfig values should be defined here. If None, 1321 the DefaultSettings() is used. Most sites should use 1322 DefaultSettings(), and then update to add any site specific 1323 values needed. 1324 templates: Dictionary of template names to partial BuildConfigs 1325 other BuildConfigs can be based on. Mostly used to reduce 1326 verbosity of the config dump file format. 1327 """ 1328 super(SiteConfig, self).__init__() 1329 self._defaults = DefaultSettings() 1330 if defaults: 1331 self._defaults.update(defaults) 1332 self._templates = AttrDict() if templates is None else AttrDict( 1333 templates) 1334 1335 def GetDefault(self): 1336 """Create the canonical default build configuration.""" 1337 # Enumeration of valid settings; any/all config settings must be in this. 1338 # All settings must be documented. 1339 return BuildConfig(**self._defaults) 1340 1341 def GetTemplates(self): 1342 """Get the templates of the build configs""" 1343 return self._templates 1344 1345 @property 1346 def templates(self): 1347 return self._templates 1348 1349 # 1350 # Methods for searching a SiteConfig's contents. 1351 # 1352 def GetBoards(self): 1353 """Return an iterable of all boards in the SiteConfig.""" 1354 return set( 1355 itertools.chain.from_iterable(x.boards for x in self.values() 1356 if x.boards)) 1357 1358 def FindFullConfigsForBoard(self, board=None): 1359 """Returns full builder configs for a board. 1360 1361 Args: 1362 board: The board to match. By default, match all boards. 1363 1364 Returns: 1365 A tuple containing a list of matching external configs and a list of 1366 matching internal release configs for a board. 1367 """ 1368 ext_cfgs = [] 1369 int_cfgs = [] 1370 1371 for name, c in self.items(): 1372 if c['boards'] and (board is None or board in c['boards']): 1373 if name.endswith( 1374 '-%s' % CONFIG_TYPE_RELEASE) and c['internal']: 1375 int_cfgs.append(c.deepcopy()) 1376 elif name.endswith( 1377 '-%s' % CONFIG_TYPE_FULL) and not c['internal']: 1378 ext_cfgs.append(c.deepcopy()) 1379 1380 return ext_cfgs, int_cfgs 1381 1382 def FindCanonicalConfigForBoard(self, board, allow_internal=True): 1383 """Get the canonical cbuildbot builder config for a board.""" 1384 ext_cfgs, int_cfgs = self.FindFullConfigsForBoard(board) 1385 # If both external and internal builds exist for this board, prefer the 1386 # internal one unless instructed otherwise. 1387 both = (int_cfgs if allow_internal else []) + ext_cfgs 1388 1389 if not both: 1390 raise ValueError('Invalid board specified: %s.' % board) 1391 return both[0] 1392 1393 def GetSlaveConfigMapForMaster(self, 1394 master_config, 1395 options=None, 1396 important_only=True): 1397 """Gets the slave builds triggered by a master config. 1398 1399 If a master builder also performs a build, it can (incorrectly) return 1400 itself. 1401 1402 Args: 1403 master_config: A build config for a master builder. 1404 options: The options passed on the commandline. This argument is required 1405 for normal operation, but we accept None to assist with testing. 1406 important_only: If True, only get the important slaves. 1407 1408 Returns: 1409 A slave_name to slave_config map, corresponding to the slaves for the 1410 master represented by master_config. 1411 1412 Raises: 1413 AssertionError if the given config is not a master config or it does 1414 not have a manifest_version. 1415 """ 1416 assert master_config.master 1417 assert master_config.slave_configs is not None 1418 1419 slave_name_config_map = {} 1420 if options is not None and options.remote_trybot: 1421 return {} 1422 1423 # Look up the build configs for all slaves named by the master. 1424 slave_name_config_map = { 1425 name: self[name] 1426 for name in master_config.slave_configs 1427 } 1428 1429 if important_only: 1430 # Remove unimportant configs from the result. 1431 slave_name_config_map = { 1432 k: v 1433 for k, v in slave_name_config_map.items() if v.important 1434 } 1435 1436 return slave_name_config_map 1437 1438 def GetSlavesForMaster(self, 1439 master_config, 1440 options=None, 1441 important_only=True): 1442 """Get a list of qualified build slave configs given the master_config. 1443 1444 Args: 1445 master_config: A build config for a master builder. 1446 options: The options passed on the commandline. This argument is optional, 1447 and only makes sense when called from cbuildbot. 1448 important_only: If True, only get the important slaves. 1449 """ 1450 slave_map = self.GetSlaveConfigMapForMaster( 1451 master_config, options=options, important_only=important_only) 1452 return list(slave_map.values()) 1453 1454 # 1455 # Methods used when creating a Config programmatically. 1456 # 1457 def Add(self, name, template=None, *args, **kwargs): 1458 """Add a new BuildConfig to the SiteConfig. 1459 1460 Examples: 1461 # Creates default build named foo. 1462 site_config.Add('foo') 1463 1464 # Creates default build with board 'foo_board' 1465 site_config.Add('foo', 1466 boards=['foo_board']) 1467 1468 # Creates build based on template_build for 'foo_board'. 1469 site_config.Add('foo', 1470 template_build, 1471 boards=['foo_board']) 1472 1473 # Creates build based on template for 'foo_board'. with mixin. 1474 # Inheritance order is default, template, mixin, arguments. 1475 site_config.Add('foo', 1476 template_build, 1477 mixin_build_config, 1478 boards=['foo_board']) 1479 1480 # Creates build without a template but with mixin. 1481 # Inheritance order is default, template, mixin, arguments. 1482 site_config.Add('foo', 1483 None, 1484 mixin_build_config, 1485 boards=['foo_board']) 1486 1487 Args: 1488 name: The name to label this configuration; this is what cbuildbot 1489 would see. 1490 template: BuildConfig to use as a template for this build. 1491 args: BuildConfigs to patch into this config. First one (if present) is 1492 considered the template. See AddTemplate for help on templates. 1493 kwargs: BuildConfig values to explicitly set on this config. 1494 1495 Returns: 1496 The BuildConfig just added to the SiteConfig. 1497 """ 1498 assert name not in self, ('%s already exists.' % name) 1499 1500 inherits, overrides = args, kwargs 1501 if template: 1502 inherits = (template, ) + inherits 1503 1504 # Make sure we don't ignore that argument silently. 1505 if '_template' in overrides: 1506 raise ValueError('_template cannot be explicitly set.') 1507 1508 result = self.GetDefault() 1509 result.apply(*inherits, **overrides) 1510 1511 # Select the template name based on template argument, or nothing. 1512 resolved_template = template.get('_template') if template else None 1513 assert not resolved_template or resolved_template in self.templates, \ 1514 '%s inherits from non-template %s' % (name, resolved_template) 1515 1516 # Our name is passed as an explicit argument. We use the first build 1517 # config as our template, or nothing. 1518 result['name'] = name 1519 result['_template'] = resolved_template 1520 self[name] = result 1521 return result 1522 1523 def AddWithoutTemplate(self, name, *args, **kwargs): 1524 """Add a config containing only explicitly listed values (no defaults).""" 1525 self.Add(name, None, *args, **kwargs) 1526 1527 def AddGroup(self, name, *args, **kwargs): 1528 """Create a new group of build configurations. 1529 1530 Args: 1531 name: The name to label this configuration; this is what cbuildbot 1532 would see. 1533 args: Configurations to build in this group. The first config in 1534 the group is considered the primary configuration and is used 1535 for syncing and creating the chroot. 1536 kwargs: Override values to use for the parent config. 1537 1538 Returns: 1539 A new BuildConfig instance. 1540 """ 1541 child_configs = [x.deepcopy().apply(grouped=True) for x in args] 1542 return self.Add(name, args[0], child_configs=child_configs, **kwargs) 1543 1544 def AddForBoards(self, 1545 suffix, 1546 boards, 1547 per_board=None, 1548 template=None, 1549 *args, 1550 **kwargs): 1551 """Create configs for all boards in |boards|. 1552 1553 Args: 1554 suffix: Config name is <board>-<suffix>. 1555 boards: A list of board names as strings. 1556 per_board: A dictionary of board names to BuildConfigs, or None. 1557 template: The template to use for all configs created. 1558 *args: Mixin templates to apply. 1559 **kwargs: Additional keyword arguments to be used in AddConfig. 1560 1561 Returns: 1562 List of the configs created. 1563 """ 1564 result = [] 1565 1566 for board in boards: 1567 config_name = '%s-%s' % (board, suffix) 1568 1569 # Insert the per_board value as the last mixin, if it exists. 1570 mixins = args + (dict(boards=[board]), ) 1571 if per_board and board in per_board: 1572 mixins = mixins + (per_board[board], ) 1573 1574 # Create the new config for this board. 1575 result.append(self.Add(config_name, template, *mixins, **kwargs)) 1576 1577 return result 1578 1579 def ApplyForBoards(self, suffix, boards, *args, **kwargs): 1580 """Update configs for all boards in |boards|. 1581 1582 Args: 1583 suffix: Config name is <board>-<suffix>. 1584 boards: A list of board names as strings. 1585 *args: Mixin templates to apply. 1586 **kwargs: Additional keyword arguments to be used in AddConfig. 1587 1588 Returns: 1589 List of the configs updated. 1590 """ 1591 result = [] 1592 1593 for board in boards: 1594 config_name = '%s-%s' % (board, suffix) 1595 assert config_name in self, ('%s does not exist.' % config_name) 1596 1597 # Update the config for this board. 1598 result.append(self[config_name].apply(*args, **kwargs)) 1599 1600 return result 1601 1602 def AddTemplate(self, name, *args, **kwargs): 1603 """Create a template named |name|. 1604 1605 Templates are used to define common settings that are shared across types 1606 of builders. They help reduce duplication in config_dump.json, because we 1607 only define the template and its settings once. 1608 1609 Args: 1610 name: The name of the template. 1611 args: See the docstring of BuildConfig.derive. 1612 kwargs: See the docstring of BuildConfig.derive. 1613 """ 1614 assert name not in self._templates, ('Template %s already exists.' % 1615 name) 1616 1617 template = BuildConfig() 1618 template.apply(*args, **kwargs) 1619 template['_template'] = name 1620 self._templates[name] = template 1621 1622 return template 1623 1624 def _MarshalBuildConfig(self, name, config): 1625 """Hide the defaults from a given config entry. 1626 1627 Args: 1628 name: Default build name (usually dictionary key). 1629 config: A config entry. 1630 1631 Returns: 1632 The same config entry, but without any defaults. 1633 """ 1634 defaults = self.GetDefault() 1635 defaults['name'] = name 1636 1637 template = config.get('_template') 1638 if template: 1639 defaults.apply(self._templates[template]) 1640 defaults['_template'] = None 1641 1642 result = {} 1643 for k, v in config.items(): 1644 if defaults.get(k) != v: 1645 if k == 'child_configs': 1646 result['child_configs'] = [ 1647 self._MarshalBuildConfig(name, child) 1648 for child in v 1649 ] 1650 else: 1651 result[k] = v 1652 1653 return result 1654 1655 def _MarshalTemplates(self): 1656 """Return a version of self._templates with only used templates. 1657 1658 Templates have callables/delete keys resolved against GetDefault() to 1659 ensure they can be safely saved to json. 1660 1661 Returns: 1662 Dict copy of self._templates with all unreferenced templates removed. 1663 """ 1664 defaults = self.GetDefault() 1665 1666 # All templates used. We ignore child configs since they 1667 # should exist at top level. 1668 used = set(c.get('_template', None) for c in self.values()) 1669 used.discard(None) 1670 1671 result = {} 1672 1673 for name in used: 1674 # Expand any special values (callables, etc) 1675 expanded = defaults.derive(self._templates[name]) 1676 # Recover the '_template' value which is filtered out by derive. 1677 expanded['_template'] = name 1678 # Hide anything that matches the default. 1679 save = {k: v for k, v in expanded.items() if defaults.get(k) != v} 1680 result[name] = save 1681 1682 return result 1683 1684 def SaveConfigToString(self): 1685 """Save this Config object to a Json format string.""" 1686 default = self.GetDefault() 1687 1688 config_dict = {} 1689 config_dict['_default'] = default 1690 config_dict['_templates'] = self._MarshalTemplates() 1691 for k, v in self.items(): 1692 config_dict[k] = self._MarshalBuildConfig(k, v) 1693 1694 return PrettyJsonDict(config_dict) 1695 1696 def SaveConfigToFile(self, config_file): 1697 """Save this Config to a Json file. 1698 1699 Args: 1700 config_file: The file to write too. 1701 """ 1702 json_string = self.SaveConfigToString() 1703 osutils.WriteFile(config_file, json_string) 1704 1705 def DumpExpandedConfigToString(self): 1706 """Dump the SiteConfig to Json with all configs full expanded. 1707 1708 This is intended for debugging default/template behavior. The dumped JSON 1709 can't be reloaded (at least not reliably). 1710 """ 1711 return PrettyJsonDict(self) 1712 1713 def DumpConfigCsv(self): 1714 """Dump the SiteConfig to CSV with all configs fully expanded. 1715 1716 This supports configuration analysis and debugging. 1717 """ 1718 raw_config = json.loads(self.DumpExpandedConfigToString()) 1719 header_keys = {'builder_name', 'test_type', 'device'} 1720 csv_rows = [] 1721 for builder_name, values in raw_config.items(): 1722 row = {'builder_name': builder_name} 1723 tests = {} 1724 raw_devices = [] 1725 for key, value in values.items(): 1726 header_keys.add(key) 1727 if value: 1728 if isinstance(value, list): 1729 if '_tests' in key: 1730 tests[key] = value 1731 elif key == 'models': 1732 raw_devices = value 1733 else: 1734 # Ignoring this for now for test analysis. 1735 if key != 'child_configs': 1736 row[key] = ' | '.join( 1737 str(array_val) for array_val in value) 1738 else: 1739 row[key] = value 1740 1741 if tests: 1742 for test_type, test_entries in tests.items(): 1743 for test_entry in test_entries: 1744 test_row = copy.deepcopy(row) 1745 test_row['test_type'] = test_type 1746 raw_test = json.loads(test_entry) 1747 for test_key, test_value in raw_test.items(): 1748 if test_value: 1749 header_keys.add(test_key) 1750 test_row[test_key] = test_value 1751 csv_rows.append(test_row) 1752 if raw_devices: 1753 for raw_device in raw_devices: 1754 device = json.loads(raw_device) 1755 test_suite = test_row.get('suite', '') 1756 test_suites = device.get('test_suites', []) 1757 if test_suite and test_suites and test_suite in test_suites: 1758 device_row = copy.deepcopy(test_row) 1759 device_row['device'] = device['name'] 1760 csv_rows.append(device_row) 1761 else: 1762 csv_rows.append(row) 1763 1764 csv_result = [','.join(header_keys)] 1765 for csv_row in csv_rows: 1766 row_values = [] 1767 for header_key in header_keys: 1768 row_values.append('"%s"' % str(csv_row.get(header_key, ''))) 1769 csv_result.append(','.join(row_values)) 1770 1771 return '\n'.join(csv_result) 1772 1773 1774# 1775# Functions related to working with GE Data. 1776# 1777 1778 1779def LoadGEBuildConfigFromFile( 1780 build_settings_file=constants.GE_BUILD_CONFIG_FILE): 1781 """Load template config dict from a Json encoded file.""" 1782 json_string = osutils.ReadFile(build_settings_file) 1783 return json.loads(json_string) 1784 1785 1786def GeBuildConfigAllBoards(ge_build_config): 1787 """Extract a list of board names from the GE Build Config. 1788 1789 Args: 1790 ge_build_config: Dictionary containing the decoded GE configuration file. 1791 1792 Returns: 1793 A list of board names as strings. 1794 """ 1795 return [b['name'] for b in ge_build_config['boards']] 1796 1797 1798def GetUnifiedBuildConfigAllBuilds(ge_build_config): 1799 """Extract a list of all unified build configurations. 1800 1801 This dictionary is based on the JSON defined by the proto generated from 1802 GoldenEye. See cs/crosbuilds.proto 1803 1804 Args: 1805 ge_build_config: Dictionary containing the decoded GE configuration file. 1806 1807 Returns: 1808 A list of unified build configurations (json configs) 1809 """ 1810 return ge_build_config.get('reference_board_unified_builds', []) 1811 1812 1813class BoardGroup(object): 1814 """Class holds leader_boards and follower_boards for grouped boards""" 1815 1816 def __init__(self): 1817 self.leader_boards = [] 1818 self.follower_boards = [] 1819 1820 def AddLeaderBoard(self, board): 1821 self.leader_boards.append(board) 1822 1823 def AddFollowerBoard(self, board): 1824 self.follower_boards.append(board) 1825 1826 def __str__(self): 1827 return ('Leader_boards: %s Follower_boards: %s' % 1828 (self.leader_boards, self.follower_boards)) 1829 1830 1831def GroupBoardsByBuilderAndBoardGroup(board_list): 1832 """Group boards by builder and board_group. 1833 1834 Args: 1835 board_list: board list from the template file. 1836 1837 Returns: 1838 builder_group_dict: maps builder to {group_n: board_group_n} 1839 builder_ungrouped_dict: maps builder to a list of ungrouped boards 1840 """ 1841 builder_group_dict = {} 1842 builder_ungrouped_dict = {} 1843 1844 for b in board_list: 1845 name = b[CONFIG_TEMPLATE_NAME] 1846 # Invalid build configs being written out with no config templates, 1847 # thus the default. See https://crbug.com/1012278. 1848 for config in b.get(CONFIG_TEMPLATE_CONFIGS, []): 1849 board = {'name': name} 1850 board.update(config) 1851 1852 builder = config[CONFIG_TEMPLATE_BUILDER] 1853 if builder not in builder_group_dict: 1854 builder_group_dict[builder] = {} 1855 if builder not in builder_ungrouped_dict: 1856 builder_ungrouped_dict[builder] = [] 1857 1858 board_group = config[CONFIG_TEMPLATE_BOARD_GROUP] 1859 if not board_group: 1860 builder_ungrouped_dict[builder].append(board) 1861 continue 1862 if board_group not in builder_group_dict[builder]: 1863 builder_group_dict[builder][board_group] = BoardGroup() 1864 if config[CONFIG_TEMPLATE_LEADER_BOARD]: 1865 builder_group_dict[builder][board_group].AddLeaderBoard(board) 1866 else: 1867 builder_group_dict[builder][board_group].AddFollowerBoard( 1868 board) 1869 1870 return (builder_group_dict, builder_ungrouped_dict) 1871 1872 1873def GroupBoardsByBuilder(board_list): 1874 """Group boards by the 'builder' flag.""" 1875 builder_to_boards_dict = {} 1876 1877 for b in board_list: 1878 # Invalid build configs being written out with no configs array, thus the 1879 # default. See https://crbug.com/1005803. 1880 for config in b.get(CONFIG_TEMPLATE_CONFIGS, []): 1881 builder = config[CONFIG_TEMPLATE_BUILDER] 1882 if builder not in builder_to_boards_dict: 1883 builder_to_boards_dict[builder] = set() 1884 builder_to_boards_dict[builder].add(b[CONFIG_TEMPLATE_NAME]) 1885 1886 return builder_to_boards_dict 1887 1888 1889def GetNonUniBuildLabBoardName(board): 1890 """Return the board name labeled in the lab for non-unibuild.""" 1891 # Those special string represent special configuration used in the image, 1892 # and should run on DUT without those string. 1893 # We strip those string from the board so that lab can handle it correctly. 1894 SPECIAL_SUFFIX = [ 1895 '-arcnext$', 1896 '-arcvm$', 1897 '-arc-r$', 1898 '-arc-r-userdebug$', 1899 '-connectivitynext$', 1900 '-kernelnext$', 1901 '-kvm$', 1902 '-ndktranslation$', 1903 '-cfm$', 1904 '-campfire$', 1905 '-borealis$', 1906 ] 1907 # ARM64 userspace boards use 64 suffix but can't put that in list above 1908 # because of collisions with boards like kevin-arc64. 1909 ARM64_BOARDS = ['cheza64', 'kevin64'] 1910 for suffix in SPECIAL_SUFFIX: 1911 board = re.sub(suffix, '', board) 1912 if board in ARM64_BOARDS: 1913 # Remove '64' suffix from the board name. 1914 board = board[:-2] 1915 return board 1916 1917 1918def GetArchBoardDict(ge_build_config): 1919 """Get a dict mapping arch types to board names. 1920 1921 Args: 1922 ge_build_config: Dictionary containing the decoded GE configuration file. 1923 1924 Returns: 1925 A dict mapping arch types to board names. 1926 """ 1927 arch_board_dict = {} 1928 1929 for b in ge_build_config[CONFIG_TEMPLATE_BOARDS]: 1930 board_name = b[CONFIG_TEMPLATE_NAME] 1931 # Invalid build configs being written out with no configs array, thus the 1932 # default. See https://crbug.com/947712. 1933 for config in b.get(CONFIG_TEMPLATE_CONFIGS, []): 1934 arch = config[CONFIG_TEMPLATE_ARCH] 1935 arch_board_dict.setdefault(arch, set()).add(board_name) 1936 1937 for b in GetUnifiedBuildConfigAllBuilds(ge_build_config): 1938 board_name = b[CONFIG_TEMPLATE_REFERENCE_BOARD_NAME] 1939 arch = b[CONFIG_TEMPLATE_ARCH] 1940 arch_board_dict.setdefault(arch, set()).add(board_name) 1941 1942 return arch_board_dict 1943 1944 1945# 1946# Functions related to loading/saving Json. 1947# 1948class ObjectJSONEncoder(json.JSONEncoder): 1949 """Json Encoder that encodes objects as their dictionaries.""" 1950 1951 # pylint: disable=method-hidden 1952 def default(self, o): 1953 return self.encode(o.__dict__) 1954 1955 1956def PrettyJsonDict(dictionary): 1957 """Returns a pretty-ified json dump of a dictionary.""" 1958 return json.dumps(dictionary, 1959 cls=ObjectJSONEncoder, 1960 sort_keys=True, 1961 indent=4, 1962 separators=(',', ': ')) + '\n' 1963 1964 1965def LoadConfigFromFile(config_file=constants.CHROMEOS_CONFIG_FILE): 1966 """Load a Config a Json encoded file.""" 1967 json_string = osutils.ReadFile(config_file) 1968 return LoadConfigFromString(json_string) 1969 1970 1971def LoadConfigFromString(json_string): 1972 """Load a cbuildbot config from it's Json encoded string.""" 1973 config_dict = json.loads(json_string) 1974 1975 # Use standard defaults, but allow the config to override. 1976 defaults = DefaultSettings() 1977 defaults.update(config_dict.pop(DEFAULT_BUILD_CONFIG)) 1978 _DeserializeConfigs(defaults) 1979 1980 templates = config_dict.pop('_templates', {}) 1981 for t in templates.values(): 1982 _DeserializeConfigs(t) 1983 1984 defaultBuildConfig = BuildConfig(**defaults) 1985 1986 builds = { 1987 n: _CreateBuildConfig(n, defaultBuildConfig, v, templates) 1988 for n, v in config_dict.items() 1989 } 1990 1991 # config is the struct that holds the complete cbuildbot config. 1992 result = SiteConfig(defaults=defaults, templates=templates) 1993 result.update(builds) 1994 1995 return result 1996 1997 1998def _DeserializeConfig(build_dict, 1999 config_key, 2000 config_class, 2001 preserve_none=False): 2002 """Deserialize config of given type inside build_dict. 2003 2004 Args: 2005 build_dict: The build_dict to update (in place) 2006 config_key: Key for the config inside build_dict. 2007 config_class: The class to instantiate for the config. 2008 preserve_none: If True, None values are preserved as is. By default, they 2009 are dropped. 2010 """ 2011 serialized_configs = build_dict.pop(config_key, None) 2012 if serialized_configs is None: 2013 if preserve_none: 2014 build_dict[config_key] = None 2015 return 2016 2017 deserialized_configs = [] 2018 for config_string in serialized_configs: 2019 if isinstance(config_string, config_class): 2020 deserialized_config = config_string 2021 else: 2022 # Each test config is dumped as a json string embedded in json. 2023 embedded_configs = json.loads(config_string) 2024 deserialized_config = config_class(**embedded_configs) 2025 deserialized_configs.append(deserialized_config) 2026 build_dict[config_key] = deserialized_configs 2027 2028 2029def _DeserializeConfigs(build_dict): 2030 """Updates a config dictionary with recreated objects. 2031 2032 Notification configs and various test configs are serialized as strings 2033 (rather than JSON objects), so we need to turn them into real objects before 2034 they can be consumed. 2035 2036 Args: 2037 build_dict: The config dictionary to update (in place). 2038 """ 2039 _DeserializeConfig(build_dict, 'vm_tests', VMTestConfig) 2040 _DeserializeConfig(build_dict, 2041 'vm_tests_override', 2042 VMTestConfig, 2043 preserve_none=True) 2044 _DeserializeConfig(build_dict, 'models', ModelTestConfig) 2045 _DeserializeConfig(build_dict, 'hw_tests', HWTestConfig) 2046 _DeserializeConfig(build_dict, 2047 'hw_tests_override', 2048 HWTestConfig, 2049 preserve_none=True) 2050 _DeserializeConfig(build_dict, 'gce_tests', GCETestConfig) 2051 _DeserializeConfig(build_dict, 'tast_vm_tests', TastVMTestConfig) 2052 _DeserializeConfig(build_dict, 'moblab_vm_tests', MoblabVMTestConfig) 2053 _DeserializeConfig(build_dict, 'notification_configs', NotificationConfig) 2054 2055 2056def _CreateBuildConfig(name, default, build_dict, templates): 2057 """Create a BuildConfig object from it's parsed JSON dictionary encoding.""" 2058 # These build config values need special handling. 2059 child_configs = build_dict.pop('child_configs', None) 2060 template = build_dict.get('_template') 2061 2062 # Use the name passed in as the default build name. 2063 build_dict.setdefault('name', name) 2064 2065 result = default.deepcopy() 2066 # Use update to explicitly avoid apply's special handing. 2067 if template: 2068 result.update(templates[template]) 2069 result.update(build_dict) 2070 2071 _DeserializeConfigs(result) 2072 2073 if child_configs is not None: 2074 result['child_configs'] = [ 2075 _CreateBuildConfig(name, default, child, templates) 2076 for child in child_configs 2077 ] 2078 2079 return result 2080 2081 2082@memoize.Memoize 2083def GetConfig(): 2084 """Load the current SiteConfig. 2085 2086 Returns: 2087 SiteConfig instance to use for this build. 2088 """ 2089 return LoadConfigFromFile(constants.CHROMEOS_CONFIG_FILE) 2090 2091 2092@memoize.Memoize 2093def GetSiteParams(): 2094 """Get the site parameter configs. 2095 2096 This is the new, preferred method of accessing the site parameters, instead of 2097 SiteConfig.params. 2098 2099 Returns: 2100 AttrDict of site parameters 2101 """ 2102 site_params = AttrDict() 2103 site_params.update(DefaultSiteParameters()) 2104 return site_params 2105 2106 2107def append_useflags(useflags): 2108 """Used to append a set of useflags to existing useflags. 2109 2110 Useflags that shadow prior use flags will cause the prior flag to be removed. 2111 (e.g. appending '-foo' to 'foo' will cause 'foo' to be removed) 2112 2113 Examples: 2114 new_config = base_config.derive(useflags=append_useflags(['foo', '-bar']) 2115 2116 Args: 2117 useflags: List of string useflags to append. 2118 """ 2119 assert isinstance(useflags, (list, set)) 2120 shadowed_useflags = { 2121 '-' + flag 2122 for flag in useflags if not flag.startswith('-') 2123 } 2124 shadowed_useflags.update( 2125 {flag[1:] 2126 for flag in useflags if flag.startswith('-')}) 2127 2128 def handler(old_useflags): 2129 new_useflags = set(old_useflags or []) 2130 new_useflags.update(useflags) 2131 new_useflags.difference_update(shadowed_useflags) 2132 return sorted(list(new_useflags)) 2133 2134 return handler 2135