2
# Copyright 2011 Canonical Ltd. This software is licensed under the
3
# GNU Affero General Public License version 3 (see the file LICENSE).
5
"""Generate slonik scripts for Slony 1.2 to 2.0 migration.
7
Remove this script after migration is complete.
15
from optparse import OptionParser
17
from textwrap import dedent
19
from canonical.config import config
20
from canonical.database.sqlbase import connect
21
from canonical.launchpad import scripts
22
import replication.helpers
23
from replication.helpers import (
27
get_all_cluster_nodes,
36
LPMAIN_SET_ID: 'lpmain_set',
37
SSO_SET_ID: 'sso_set',
38
LPMIRROR_SET_ID: 'lpmirror_set',
42
def outpath(filename):
43
return os.path.join(options.outdir, filename)
46
def message(outf, msg):
48
print >> outf, "echo '%s';" % msg
51
def generate_preamble():
52
outf = open(outpath('mig_preamble.sk'), 'w')
53
print >> outf, replication.helpers.preamble(con)
57
for set_id, set_name in list(sets.items()):
59
"SELECT set_origin FROM _sl.sl_set WHERE set_id=%s", [set_id])
60
result = cur.fetchone()
63
print >> outf, "define %s_origin %d;" % (set_name, origin)
65
del sets[set_id] # For testing. Production will have 3 sets.
69
def generate_uninstall():
70
outf = open(outpath('mig_uninstall.sk'), 'w')
71
print >> outf, "# Uninstall Slony-I 1.2 from all nodes"
72
print >> outf, "include <mig_preamble.sk>;"
74
nodes = get_all_cluster_nodes(con)
76
# Ensure everything is really, really synced since we will be
77
# resubscribing with 'omit copy'
79
print >> outf, dedent("""\
81
wait for event (origin=%d, confirmed=all, wait on=%d);
82
""").strip() % (node.node_id, node.node_id, node.node_id)
85
message(outf, "Uninstall node %d" % node.node_id)
86
print >> outf, "uninstall node (id=%d);" % node.node_id
91
outf = open(outpath('mig_sync.sk'), 'w')
92
message(outf, "Waiting for sync")
93
print >> outf, "sync (id=@master_node);"
94
print >> outf, dedent("""\
96
origin=@master_node, confirmed=all, wait on=@master_node);
101
def generate_rebuild():
102
outf = open(outpath('mig_rebuild.sk'), 'w')
103
print >> outf, "# Rebuild the replication cluster with Slony-I 2.0"
104
print >> outf, "include <mig_preamble.sk>;"
106
nodes = get_all_cluster_nodes(con)
107
first_node = nodes[0]
108
remaining_nodes = nodes[1:]
110
# Initialize the cluster
111
message(outf, "Initializing cluster (node %d)" % first_node.node_id)
112
print >> outf, "init cluster (id=%d);" % first_node.node_id
114
# Create all the other nodes
115
for node in remaining_nodes:
116
message(outf, "Initializing node %d" % node.node_id)
117
print >> outf, "store node (id=%d, event node=%d);" % (
118
node.node_id, first_node.node_id)
120
# Create paths so they can communicate.
121
message(outf, "Storing %d paths" % pow(len(nodes),2))
122
for client_node in nodes:
123
for server_node in nodes:
125
"store path (server=%d, client=%d, "
126
"conninfo=@node%d_node_conninfo);" % (
127
server_node.node_id, client_node.node_id,
128
server_node.node_id))
130
# sync to ensure replication is happening.
131
print >> outf, "include <mig_sync.sk>;"
133
# Create replication sets.
134
for set_id, set_name in sets.items():
135
generate_initialize_set(set_id, set_name, outf)
136
print >> outf, "include <mig_sync.sk>;"
138
# Subscribe slave nodes to replication sets.
139
for set_id, set_name in sets.items():
140
generate_subscribe_set(set_id, set_name, outf)
145
def generate_initialize_set(set_id, set_name, outf):
146
origin_node = get_master_node(con, set_id)
147
message(outf, "Creating %s origin %d" % (set_name, origin_node.node_id))
148
print >> outf, "create set (id=%d, origin=@%s_origin, comment='%s');" % (
149
set_id, set_name, set_name)
152
SELECT tab_id, tab_nspname, tab_relname, tab_comment
153
FROM _sl.sl_table WHERE tab_set=%s
155
results = cur.fetchall()
156
message(outf, "Adding %d tables to %s" % (len(results), set_name))
157
for tab_id, tab_nspname, tab_relname, tab_comment in results:
160
print >> outf, dedent("""\
162
set id=@%s, origin=@%s_origin, id=%d,
163
fully qualified name='%s.%s',
166
set_name, set_name, tab_id,
167
tab_nspname, tab_relname, tab_comment)
169
SELECT seq_id, seq_nspname, seq_relname, seq_comment
170
FROM _sl.sl_sequence WHERE seq_set=%s
172
results = cur.fetchall()
173
message(outf, "Adding %d sequences to %s" % (len(results), set_name))
174
for seq_id, seq_nspname, seq_relname, seq_comment in results:
177
print >> outf, dedent("""\
179
set id=@%s, origin=@%s_origin, id=%d,
180
fully qualified name='%s.%s',
183
set_name, set_name, seq_id,
184
seq_nspname, seq_relname, seq_comment)
187
def generate_subscribe_set(set_id, set_name, outf):
188
origin_node = get_master_node(con, set_id)
191
SELECT sub_receiver FROM _sl.sl_subscribe
192
WHERE sub_set=%s and sub_active is true
194
for receiver_id, in cur.fetchall():
195
message(outf, "Subscribing node %d to %s" % (receiver_id, set_name))
196
print >> outf, dedent("""\
198
id=%d, provider=@%s_origin, receiver=%d,
199
forward=true, omit copy=true);
201
origin=@%s_origin, confirmed=all, wait on=@%s_origin);
203
set_id, set_name, receiver_id,
205
print >> outf, "include <mig_sync.sk>;"
209
parser = OptionParser()
210
scripts.db_options(parser)
212
"-o", "--output-dir", dest='outdir', default=".",
213
help="Write slonik scripts to DIR", metavar="DIR")
215
options, args = parser.parse_args()
217
parser.error("Too many arguments")
218
scripts.execute_zcml_for_scripts(use_web_security=False)
231
if __name__ == '__main__':
232
raise SystemExit(main())