20
from preflight import (
21
KillConnectionsPreflight,
22
NoConnectionCheckPreflight,
24
import security # security.py script
25
import upgrade # upgrade.py script
21
28
PGBOUNCER_INITD = ['sudo', '/etc/init.d/pgbouncer']
24
def run_script(script, *extra_args):
25
script_path = os.path.join(os.path.dirname(__file__), script)
26
return subprocess.call([script_path] + sys.argv[1:] + list(extra_args))
31
def run_pgbouncer(log, cmd):
32
"""Invoke the pgbouncer initscript.
34
:param cmd: One of 'start', 'stop' or 'status'.
36
assert cmd in ('start', 'stop', 'status'), '''
37
Unrecognized command; remember any new commands need to be
38
granted sudo on staging and prod.
40
pgbouncer_rc = subprocess.call(PGBOUNCER_INITD + [cmd])
43
log.error("pgbouncer '%s' failed [%s]", cmd, pgbouncer_rc)
47
def run_upgrade(options, log):
48
"""Invoke upgrade.py in-process.
50
It would be easier to just invoke the script, but this way we save
51
several seconds of overhead as the component architecture loads up.
53
# Fake expected command line arguments and global log
55
options.partial = False
56
upgrade.options = options
58
# Invoke the database schema upgrade process.
62
log.exception('Unhandled exception')
65
log.fatal("upgrade.py failed [%s]", x)
68
def run_security(options, log):
69
"""Invoke security.py in-process.
71
It would be easier to just invoke the script, but this way we save
72
several seconds of overhead as the component architecture loads up.
74
# Fake expected command line arguments and global log
75
options.dryrun = False
77
options.owner = 'postgres'
78
options.cluster = True
79
security.options = options
81
# Invoke the database security reset process.
83
return security.main(options)
85
log.exception('Unhandled exception')
88
log.fatal("security.py failed [%s]", x)
46
108
# We initially ignore open connections, as they will shortly be
48
preflight_rc = run_script('preflight.py', '--skip-connection-check')
110
if not NoConnectionCheckPreflight(log).check_all():
52
113
# Confirm we can invoke PGBOUNCER_INITD
53
pgbouncer_status_cmd = PGBOUNCER_INITD + ['status']
54
pgbouncer_rc = subprocess.call(pgbouncer_status_cmd)
114
pgbouncer_rc = run_pgbouncer(log, 'status')
56
115
if pgbouncer_rc != 0:
57
log.fatal("Unable to invoke %s", ' '.join(pgbouncer_status_cmd))
58
116
return pgbouncer_rc
66
124
pgbouncer_down = False
67
125
upgrade_run = False
69
126
security_run = False
72
129
# Shutdown pgbouncer
73
pgbouncer_rc = subprocess.call(PGBOUNCER_INITD + ['stop'])
130
pgbouncer_rc = run_pgbouncer(log, 'stop')
75
131
if pgbouncer_rc != 0:
76
132
log.fatal("pgbouncer not shut down [%s]", pgbouncer_rc)
77
133
return pgbouncer_rc
78
134
pgbouncer_down = True
80
preflight_rc = run_script('preflight.py', '--kill-connections')
136
if not KillConnectionsPreflight(log).check_all():
84
upgrade_rc = run_script('upgrade.py')
139
upgrade_rc = run_upgrade(options, log)
85
140
if upgrade_rc != 0:
86
log.warning("upgrade.py run may have been partial")
88
142
upgrade_run = True
90
fti_rc = run_script('fti.py')
95
security_rc = run_script('security.py', '--cluster')
144
security_rc = run_security(options, log)
96
145
if security_rc != 0:
97
146
return security_rc
98
147
security_run = True
100
149
log.info("All database upgrade steps completed")
102
pgbouncer_rc = subprocess.call(PGBOUNCER_INITD + ['start'])
151
pgbouncer_rc = run_pgbouncer(log, 'start')
104
152
if pgbouncer_rc != 0:
105
153
log.fatal("pgbouncer not restarted [%s]", pgbouncer_rc)
106
154
return pgbouncer_rc
109
157
# We will start seeing connections as soon as pgbouncer is
110
158
# reenabled, so ignore them here.
111
preflight_rc = run_script('preflight.py', '--skip-connection-check')
112
if preflight_rc != 0:
159
if not NoConnectionCheckPreflight(log).check_all():
115
162
log.info("All good. All done.")
119
166
if pgbouncer_down:
120
log.warning("pgbouncer is down and will need to be restarted")
167
# Even if upgrade.py or security.py failed, we should be in
168
# a good enough state to continue operation so restart
169
# pgbouncer and allow connections.
170
# - upgrade.py may have failed to update the master, and
171
# changes should have rolled back.
172
# - upgrade.py may have failed to update a slave, breaking
173
# replication. The master is still operational, but
174
# slaves may be lagging and have the old schema.
175
# - security.py may have died, rolling back its changes on
177
# In all cases except the first, we have recovery to do but
178
# systems are probably ok, or at least providing some
180
pgbouncer_rc = run_pgbouncer(log, 'start')
181
if pgbouncer_rc == 0:
182
log.info("Despite failures, pgbouncer restarted.")
184
log.fatal("pgbouncer is down and refuses to restart")
121
185
if not upgrade_run:
122
186
log.warning("upgrade.py still needs to be run")
124
log.warning("fti.py still needs to be run")
125
187
if not security_run:
126
188
log.warning("security.py still needs to be run")