1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
|
#!/usr/bin/python -S
#
# Copyright 2009 Canonical Ltd. This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
"""Remove the broken Slony-I information from database populated by
pg_restore(1).
When you dump a database using pg_dump(1), the Slony-I install is dumped
too. After restoring this dump, you have a non-functional Slony-I
installation. If you are recovering the database for disaster recovery
purposes, you can keep the current install by repairing it using the
slonik(1) command REPAIR CONFIG. In other cases, you need to remove
Slony-I from the database (eg. building a staging database, we need
to install replication fresh.). This script does this procedure.
"""
__metaclass__ = type
__all__ = []
import _pythonpath
from optparse import OptionParser
import sys
import psycopg2
from lp.services.config import config
from lp.services.database.postgresql import ConnectionString
from lp.services.database.sqlbase import (
connect,
ISOLATION_LEVEL_AUTOCOMMIT,
quote,
)
from lp.services.scripts import (
db_options,
logger,
logger_options,
)
import replication.helpers
def main():
parser = OptionParser()
db_options(parser)
logger_options(parser)
parser.set_defaults(dbuser='slony')
options, args = parser.parse_args()
log = logger(options)
con = connect(isolation=ISOLATION_LEVEL_AUTOCOMMIT)
if not replication.helpers.slony_installed(con):
log.info("Slony-I not installed. Nothing to do.")
return 0
if not repair_with_slonik(log, options, con):
repair_with_drop_schema(log, con)
return 0
def repair_with_slonik(log, options, con):
"""Attempt to uninstall Slony-I via 'UNINSTALL NODE' per best practice.
Returns True on success, False if unable to do so for any reason.
"""
cur = con.cursor()
# Determine the node id the database thinks it is.
try:
cmd = "SELECT %s.getlocalnodeid(%s)" % (
replication.helpers.CLUSTER_NAMESPACE,
quote(replication.helpers.CLUSTER_NAMESPACE))
cur.execute(cmd)
node_id = cur.fetchone()[0]
log.debug("Node Id is %d" % node_id)
# Get a list of set ids in the database.
cur.execute(
"SELECT DISTINCT set_id FROM %s.sl_set"
% replication.helpers.CLUSTER_NAMESPACE)
set_ids = set(row[0] for row in cur.fetchall())
log.debug("Set Ids are %s" % repr(set_ids))
except psycopg2.InternalError:
# Not enough information to determine node id. Possibly
# this is an empty database.
log.debug('Broken or no Slony-I install.')
return False
connection_string = ConnectionString(config.database.rw_main_master)
if options.dbname:
connection_string.dbname = options.dbname
if options.dbuser:
connection_string.user = options.dbuser
if options.dbhost:
connection_string.host = options.dbhost
script = [
"cluster name = %s;" % replication.helpers.CLUSTERNAME,
"node %d admin conninfo = '%s';" % (node_id, connection_string),
]
for set_id in set_ids:
script.append(
"repair config (set id=%d, event node=%d, execute only on=%d);"
% (set_id, node_id, node_id))
script.append("uninstall node (id=%d);" % node_id)
for line in script:
log.debug(line)
script = '\n'.join(script)
return replication.helpers.execute_slonik(
script, auto_preamble=False, exit_on_fail=False)
def repair_with_drop_schema(log, con):
"""
Just drop the _sl schema as it is 'good enough' with Slony-I 1.2.
This mechanism fails with Slony added primary keys, but we don't
do that.
"""
log.info('Fallback mode - dropping _sl schema.')
cur = con.cursor()
cur.execute("DROP SCHEMA _sl CASCADE")
return True
if __name__ == '__main__':
sys.exit(main())
|