~launchpad-pqm/launchpad/devel

9795.4.32 by Stuart Bishop
Generate slony migration scripts script
1
#!/usr/bin/python -S
2
# Copyright 2011 Canonical Ltd.  This software is licensed under the
3
# GNU Affero General Public License version 3 (see the file LICENSE).
4
5
"""Generate slonik scripts for Slony 1.2 to 2.0 migration.
6
7
Remove this script after migration is complete.
8
"""
9
10
__metaclass__ = type
11
__all__ = []
12
14606.3.5 by William Grant
Reformat
13
import _pythonpath
14
9795.4.32 by Stuart Bishop
Generate slony migration scripts script
15
from optparse import OptionParser
16
import os.path
17
from textwrap import dedent
18
14606.3.5 by William Grant
Reformat
19
from lp.services import scripts
20
from lp.services.database.sqlbase import connect
9795.4.32 by Stuart Bishop
Generate slony migration scripts script
21
import replication.helpers
22
from replication.helpers import (
23
    LPMAIN_SET_ID,
24
    LPMIRROR_SET_ID,
25
    SSO_SET_ID,
26
    get_all_cluster_nodes,
27
    get_master_node,
28
    )
29
30
31
con = None
32
options = None
33
34
sets = {
35
    LPMAIN_SET_ID: 'lpmain_set',
36
    SSO_SET_ID: 'sso_set',
37
    LPMIRROR_SET_ID: 'lpmirror_set',
38
    }
39
40
41
def outpath(filename):
42
    return os.path.join(options.outdir, filename)
43
44
9795.4.34 by Stuart Bishop
Slony 2.0 migration script for testing
45
def message(outf, msg):
46
    assert "'" not in msg
47
    print >> outf, "echo '%s';" % msg
48
49
9795.4.32 by Stuart Bishop
Generate slony migration scripts script
50
def generate_preamble():
51
    outf = open(outpath('mig_preamble.sk'), 'w')
52
    print >> outf, replication.helpers.preamble(con)
53
54
    cur = con.cursor()
55
56
    for set_id, set_name in list(sets.items()):
57
        cur.execute(
58
            "SELECT set_origin FROM _sl.sl_set WHERE set_id=%s", [set_id])
59
        result = cur.fetchone()
60
        if result:
61
            origin = result[0]
62
            print >> outf, "define %s_origin %d;" % (set_name, origin)
63
        else:
14157.3.1 by Jeroen Vermeulen
Lint.
64
            del sets[set_id]  # For testing. Production will have 3 sets.
9795.4.32 by Stuart Bishop
Generate slony migration scripts script
65
    outf.close()
66
67
68
def generate_uninstall():
69
    outf = open(outpath('mig_uninstall.sk'), 'w')
70
    print >> outf, "# Uninstall Slony-I 1.2 from all nodes"
71
    print >> outf, "include <mig_preamble.sk>;"
72
9795.4.33 by Stuart Bishop
moar sync
73
    nodes = get_all_cluster_nodes(con)
74
75
    # Ensure everything is really, really synced since we will be
76
    # resubscribing with 'omit copy'
77
    for node in nodes:
78
        print >> outf, dedent("""\
79
                sync (id=%d);
80
                wait for event (origin=%d, confirmed=all, wait on=%d);
81
                """).strip() % (node.node_id, node.node_id, node.node_id)
82
83
    for node in nodes:
9795.4.34 by Stuart Bishop
Slony 2.0 migration script for testing
84
        message(outf, "Uninstall node %d" % node.node_id)
9795.4.32 by Stuart Bishop
Generate slony migration scripts script
85
        print >> outf, "uninstall node (id=%d);" % node.node_id
86
    outf.close()
87
88
89
def generate_sync():
90
    outf = open(outpath('mig_sync.sk'), 'w')
9795.4.34 by Stuart Bishop
Slony 2.0 migration script for testing
91
    message(outf, "Waiting for sync")
92
    print >> outf, "sync (id=@master_node);"
93
    print >> outf, dedent("""\
94
        wait for event (
95
                origin=@master_node, confirmed=all, wait on=@master_node);
96
            """).strip()
9795.4.32 by Stuart Bishop
Generate slony migration scripts script
97
    outf.close()
98
99
100
def generate_rebuild():
101
    outf = open(outpath('mig_rebuild.sk'), 'w')
102
    print >> outf, "# Rebuild the replication cluster with Slony-I 2.0"
103
    print >> outf, "include <mig_preamble.sk>;"
104
105
    nodes = get_all_cluster_nodes(con)
106
    first_node = nodes[0]
107
    remaining_nodes = nodes[1:]
108
109
    # Initialize the cluster
9795.4.34 by Stuart Bishop
Slony 2.0 migration script for testing
110
    message(outf, "Initializing cluster (node %d)" % first_node.node_id)
9795.4.32 by Stuart Bishop
Generate slony migration scripts script
111
    print >> outf, "init cluster (id=%d);" % first_node.node_id
112
113
    # Create all the other nodes
114
    for node in remaining_nodes:
9795.4.34 by Stuart Bishop
Slony 2.0 migration script for testing
115
        message(outf, "Initializing node %d" % node.node_id)
9795.4.32 by Stuart Bishop
Generate slony migration scripts script
116
        print >> outf, "store node (id=%d, event node=%d);" % (
117
            node.node_id, first_node.node_id)
118
119
    # Create paths so they can communicate.
14157.3.1 by Jeroen Vermeulen
Lint.
120
    message(outf, "Storing %d paths" % pow(len(nodes), 2))
9795.4.32 by Stuart Bishop
Generate slony migration scripts script
121
    for client_node in nodes:
122
        for server_node in nodes:
123
            print >> outf, (
124
                "store path (server=%d, client=%d, "
125
                "conninfo=@node%d_node_conninfo);" % (
126
                    server_node.node_id, client_node.node_id,
127
                    server_node.node_id))
128
129
    # sync to ensure replication is happening.
130
    print >> outf, "include <mig_sync.sk>;"
131
132
    # Create replication sets.
133
    for set_id, set_name in sets.items():
134
        generate_initialize_set(set_id, set_name, outf)
135
    print >> outf, "include <mig_sync.sk>;"
136
137
    # Subscribe slave nodes to replication sets.
138
    for set_id, set_name in sets.items():
139
        generate_subscribe_set(set_id, set_name, outf)
140
141
    outf.close()
142
143
144
def generate_initialize_set(set_id, set_name, outf):
145
    origin_node = get_master_node(con, set_id)
9795.4.34 by Stuart Bishop
Slony 2.0 migration script for testing
146
    message(outf, "Creating %s origin %d" % (set_name, origin_node.node_id))
147
    print >> outf, "create set (id=%d, origin=@%s_origin, comment='%s');" % (
148
        set_id, set_name, set_name)
9795.4.36 by Stuart Bishop
Pull table and sequence lists from a node subscribed to the relevant set
149
    # Need to connect to a node currently replicating the desired set.
150
    origin_con = origin_node.connect()
151
    cur = origin_con.cursor()
9795.4.32 by Stuart Bishop
Generate slony migration scripts script
152
    cur.execute("""
153
        SELECT tab_id, tab_nspname, tab_relname, tab_comment
154
        FROM _sl.sl_table WHERE tab_set=%s
155
        """, (set_id,))
9795.4.34 by Stuart Bishop
Slony 2.0 migration script for testing
156
    results = cur.fetchall()
157
    message(outf, "Adding %d tables to %s" % (len(results), set_name))
158
    for tab_id, tab_nspname, tab_relname, tab_comment in results:
9795.4.32 by Stuart Bishop
Generate slony migration scripts script
159
        if not tab_comment:
14157.3.1 by Jeroen Vermeulen
Lint.
160
            tab_comment = ''
9795.4.32 by Stuart Bishop
Generate slony migration scripts script
161
        print >> outf, dedent("""\
162
                set add table (
9795.4.34 by Stuart Bishop
Slony 2.0 migration script for testing
163
                    set id=@%s, origin=@%s_origin, id=%d,
9795.4.32 by Stuart Bishop
Generate slony migration scripts script
164
                    fully qualified name='%s.%s',
165
                    comment='%s');
166
                """).strip() % (
9795.4.34 by Stuart Bishop
Slony 2.0 migration script for testing
167
                    set_name, set_name, tab_id,
9795.4.32 by Stuart Bishop
Generate slony migration scripts script
168
                    tab_nspname, tab_relname, tab_comment)
169
    cur.execute("""
170
        SELECT seq_id, seq_nspname, seq_relname, seq_comment
171
        FROM _sl.sl_sequence WHERE seq_set=%s
172
        """, (set_id,))
9795.4.34 by Stuart Bishop
Slony 2.0 migration script for testing
173
    results = cur.fetchall()
174
    message(outf, "Adding %d sequences to %s" % (len(results), set_name))
175
    for seq_id, seq_nspname, seq_relname, seq_comment in results:
9795.4.32 by Stuart Bishop
Generate slony migration scripts script
176
        if not seq_comment:
14157.3.1 by Jeroen Vermeulen
Lint.
177
            seq_comment = ''
9795.4.32 by Stuart Bishop
Generate slony migration scripts script
178
        print >> outf, dedent("""\
179
                set add sequence (
9795.4.34 by Stuart Bishop
Slony 2.0 migration script for testing
180
                    set id=@%s, origin=@%s_origin, id=%d,
9795.4.32 by Stuart Bishop
Generate slony migration scripts script
181
                    fully qualified name='%s.%s',
182
                    comment='%s');
183
                """).strip() % (
9795.4.34 by Stuart Bishop
Slony 2.0 migration script for testing
184
                    set_name, set_name, seq_id,
9795.4.32 by Stuart Bishop
Generate slony migration scripts script
185
                    seq_nspname, seq_relname, seq_comment)
186
187
188
def generate_subscribe_set(set_id, set_name, outf):
189
    cur = con.cursor()
190
    cur.execute("""
191
        SELECT sub_receiver FROM _sl.sl_subscribe
192
        WHERE sub_set=%s and sub_active is true
193
        """, (set_id,))
194
    for receiver_id, in cur.fetchall():
9795.4.34 by Stuart Bishop
Slony 2.0 migration script for testing
195
        message(outf, "Subscribing node %d to %s" % (receiver_id, set_name))
9795.4.32 by Stuart Bishop
Generate slony migration scripts script
196
        print >> outf, dedent("""\
197
                subscribe set (
9795.4.34 by Stuart Bishop
Slony 2.0 migration script for testing
198
                    id=%d, provider=@%s_origin, receiver=%d,
9795.4.32 by Stuart Bishop
Generate slony migration scripts script
199
                    forward=true, omit copy=true);
200
                wait for event (
9795.4.34 by Stuart Bishop
Slony 2.0 migration script for testing
201
                    origin=@%s_origin, confirmed=all, wait on=@%s_origin);
9795.4.32 by Stuart Bishop
Generate slony migration scripts script
202
                """).strip() % (
9795.4.34 by Stuart Bishop
Slony 2.0 migration script for testing
203
                    set_id, set_name, receiver_id,
204
                    set_name, set_name)
9795.4.32 by Stuart Bishop
Generate slony migration scripts script
205
        print >> outf, "include <mig_sync.sk>;"
206
207
208
def main():
209
    parser = OptionParser()
210
    scripts.db_options(parser)
211
    parser.add_option(
212
        "-o", "--output-dir", dest='outdir', default=".",
213
        help="Write slonik scripts to DIR", metavar="DIR")
214
    global options
215
    options, args = parser.parse_args()
216
    if args:
217
        parser.error("Too many arguments")
218
    scripts.execute_zcml_for_scripts(use_web_security=False)
219
220
    global con
221
    con = connect()
222
223
    generate_preamble()
224
    generate_sync()
225
    generate_uninstall()
226
    generate_rebuild()
227
228
    return 0
229
230
231
if __name__ == '__main__':
232
    raise SystemExit(main())