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

Side by Side Diff: infra/tools/master_manager/__main__.py

Issue 1108523002: Add buildbot state machine and master_manager tool. (Closed) Base URL: https://chromium.googlesource.com/infra/infra.git@daemon_bot_lib_merge
Patch Set: Address iannucci's final comments. 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 """Start, restart and shut down masters as needed."""
6
7 import argparse
8 import json
9 import logging
10 import os
11 import subprocess
12 import sys
13
14 from functools import partial
15
16 from infra.libs import logs
17 from infra.libs.buildbot import master
18 from infra.libs.service_utils import daemon
19 from infra.libs.service_utils import outer_loop
20 from infra.services.master_lifecycle import buildbot_state
21
22
23 def parse_args(): # pragma: no cover
24 parser = argparse.ArgumentParser(
25 description='Manage the state of a buildbot master. NOTE: Does nothing '
26 'unless --prod is specified')
27 parser.add_argument('directory', nargs='?',
28 help='location of the master to manage')
29 parser.add_argument('--desired-state-file', default='desired_state.json',
30 help='location of the state file to look up information')
31 parser.add_argument('--list-all-states', action='store_true',
32 help='list all states with their actions and exit')
33 parser.add_argument('--enable-gclient-sync', action='store_true',
34 help='perform a gclient sync before every master start')
35 parser.add_argument('--emergency-file',
36 default='.stop_master_lifecycle',
37 help='filename of the emergency stop file. if this file is found in the '
38 'master directory, exit immediately')
39 parser.add_argument('--prod', action='store_true',
40 help='actually run commands instead of printing them.')
41 parser.add_argument('--loop', action='store_true',
42 help='repeatedly run the state machine. will not terminate unless killed')
43 parser.add_argument('--loop-sleep-secs', type=int, default=5,
44 help='how many seconds to wait between loop runs. default %(default)s')
45 parser.add_argument('--connection-timeout', type=int, default=30,
46 help='how many seconds to wait for a master http request before timing '
47 'out.')
48 outer_loop.add_argparse_options(parser)
49 logs.add_argparse_options(parser)
50
51 args = parser.parse_args()
52 logs.process_argparse_options(args)
53
54 if not args.list_all_states and not args.directory:
55 parser.error('A master directory must be specified.')
56 return args
57
58
59 def run_state_machine_pass(
60 logger, matchlist, abs_master_directory, emergency_file, desired_state_file,
61 enable_gclient_sync, prod, connection_timeout): # pragma: no cover
62 if os.path.exists(os.path.join(abs_master_directory, emergency_file)):
63 logger.error('%s detected in %s, aborting!',
64 emergency_file, abs_master_directory)
65 return 1
66
67 desired_state_file = os.path.abspath(desired_state_file)
68 master_directory = os.path.basename(abs_master_directory)
69 evidence = buildbot_state.collect_evidence(
70 abs_master_directory, connection_timeout=connection_timeout)
71 with open(desired_state_file) as f:
72 evidence['desired_buildbot_state'] = json.load(f).get(master_directory)
73 if not evidence['desired_buildbot_state']:
74 raise KeyError('Couldn\'t get evidence for master %s from %s.' %
75 (master_directory, desired_state_file))
76
77 state, action_name, action_items = matchlist.execution_list(evidence)
78 execution_list = list(
79 master.convert_action_items_to_cli(
80 action_items, abs_master_directory,
81 enable_gclient=enable_gclient_sync))
82 logger.info('current state: %s', state)
83 logger.info('performing action: %s', action_name)
84
85 if execution_list:
86 if prod:
87 logger.info('production run, executing:')
88 else:
89 logger.info('dry run, not executing:')
90 for cmd in execution_list:
91 logger.info('* %s (in %s)', cmd['cmd'], cmd['cwd'])
92 if prod:
93 try:
94 with daemon.flock(cmd['lockfile']):
95 subprocess.check_call([str(x) for x in cmd['cmd']], cwd=cmd['cwd'])
96 except daemon.LockAlreadyLocked:
97 logger.warn(' lock on %s could not be acquired, no action taken.',
98 cmd['lockfile'])
99 else:
100 logger.info('no action to be taken.')
101 return 0
102
103
104 def main(): # pragma: no cover
105 args = parse_args()
106 matchlist = buildbot_state.construct_pattern_matcher()
107 logger = logging.getLogger(__name__)
108
109 if args.list_all_states:
110 matchlist.print_all_states()
111 return 0
112
113 abs_master_directory = os.path.abspath(args.directory)
114
115 state_machine = partial(run_state_machine_pass, logger,
116 matchlist, abs_master_directory, args.emergency_file,
117 args.desired_state_file, args.enable_gclient_sync, args.prod,
118 args.connection_timeout)
119
120 if args.loop:
121 loop_opts = outer_loop.process_argparse_options(args)
122 outer_loop.loop(
123 state_machine, lambda: args.loop_sleep_secs, **loop_opts)
124 else:
125 return state_machine()
126
127 return 0
128
129
130 if __name__ == '__main__': # pragma: no cover
131 sys.exit(main())
OLDNEW
« infra/services/master_lifecycle/buildbot_state.py ('K') | « infra/tools/master_manager/__init__.py ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698