~launchpad-pqm/launchpad/devel

10637.3.7 by Guilherme Salgado
merge devel
1
#!/usr/bin/python -S
8687.15.9 by Karl Fogel
Add the copyright header block to more files (everything under database/).
2
#
3
# Copyright 2009 Canonical Ltd.  This software is licensed under the
4
# GNU Affero General Public License version 3 (see the file LICENSE).
5799.1.12 by Stuart Bishop
Replication maintenance scripts, work in progress
5
5799.1.26 by Stuart Bishop
Work in progress
6
"""Initialize the cluster.
7
8
This script is run once to convert a singledb Launchpad instance to
9
a replicated setup.
10
"""
5799.1.12 by Stuart Bishop
Replication maintenance scripts, work in progress
11
12
import _pythonpath
13
14
from optparse import OptionParser
15
import subprocess
16
import sys
17
18
import helpers
19
5799.1.55 by Stuart Bishop
Improve initialize, less magic dev setup
20
from canonical.config import config
7178.4.15 by Stuart Bishop
staging db needs to be restored with --no-acl
21
from canonical.database.sqlbase import connect, ISOLATION_LEVEL_AUTOCOMMIT
5799.1.20 by Stuart Bishop
Work in progress
22
from canonical.database.postgresql import (
5799.1.55 by Stuart Bishop
Improve initialize, less magic dev setup
23
        all_sequences_in_schema, all_tables_in_schema, ConnectionString
5799.1.20 by Stuart Bishop
Work in progress
24
        )
5799.1.12 by Stuart Bishop
Replication maintenance scripts, work in progress
25
from canonical.launchpad.scripts import (
5799.1.39 by Stuart Bishop
Delint
26
        logger, logger_options, db_options
5799.1.12 by Stuart Bishop
Replication maintenance scripts, work in progress
27
        )
28
29
__metaclass__ = type
30
__all__ = []
31
32
5799.1.45 by Stuart Bishop
Breakup large main() in replication/initialize.py as per review comments.
33
log = None # Global logger, initialized in main()
34
35
options = None # Parsed command line options, initialized in main()
36
37
cur = None # Shared database cursor to the master, initialized in main()
38
39
40
def duplicate_schema():
41
    """Duplicate the master schema into the slaves."""
42
    log.info('Duplicating database schema')
5799.1.55 by Stuart Bishop
Improve initialize, less magic dev setup
43
7675.395.43 by Stuart Bishop
Update replication scripts to cope with recent ro mode changes
44
    master_cs = ConnectionString(config.database.rw_main_master)
5799.1.55 by Stuart Bishop
Improve initialize, less magic dev setup
45
    master_cs.user = options.dbuser
7675.395.43 by Stuart Bishop
Update replication scripts to cope with recent ro mode changes
46
    slave1_cs = ConnectionString(config.database.rw_main_slave)
5799.1.55 by Stuart Bishop
Improve initialize, less magic dev setup
47
    slave1_cs.user = options.dbuser
48
5799.1.12 by Stuart Bishop
Replication maintenance scripts, work in progress
49
    # We can't use pg_dump to replicate security as not all of the roles
50
    # may exist in the slave databases' clusters yet.
5799.1.55 by Stuart Bishop
Improve initialize, less magic dev setup
51
    cmd = "pg_dump -x -s %s | psql -q %s" % (
52
        master_cs.asPGCommandLineArgs(), slave1_cs.asPGCommandLineArgs())
53
    log.debug('Running %s' % cmd)
54
    rv = subprocess.call(cmd, shell=True)
5799.1.12 by Stuart Bishop
Replication maintenance scripts, work in progress
55
    if rv != 0:
5799.1.42 by Stuart Bishop
Review feedback, round 1
56
        log.fatal("Schema duplication failed, pg_dump returned %d" % rv)
5799.1.12 by Stuart Bishop
Replication maintenance scripts, work in progress
57
        sys.exit(rv)
58
59
    # Now setup security on the slaves and create any needed roles,
60
    log.info('Setting up security on slave')
5799.1.55 by Stuart Bishop
Improve initialize, less magic dev setup
61
    cmd = "../schema/security.py %s" % slave1_cs.asLPCommandLineArgs()
62
    log.debug("Running %s" % cmd)
63
    rv = subprocess.call(cmd.split())
5799.1.12 by Stuart Bishop
Replication maintenance scripts, work in progress
64
    if rv != 0:
65
        print >> sys.stderr, "ERR: security setup failed, returning %d" % rv
66
        sys.exit(rv)
67
5799.1.45 by Stuart Bishop
Breakup large main() in replication/initialize.py as per review comments.
68
69
def initialize_cluster():
70
    """Initialize the cluster."""
5799.1.12 by Stuart Bishop
Replication maintenance scripts, work in progress
71
    log.info('Initializing Slony-I cluster')
7675.85.2 by Jonathan Lange
Undo revision generated by step 2 of process.
72
    master_connection_string = ConnectionString(
7675.395.43 by Stuart Bishop
Update replication scripts to cope with recent ro mode changes
73
        config.database.rw_main_master)
7675.85.2 by Jonathan Lange
Undo revision generated by step 2 of process.
74
    master_connection_string.user = 'slony'
5799.1.12 by Stuart Bishop
Replication maintenance scripts, work in progress
75
    helpers.execute_slonik("""
7675.85.2 by Jonathan Lange
Undo revision generated by step 2 of process.
76
        node 1 admin conninfo = '%s';
5799.1.20 by Stuart Bishop
Work in progress
77
        try {
78
            echo 'Initializing cluster and Master node.';
7675.85.2 by Jonathan Lange
Undo revision generated by step 2 of process.
79
            init cluster (id=1, comment='Master Node');
5799.1.20 by Stuart Bishop
Work in progress
80
            }
81
        on success { echo 'Cluster initialized.'; }
82
        on error { echo 'Cluster initialization failed.'; exit 1; }
7675.85.2 by Jonathan Lange
Undo revision generated by step 2 of process.
83
        """ % master_connection_string)
5799.1.12 by Stuart Bishop
Replication maintenance scripts, work in progress
84
5799.1.45 by Stuart Bishop
Breakup large main() in replication/initialize.py as per review comments.
85
86
def ensure_live():
5799.1.20 by Stuart Bishop
Work in progress
87
    log.info('Ensuring slon daemons are live and propagating events.')
5799.1.45 by Stuart Bishop
Breakup large main() in replication/initialize.py as per review comments.
88
    helpers.sync(120) # Will exit on failure.
89
90
7675.395.72 by Stuart Bishop
Remove remaining authdb awareness from replication scripts
91
def create_replication_sets(lpmain_tables, lpmain_sequences):
5799.1.45 by Stuart Bishop
Breakup large main() in replication/initialize.py as per review comments.
92
    """Create the replication sets."""
5799.1.12 by Stuart Bishop
Replication maintenance scripts, work in progress
93
    log.info('Creating Slony-I replication sets.')
7675.85.2 by Jonathan Lange
Undo revision generated by step 2 of process.
94
7178.4.19 by Stuart Bishop
Single replication set for now
95
    script = ["try {"]
5799.1.20 by Stuart Bishop
Work in progress
96
7675.395.72 by Stuart Bishop
Remove remaining authdb awareness from replication scripts
97
    entry_id = 1
5799.1.20 by Stuart Bishop
Work in progress
98
99
    script.append("""
5799.1.55 by Stuart Bishop
Improve initialize, less magic dev setup
100
        echo 'Creating LPMain replication set (@lpmain_set)';
5799.1.20 by Stuart Bishop
Work in progress
101
        create set (
5799.1.55 by Stuart Bishop
Improve initialize, less magic dev setup
102
            id=@lpmain_set, origin=@master_node,
5799.1.12 by Stuart Bishop
Replication maintenance scripts, work in progress
103
            comment='Launchpad tables and sequences');
5799.1.20 by Stuart Bishop
Work in progress
104
        """)
105
7675.251.1 by Stuart Bishop
Make replication initializer less noisy
106
    script.append(
107
        "echo 'Adding %d tables to replication set @lpmain_set';"
108
        % len(lpmain_tables))
5799.1.24 by Stuart Bishop
Tweak initialize
109
    for table in sorted(lpmain_tables):
5799.1.20 by Stuart Bishop
Work in progress
110
        script.append("""
111
            set add table (
5799.1.55 by Stuart Bishop
Improve initialize, less magic dev setup
112
                set id=@lpmain_set,
113
                origin=@master_node,
5799.1.20 by Stuart Bishop
Work in progress
114
                id=%(entry_id)d,
115
                fully qualified name='%(table)s');
116
            """ % vars())
117
        entry_id += 1
5799.1.31 by Stuart Bishop
Add newly created tables to lpmain replication set
118
7675.395.72 by Stuart Bishop
Remove remaining authdb awareness from replication scripts
119
    entry_id = 1
7675.251.1 by Stuart Bishop
Make replication initializer less noisy
120
    script.append(
121
        "echo 'Adding %d sequences to replication set @lpmain_set';"
122
        % len(lpmain_sequences))
5799.1.24 by Stuart Bishop
Tweak initialize
123
    for sequence in sorted(lpmain_sequences):
5799.1.20 by Stuart Bishop
Work in progress
124
        script.append("""
125
            set add sequence (
5799.1.55 by Stuart Bishop
Improve initialize, less magic dev setup
126
                set id=@lpmain_set,
127
                origin=@master_node,
5799.1.20 by Stuart Bishop
Work in progress
128
                id=%(entry_id)d,
129
                fully qualified name='%(sequence)s');
130
            """ % vars())
131
        entry_id += 1
132
133
    script.append("""
134
        }
135
        on error { echo 'Failed.'; exit 1; }
136
        """)
137
    helpers.execute_slonik('\n'.join(script), sync=600)
5799.1.12 by Stuart Bishop
Replication maintenance scripts, work in progress
138
5799.1.26 by Stuart Bishop
Work in progress
139
    helpers.validate_replication(cur) # Explode now if we have messed up.
140
5799.1.45 by Stuart Bishop
Breakup large main() in replication/initialize.py as per review comments.
141
142
def main():
143
    parser = OptionParser()
144
    db_options(parser)
145
    logger_options(parser)
146
147
    parser.set_defaults(dbuser='slony')
148
149
    global options
150
    options, args = parser.parse_args()
151
152
    global log
153
    log = logger(options)
154
155
    # Generate lists of sequences and tables for our replication sets.
156
    log.debug("Connecting as %s" % options.dbuser)
157
    con = connect(options.dbuser)
7178.4.15 by Stuart Bishop
staging db needs to be restored with --no-acl
158
    con.set_isolation_level(ISOLATION_LEVEL_AUTOCOMMIT)
5799.1.45 by Stuart Bishop
Breakup large main() in replication/initialize.py as per review comments.
159
    global cur
160
    cur = con.cursor()
7675.85.2 by Jonathan Lange
Undo revision generated by step 2 of process.
161
    log.debug("Calculating lpmain replication set.")
5799.1.45 by Stuart Bishop
Breakup large main() in replication/initialize.py as per review comments.
162
    lpmain_tables, lpmain_sequences = helpers.calculate_replication_set(
163
        cur, helpers.LPMAIN_SEED)
164
165
    # Sanity check these lists - we want all objects in the public
166
    # schema to be in one and only one replication set.
7675.85.2 by Jonathan Lange
Undo revision generated by step 2 of process.
167
    log.debug("Performing sanity checks.")
5799.1.45 by Stuart Bishop
Breakup large main() in replication/initialize.py as per review comments.
168
    fails = 0
169
    for table in all_tables_in_schema(cur, 'public'):
170
        times_seen = 0
7675.395.72 by Stuart Bishop
Remove remaining authdb awareness from replication scripts
171
        for table_set in [lpmain_tables, helpers.IGNORED_TABLES]:
5799.1.45 by Stuart Bishop
Breakup large main() in replication/initialize.py as per review comments.
172
            if table in table_set:
173
                times_seen += 1
174
        if times_seen == 0:
175
            log.error("%s not in any replication set." % table)
176
            fails += 1
177
        if times_seen > 1:
178
            log.error("%s is in multiple replication sets." % table)
179
            fails += 1
180
    for sequence in all_sequences_in_schema(cur, 'public'):
181
        times_seen = 0
7675.395.72 by Stuart Bishop
Remove remaining authdb awareness from replication scripts
182
        for sequence_set in [lpmain_sequences, helpers.IGNORED_SEQUENCES]:
5799.1.45 by Stuart Bishop
Breakup large main() in replication/initialize.py as per review comments.
183
            if sequence in sequence_set:
184
                times_seen += 1
185
        if times_seen == 0:
186
            log.error("%s not in any replication set." % sequence)
187
            fails += 1
188
        if times_seen > 1:
189
            log.error("%s is in multiple replication sets." % sequence)
190
            fails += 1
191
    if fails > 0:
192
        log.fatal("%d errors in replication set definitions." % fails)
193
        sys.exit(1)
194
195
    initialize_cluster()
196
197
    ensure_live()
198
7675.395.72 by Stuart Bishop
Remove remaining authdb awareness from replication scripts
199
    create_replication_sets(lpmain_tables, lpmain_sequences)
5799.1.45 by Stuart Bishop
Breakup large main() in replication/initialize.py as per review comments.
200
7675.85.2 by Jonathan Lange
Undo revision generated by step 2 of process.
201
    helpers.sync(0)
5799.1.45 by Stuart Bishop
Breakup large main() in replication/initialize.py as per review comments.
202
203
5799.1.12 by Stuart Bishop
Replication maintenance scripts, work in progress
204
if __name__ == '__main__':
205
    sys.exit(main())