~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
13
from optparse import OptionParser
14
import os.path
15
from textwrap import dedent
16
14157.3.1 by Jeroen Vermeulen
Lint.
17
import _pythonpath
9795.4.32 by Stuart Bishop
Generate slony migration scripts script
18
import replication.helpers
19
from replication.helpers import (
20
    LPMAIN_SET_ID,
21
    LPMIRROR_SET_ID,
22
    SSO_SET_ID,
23
    get_all_cluster_nodes,
24
    get_master_node,
25
    )
26
14157.3.1 by Jeroen Vermeulen
Lint.
27
from canonical.database.sqlbase import connect
14565.2.15 by Curtis Hovey
Moved canonical.launchpad.scripts __init__ to lp.services.scripts.
28
from lp.services import scripts
14157.3.1 by Jeroen Vermeulen
Lint.
29
9795.4.32 by Stuart Bishop
Generate slony migration scripts script
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())