Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(560)

Side by Side Diff: infra/services/master_manager_launcher/desired_state_parser.py

Issue 1128783003: Add master_manager_launch script which launches master_manager scripts for each master on a host. (Closed) Base URL: https://chromium.googlesource.com/infra/infra.git@master
Patch Set: Move to infra/services. Created 5 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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 class InvalidDesiredMasterState(ValueError):
22 pass
23
24
25 def load_desired_state_file(filename): # pragma: no cover
agable 2015/05/07 17:59:31 Seems like this could be tested with a couple vali
ghost stip (do not use) 2015/05/07 19:49:39 Done.
26 with open(filename) as f:
27 desired_state = json.load(f)
28 if not desired_master_state_is_valid(desired_state):
29 raise InvalidDesiredMasterState()
30 return desired_state
31
32
33 def desired_master_state_is_valid(desired_state):
34 """Verify that the desired_master_state file is valid."""
35 now = timestamp.utcnow_ts()
36
37 for mastername, states in desired_state.iteritems():
38 # Verify desired_state and transition_time_utc are present.
39 for k in ('desired_state', 'transition_time_utc'):
40 if not all(k in state for state in states):
41 LOGGER.error(
42 'one or more states for master %s do not contain %s', mastername, k)
43 return False
44
45 # Verify the list is properly sorted.
46 sorted_states = sorted(
47 states, key=operator.itemgetter('transition_time_utc'))
48 if sorted_states != states:
49 LOGGER.error('master %s does not have states sorted by timestamp',
50 mastername)
51 LOGGER.error('should be:\n%s', json.dumps(sorted_states, indent=2))
52 return False
53
54 # Verify desired_state and timestamp are valid.
agable 2015/05/07 17:59:31 Seems like the first "check for presence" loop cou
ghost stip (do not use) 2015/05/07 19:49:39 Done.
55 for state in states:
56 if (state['desired_state'] not in
57 buildbot_state.STATES['desired_buildbot_state']):
58 LOGGER.error(
59 'desired_state \'%s\' is not one of %s',
60 state['desired_state'],
61 buildbot_state.STATES['desired_buildbot_state'])
62 return False
63
64 if not isinstance(state['transition_time_utc'], (int, float)):
65 LOGGER.error(
66 'transition_time_utc \'%s\' is not an int or float',
67 state['transition_time_utc'])
68 return False
69
70 # Verify there is at least one state in the past.
71 if not get_master_state(states, now=now):
72 LOGGER.error(
73 'master %s does not have a state older than %s', mastername, now)
74 return False
75
76 return True
77
78
79 def get_master_state(states, now=None):
80 """Returns the latest state earlier than the current (or specified) time.
81
82 If there are three items, each with transition times of 100, 200 and 300:
83 * calling when 'now' is 50 will return None
84 * calling when 'now' is 150 will return the first item
85 * calling when 'now' is 400 will return the third item
86 """
87 now = now or timestamp.utcnow_ts()
88
89 times = [x['transition_time_utc'] for x in states]
90 index = bisect.bisect_left(times, now)
91 if index:
agable 2015/05/07 17:59:31 bisect_left is guaranteed to return an int, so ple
ghost stip (do not use) 2015/05/07 19:49:39 Done.
92 return states[index - 1]
93 return None
94
95
96 def get_masters_for_host(desired_state, build_dir, hostname):
97 """Identify which masters on this host should be managed.
98
99 Returns triggered_masters and ignored_masters (a set and a list).
agable 2015/05/07 17:59:31 (a list and a set, respectively).
ghost stip (do not use) 2015/05/07 19:49:39 Done.
100
101 triggered_masters are masters on this host which have a corresponding entry in
102 the desired_master_state file. Any master running assigned to this host that
103 does *not* have an entry in the desired_master_state file is considered
104 'ignored.'
105
106 triggered_masters is a list of dicts. Each dict is the full dict from
107 mastermap.py with two extra keys: 'fulldir' (the absolute path to the master
108 directory), and 'states' (a list of desired states sorted by transition time,
109 pulled from the desired states file).
110
111 ignored_masters is a set of 'dirname' strings (ex: master.chromium).
112 """
113 triggered_masters = []
114 ignored_masters = set()
115 for master_dict in master.get_mastermap_for_host(
116 build_dir, hostname):
117 if master_dict['dirname'] in desired_state:
118 if master_dict['internal']:
119 master_dir = os.path.abspath(os.path.join(
120 build_dir, os.pardir, 'build_internal', 'masters',
121 master_dict['dirname']))
122 else:
123 master_dir = os.path.abspath(os.path.join(
124 build_dir, 'masters', master_dict['dirname']))
125 master_dict['fulldir'] = master_dir
126 master_dict['states'] = desired_state[master_dict['dirname']]
127
128 triggered_masters.append(master_dict)
129 else:
130 ignored_masters.add(master_dict['dirname'])
131 return triggered_masters, ignored_masters
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698