Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 #!/usr/bin/python | |
| 2 # Copyright 2015 Google Inc. All Rights Reserved. | |
| 3 # pylint: disable=F0401 | |
| 4 | |
| 5 """Parse, validate and query the desired master state json.""" | |
| 6 | |
| 7 import bisect | |
| 8 import json | |
| 9 import logging | |
| 10 import operator | |
| 11 import os | |
| 12 | |
| 13 from infra.libs.buildbot import master | |
| 14 from infra.libs.time_functions import timestamp | |
| 15 from infra.services.master_lifecycle import buildbot_state | |
| 16 | |
| 17 | |
| 18 LOGGER = logging.getLogger(__name__) | |
| 19 | |
| 20 | |
| 21 def load_desired_state_file(filename): # pragma: no cover | |
|
agable
2015/05/06 23:29:30
Feels odd. Not finding the file is exception-worth
ghost stip (do not use)
2015/05/07 06:50:57
Done.
| |
| 22 with open(filename) as f: | |
| 23 desired_state = json.load(f) | |
| 24 if not desired_master_state_is_valid(desired_state): | |
| 25 return None | |
| 26 return desired_state | |
| 27 | |
| 28 | |
| 29 def desired_master_state_is_valid(desired_state): | |
| 30 """Verify that the desired_master_state file is valid.""" | |
| 31 now = timestamp.utcnow_ts() | |
| 32 | |
| 33 for mastername, states in desired_state.iteritems(): | |
| 34 # Verify desired_state and transition_time_utc are present. | |
| 35 for k in ('desired_state', 'transition_time_utc'): | |
| 36 if not all(k in state for state in states): | |
| 37 LOGGER.error( | |
| 38 'one or more states for master %s do not contain %s', mastername, k) | |
| 39 return False | |
| 40 | |
| 41 # Verify the list is properly sorted. | |
| 42 sorted_states = sorted( | |
|
agable
2015/05/06 23:29:30
Why does it matter if they're sorted in the file w
ghost stip (do not use)
2015/05/07 06:50:57
Because a human editing / reading the file would b
agable
2015/05/07 17:59:31
Fair enough, that makes sense.
| |
| 43 states, key=operator.itemgetter('transition_time_utc')) | |
| 44 if sorted_states != states: | |
| 45 LOGGER.error('master %s does not have states sorted by timestamp', | |
| 46 mastername) | |
| 47 LOGGER.error('should be:\n%s', json.dumps(sorted_states, indent=2)) | |
| 48 return False | |
| 49 | |
| 50 # Verify desired_state and timestamp are valid. | |
| 51 for state in states: | |
| 52 if (state['desired_state'] not in | |
| 53 buildbot_state.STATES['desired_buildbot_state']): | |
| 54 LOGGER.error( | |
| 55 'desired_state \'%s\' is not one of %s', | |
| 56 state['desired_state'], | |
| 57 buildbot_state.STATES['desired_buildbot_state']) | |
| 58 return False | |
| 59 | |
| 60 if not isinstance(state['transition_time_utc'], (int, float)): | |
| 61 LOGGER.error( | |
| 62 'transition_time_utc \'%s\' is not an int or float', | |
| 63 state['transition_time_utc']) | |
| 64 return False | |
| 65 | |
| 66 # Verify there is at least one state in the past. | |
| 67 if not get_master_state(states, now=now): | |
|
agable
2015/05/06 23:29:30
Why? Why can't I create the file containing just a
iannucci
2015/05/06 23:48:05
Because then the script doesn't know what state it
ghost stip (do not use)
2015/05/07 06:50:57
Right, this is a discussion Robbie and I had in mo
agable
2015/05/07 17:59:31
I don't see how my proposal goes against your disc
ghost stip (do not use)
2015/05/07 19:49:39
There is only one case where a person who wants to
agable
2015/05/08 00:14:53
Case 1.5: The file used to contain a bunch of entr
ghost stip (do not use)
2015/05/08 00:22:13
You can't clean it out for readability fully, beca
| |
| 68 LOGGER.error( | |
| 69 'master %s does not have a state older than %s', mastername, now) | |
| 70 return False | |
| 71 | |
| 72 return True | |
| 73 | |
| 74 | |
| 75 def get_master_state(states, now=None): | |
| 76 """Returns the latest state earlier than the current (or specified) time. | |
| 77 | |
| 78 If there are three items, each with transition times of 100, 200 and 300: | |
| 79 * calling when 'now' is 50 will return None | |
| 80 * calling when 'now' is 150 will return the first item | |
| 81 * calling when 'now' is 400 will return the third item | |
| 82 """ | |
| 83 now = now or timestamp.utcnow_ts() | |
| 84 | |
| 85 times = [x['transition_time_utc'] for x in states] | |
| 86 index = bisect.bisect_left(times, now) | |
| 87 if index: | |
| 88 return states[index - 1] | |
| 89 return None | |
| 90 | |
| 91 | |
| 92 def get_masters_for_host(desired_state, build_dir, hostname): | |
| 93 """Identify which masters on this host should be managed. | |
| 94 | |
| 95 Returns two lists: triggered_masters and ignored_masters. | |
|
agable
2015/05/06 23:29:30
Any reason they're sets instead of lists? Ordering
ghost stip (do not use)
2015/05/07 06:50:57
TypeError: unhashable type: 'dict'
ignored_master
| |
| 96 | |
| 97 triggered_masters is a list of dicts. Each dict is the full dict from | |
| 98 mastermap.py with two extra keys: 'fulldir' (the absolute path to the master | |
| 99 directory), and 'states' (a list of desired states sorted by transition time, | |
| 100 pulled from the desired states file). | |
| 101 | |
| 102 ignored_masters is merely a list of 'dirname' strings (ex: master.chromium). | |
|
agable
2015/05/06 23:29:30
Docstring should explain why they're ignored
ghost stip (do not use)
2015/05/07 06:50:57
Done.
| |
| 103 """ | |
| 104 triggered_masters = [] | |
| 105 ignored_masters = [] | |
| 106 for master_dict in master.get_mastermap_for_host( | |
| 107 build_dir, hostname): | |
| 108 if master_dict['dirname'] in desired_state: | |
| 109 if master_dict['internal']: | |
| 110 master_dir = os.path.abspath(os.path.join( | |
| 111 build_dir, os.pardir, 'build_internal', 'masters', | |
| 112 master_dict['dirname'])) | |
| 113 else: | |
| 114 master_dir = os.path.abspath(os.path.join( | |
| 115 build_dir, 'masters', master_dict['dirname'])) | |
| 116 master_dict['fulldir'] = master_dir | |
| 117 master_dict['states'] = desired_state[master_dict['dirname']] | |
| 118 | |
| 119 triggered_masters.append(master_dict) | |
| 120 else: | |
| 121 ignored_masters.append(master_dict['dirname']) | |
| 122 return triggered_masters, ignored_masters | |
| OLD | NEW |