summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/walmgr.txt7
-rw-r--r--python/conf/wal-master.ini1
-rw-r--r--python/conf/wal-slave.ini2
-rwxr-xr-xpython/walmgr.py88
4 files changed, 91 insertions, 7 deletions
diff --git a/doc/walmgr.txt b/doc/walmgr.txt
index bf4e3a02..c1390d75 100644
--- a/doc/walmgr.txt
+++ b/doc/walmgr.txt
@@ -187,6 +187,9 @@ Slave directory where incomplete WAL files are stored.
==== full_backup ====
Slave directory where full backups are stored.
+==== config_backup ====
+Slave directory where configuration file backups are stored. Optional.
+
==== loop_delay ====
The frequency of syncdaemon updates. In record shipping mode only
incremental updates are sent, so smaller interval can be used.
@@ -228,6 +231,10 @@ at log switch times.
Postgres data directory for the slave. This is where the restored
backup is copied/moved.
+==== slave_config_dir ====
+Directory for postgres configuration files. If specified, "walmgr restore"
+attempts to restore configuration files from config_backup directory.
+
==== slave_stop_cmd ====
Script to stop postmaster on slave.
diff --git a/python/conf/wal-master.ini b/python/conf/wal-master.ini
index b855a710..662fd52e 100644
--- a/python/conf/wal-master.ini
+++ b/python/conf/wal-master.ini
@@ -15,6 +15,7 @@ slave_config = /var/lib/postgresql/conf/wal-slave.ini
completed_wals = %(slave)s/logs.complete
partial_wals = %(slave)s/logs.partial
full_backup = %(slave)s/data.master
+config_backup = %(slave)s/config.backup
# syncdaemon update frequency
loop_delay = 10.0
diff --git a/python/conf/wal-slave.ini b/python/conf/wal-slave.ini
index fccc01df..7f60ce6f 100644
--- a/python/conf/wal-slave.ini
+++ b/python/conf/wal-slave.ini
@@ -7,11 +7,13 @@ slave_data = /var/lib/postgresql/8.3/main
slave_bin = /usr/lib/postgresql/8.3/bin
slave_stop_cmd = /etc/init.d/postgresql-8.3 stop
slave_start_cmd = /etc/init.d/postgresql-8.3 start
+slave_config_dir = /etc/postgresql/8.3/main
slave = /var/lib/postgresql/walshipping
completed_wals = %(slave)s/logs.complete
partial_wals = %(slave)s/logs.partial
full_backup = %(slave)s/data.master
+config_backup = %(slave)s/config.backup
keep_backups = 0
archive_command =
diff --git a/python/walmgr.py b/python/walmgr.py
index 8bc389de..1af19417 100755
--- a/python/walmgr.py
+++ b/python/walmgr.py
@@ -178,11 +178,11 @@ class BackupLabel:
class PostgresConfiguration:
"""Postgres configuration manipulation"""
- def __init__(self, walmgr):
+ def __init__(self, walmgr, cf_file):
"""load the configuration from master_config"""
self.walmgr = walmgr
self.log = walmgr.log
- self.cf_file = walmgr.cf.get("master_config")
+ self.cf_file = cf_file
self.cf_buf = open(self.cf_file, "r").read()
def archive_mode(self):
@@ -469,7 +469,7 @@ class WalMgr(skytools.DBScript):
def master_configure_archiving(self, enable_archiving, can_restart):
"""Turn the archiving on or off"""
- cf = PostgresConfiguration(self)
+ cf = PostgresConfiguration(self, self.cf.get("master_config"))
archive_mode = cf.archive_mode()
if enable_archiving:
@@ -508,6 +508,21 @@ class WalMgr(skytools.DBScript):
cf.modify(cf_params)
cf.write()
+ def slave_deconfigure_archiving(self, cf_file):
+ """Turn the archiving off for the slave"""
+
+ self.log.debug("Disable archiving in %s" % cf_file)
+
+ cf = PostgresConfiguration(self, cf_file)
+
+ cf_params = { "archive_command": "" }
+ if cf.archive_mode():
+ cf_params["archive_mode"] = "off"
+
+ self.log.debug("modifying configuration: %s" % cf_params)
+ cf.modify(cf_params)
+ cf.write()
+
def remote_mkdir(self, remdir):
tmp = remdir.split(":", 1)
if len(tmp) < 1:
@@ -574,6 +589,11 @@ class WalMgr(skytools.DBScript):
mkdir(self.cf.get("partial_wals"))
mkdir(self.cf.get("full_backup"))
+ cf_backup = self.cf.get("config_backup", "")
+ if cf_backup:
+ mkdir(cf_backup)
+
+
def master_periodic(self):
"""
Run periodic command on master node.
@@ -684,6 +704,18 @@ class WalMgr(skytools.DBScript):
"--delete", "pg_xlog", dst_loc]
self.exec_big_rsync(cmdline)
+ # copy config files
+ conf_dst_loc = self.cf.get("config_backup", "")
+ if conf_dst_loc:
+ master_conf_dir = os.path.dirname(self.cf.get("master_config"))
+ self.log.info("Backup conf files from %s" % master_conf_dir)
+ self.chdir(master_conf_dir)
+ cmdline = [
+ "--include", "*.conf",
+ "--exclude", "*",
+ ".", conf_dst_loc]
+ self.exec_big_rsync(cmdline)
+
self.remote_walmgr("xpurgewals")
except Exception, e:
self.log.error(e)
@@ -1214,8 +1246,9 @@ STOP TIME: %(stop_time)s
createbackup = False
if not setname and os.path.isdir(data_dir):
- # compatibility mode - default restore on slave and data directory exists
- self.log.warning("Old data directory is in the way, gotta move it.")
+ # compatibility mode - restore without a set name and data directory
+ # already exists. Move it out of the way.
+ self.log.warning("Data directory already exists, moving it out of the way.")
createbackup = True
# move old data away
@@ -1230,7 +1263,8 @@ STOP TIME: %(stop_time)s
if not setname:
os.rename(full_dir, data_dir)
else:
- self.exec_rsync(["--delete", "--no-relative", "--exclude=pg_xlog/*", os.path.join(full_dir,""), data_dir], True)
+ self.exec_rsync(["--delete", "--no-relative", "--exclude=pg_xlog/*",
+ os.path.join(full_dir,""), data_dir], True)
if self.wtype == MASTER and createbackup and os.path.isdir(bak):
# restore original xlog files to data_dir/pg_xlog
# symlinked directories are dereferences
@@ -1238,6 +1272,7 @@ STOP TIME: %(stop_time)s
else:
data_dir = full_dir
+ # copy configuration files to rotated backup directory
if createbackup and os.path.isdir(bak):
for cf in ('postgresql.conf', 'pg_hba.conf', 'pg_ident.conf'):
cfsrc = os.path.join(bak, cf)
@@ -1245,7 +1280,7 @@ STOP TIME: %(stop_time)s
if os.path.exists(cfdst):
self.log.info("Already exists: %s" % cfdst)
elif os.path.exists(cfsrc):
- self.log.info("Copy %s to %s" % (cfsrc, cfdst))
+ self.log.debug("Copy %s to %s" % (cfsrc, cfdst))
if not self.not_really:
copy_conf(cfsrc, cfdst)
@@ -1317,6 +1352,11 @@ restore_command = '%s %s %s'
if not self.not_really:
os.remove(stopfile)
+ # attempt to restore configuration. Note that we cannot
+ # postpone this to boot time, as the configuration is needed
+ # to start postmaster.
+ self.slave_restore_config()
+
# run database in recovery mode
self.log.info("Starting postmaster: " + start_cmd)
self.exec_system(start_cmd)
@@ -1324,6 +1364,40 @@ restore_command = '%s %s %s'
self.log.info("Data files restored, recovery.conf created.")
self.log.info("postgresql.conf and additional WAL files may need to be restored manually.")
+ def slave_restore_config(self):
+ """Restore the configuration files if target directory specified."""
+ self.assert_valid_role(SLAVE)
+
+ cf_source_dir = self.cf.get("config_backup", "")
+ cf_target_dir = self.cf.get("slave_config_dir", "")
+
+ if not cf_source_dir:
+ self.log.info("Configuration backup location not specified.")
+ return
+
+ if not cf_target_dir:
+ self.log.info("Configuration directory not specified, config files not restored.")
+ return
+
+ if not os.path.exists(cf_target_dir):
+ self.log.warning("Configuration directory does not exist: %s" % cf_target_dir)
+ return
+
+ self.log.info("Restoring configuration files")
+ for cf in ('postgresql.conf', 'pg_hba.conf', 'pg_ident.conf'):
+ cfsrc = os.path.join(cf_source_dir, cf)
+ cfdst = os.path.join(cf_target_dir, cf)
+
+ if not os.path.isfile(cfsrc):
+ self.log.warning("Missing configuration file backup: %s" % cf)
+ continue
+
+ self.log.debug("Copy %s to %s" % (cfsrc, cfdst))
+ if not self.not_really:
+ copy_conf(cfsrc, cfdst)
+ if cf == 'postgresql.conf':
+ self.slave_deconfigure_archiving(cfdst)
+
def slave_boot(self):
self.assert_valid_role(SLAVE)