(self, restore_config=True)
Restore the database from backup

        If setname is specified, the contents of that backup set directory are
        restored instead of "full_backup". Also copy is used instead of rename to
        restore the directory (unless a pg_xlog directory has been specified).

        Restore to altdst if specified. Complain if it exists.
Restore the database from backup
Restore the database from backup
def restore_database(self, restore_config=True):
        """Restore the database from backup

        If setname is specified, the contents of that backup set directory are
        restored instead of "full_backup". Also copy is used instead of rename to
        restore the directory (unless a pg_xlog directory has been specified).

        Restore to altdst if specified. Complain if it exists.
        """

        setname = len(self.args) > 0 and self.args[0] or None
        altdst  = len(self.args) > 1 and self.args[1] or None

        if not self.is_master:
            data_dir = self.cf.getfile("slave_data")
            stop_cmd = self.cf.getfile("slave_stop_cmd", "")
            start_cmd = self.cf.getfile("slave_start_cmd")
            pidfile = os.path.join(data_dir, "postmaster.pid")
        else:
            if not setname or not altdst:
                die(1, "Source and target directories must be specified if running on master node.")
            data_dir = altdst
            stop_cmd = None
            pidfile = None

        if setname:
            full_dir = os.path.join(self.cf.getfile("walmgr_data"), setname)
        else:
            full_dir = self.cf.getfile("full_backup")

        # stop postmaster if ordered
        if stop_cmd and os.path.isfile(pidfile):
            self.log.info("Stopping postmaster: %s", stop_cmd)
            self.exec_system(stop_cmd)
            time.sleep(3)

        # is it dead?
        if pidfile and os.path.isfile(pidfile):
            self.log.info("Pidfile exists, checking if process is running.")
            if self.signal_postmaster(data_dir, 0):
                self.log.fatal("Postmaster still running.  Cannot continue.")
                sys.exit(1)

        # find name for data backup
        i = 0
        while 1:
            bak = "%s.%d" % (data_dir.rstrip("/"), i)
            if not os.path.isdir(bak):
                break
            i += 1

        if self.is_master:
            print >>sys.stderr, "About to restore to directory %s. The postgres cluster should be shut down." % data_dir
            if not yesno("Is postgres shut down on %s ?" % data_dir):
                die(1, "Shut it down and try again.")

        if not self.is_master:
            createbackup = True
        elif os.path.isdir(data_dir):
            createbackup = yesno("Create backup of %s?" % data_dir)
        else:
            # nothing to back up
            createbackup = False

        # see if we have to make a backup of the data directory
        backup_datadir = self.cf.getboolean('backup_datadir', True)

        if os.path.isdir(data_dir) and not backup_datadir:
            self.log.warning('backup_datadir is disabled, deleting old data dir')
            shutil.rmtree(data_dir)

        if not setname and os.path.isdir(data_dir) and backup_datadir:
            # compatibility mode - restore without a set name and data directory exists
            self.log.info("Data directory already exists, moving it out of the way.")
            createbackup = True

        # move old data away
        if createbackup and os.path.isdir(data_dir):
            self.log.info("Move %s to %s", data_dir, bak)
            if not self.not_really:
                os.rename(data_dir, bak)

        # move new data, copy if setname specified
        self.log.info("%s %s to %s", setname and "Copy" or "Move", full_dir, data_dir)

        if self.cf.getfile('slave_pg_xlog', ''):
            link_xlog_dir = True
            exclude_pg_xlog = '--exclude=pg_xlog'
        else:
            link_xlog_dir = False
            exclude_pg_xlog = ''

        if not self.not_really:
            if not setname and not link_xlog_dir:
                os.rename(full_dir, data_dir)
            else:
                rsync_args=["--delete", "--no-relative", "--exclude=pg_xlog/*"]
                if exclude_pg_xlog:
                    rsync_args.append(exclude_pg_xlog)
                rsync_args += [os.path.join(full_dir, ""), data_dir]

                self.exec_rsync(rsync_args, True)

                if link_xlog_dir:
                   os.symlink(self.cf.getfile('slave_pg_xlog'), "%s/pg_xlog" % data_dir)

                if (self.is_master and createbackup and os.path.isdir(bak)):
                    # restore original xlog files to data_dir/pg_xlog
                    # symlinked directories are dereferenced
                    self.exec_cmd(["cp", "-rL", "%s/pg_xlog/" % full_dir, "%s/pg_xlog" % data_dir ])
                else:
                    # create an archive_status directory
                    xlog_dir = os.path.join(data_dir, "pg_xlog")
                    archive_path = os.path.join(xlog_dir, "archive_status")
                    if not os.path.exists(archive_path):
                        os.mkdir(archive_path, 0700)
        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)
                cfdst = os.path.join(data_dir, cf)
                if os.path.exists(cfdst):
                    self.log.info("Already exists: %s", cfdst)
                elif os.path.exists(cfsrc):
                    self.log.debug("Copy %s to %s", cfsrc, cfdst)
                    if not self.not_really:
                        copy_conf(cfsrc, cfdst)

        # re-link tablespaces
        spc_dir = os.path.join(data_dir, "pg_tblspc")
        tmp_dir = os.path.join(data_dir, "tmpspc")
        if not os.path.isdir(spc_dir):
            # 8.3 requires its existence
            os.mkdir(spc_dir)
        if os.path.isdir(tmp_dir):
            self.log.info("Linking tablespaces to temporary location")

            # don't look into spc_dir, thus allowing
            # user to move them before.  re-link only those
            # that are still in tmp_dir
            list = os.listdir(tmp_dir)
            list.sort()

            for d in list:
                if d[0] == ".":
                    continue
                link_loc = os.path.abspath(os.path.join(spc_dir, d))
                link_dst = os.path.abspath(os.path.join(tmp_dir, d))
                self.log.info("Linking tablespace %s to %s", d, link_dst)
                if not self.not_really:
                    if os.path.islink(link_loc):
                        os.remove(link_loc)
                    os.symlink(link_dst, link_loc)


        # write recovery.conf
        rconf = os.path.join(data_dir, "recovery.conf")
        cf_file = os.path.abspath(self.cf.filename)

        # determine if we can use %r in restore_command
        ctl = PgControlData(self.cf.getfile("slave_bin", ""), data_dir, True)
        if ctl.pg_version > 830:
            self.log.debug('pg_version is %s, adding %%r to restore command', ctl.pg_version)
            restore_command = 'xrestore %f "%p" %r'
        else:
            if not ctl.is_valid:
                self.log.warning('unable to run pg_controldata, assuming pre 8.3 environment')
            else:
                self.log.debug('using pg_controldata to determine restart points')
            restore_command = 'xrestore %f "%p"'

        conf = "restore_command = '%s %s %s'\n" % (self.script, cf_file, restore_command)

        # do we have streaming replication (hot standby)
        primary_conninfo = self.cf.get("primary_conninfo", "")
        if primary_conninfo:
            conf += "standby_mode = 'on'\n"
            conf += "trigger_file = '%s'\n" % os.path.join(self.cf.getfile("completed_wals"), "STOP")
            conf += "primary_conninfo = '%s'\n" % primary_conninfo
            conf += "archive_cleanup_command = '%s %s %%r'\n" % \
                (os.path.join(self.cf.getfile("slave_bin"), "pg_archivecleanup"),
                self.cf.getfile("completed_wals"))

        self.log.info("Write %s", rconf)
        if self.not_really:
            print conf
        else:
            f = open(rconf, "w")
            f.write(conf)
            f.close()

        # remove stopfile on slave
        if not self.is_master:
            stopfile = os.path.join(self.cf.getfile("completed_wals"), "STOP")
            if os.path.isfile(stopfile):
                self.log.info("Removing stopfile: %s", stopfile)
                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.
            if restore_config:
                self.slave_restore_config()

            # run database in recovery mode
            self.log.info("Starting postmaster: %s", start_cmd)
            self.exec_system(start_cmd)
        else:
            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 restore_database ( self , restore_config = True ) : setname = len ( self . args ) > 0 and self . args [ 0 ] or None altdst = len ( self . args ) > 1 and self . args [ 1 ] or None if not self . is_master : data_dir = self . cf . getfile ( "slave_data" ) stop_cmd = self . cf . getfile ( "slave_stop_cmd" , "" ) start_cmd = self . cf . getfile ( "slave_start_cmd" ) pidfile = os . path . join ( data_dir , "postmaster.pid" ) else : if not setname or not altdst : die ( 1 , "Source and target directories must be specified if running on master node." ) data_dir = altdst stop_cmd = None pidfile = None if setname : full_dir = os . path . join ( self . cf . getfile ( "walmgr_data" ) , setname ) else : full_dir = self . cf . getfile ( "full_backup" ) # stop postmaster if ordered if stop_cmd and os . path . isfile ( pidfile ) : self . log . info ( "Stopping postmaster: %s" , stop_cmd ) self . exec_system ( stop_cmd ) time . sleep ( 3 ) # is it dead? if pidfile and os . path . isfile ( pidfile ) : self . log . info ( "Pidfile exists, checking if process is running." ) if self . signal_postmaster ( data_dir , 0 ) : self . log . fatal ( "Postmaster still running.  Cannot continue." ) sys . exit ( 1 ) # find name for data backup i = 0 while 1 : bak = "%s.%d" % ( data_dir . rstrip ( "/" ) , i ) if not os . path . isdir ( bak ) : break i += 1 if self . is_master : print >> sys . stderr , "About to restore to directory %s. The postgres cluster should be shut down." % data_dir if not yesno ( "Is postgres shut down on %s ?" % data_dir ) : die ( 1 , "Shut it down and try again." ) if not self . is_master : createbackup = True elif os . path . isdir ( data_dir ) : createbackup = yesno ( "Create backup of %s?" % data_dir ) else : # nothing to back up createbackup = False # see if we have to make a backup of the data directory backup_datadir = self . cf . getboolean ( 'backup_datadir' , True ) if os . path . isdir ( data_dir ) and not backup_datadir : self . log . warning ( 'backup_datadir is disabled, deleting old data dir' ) shutil . rmtree ( data_dir ) if not setname and os . path . isdir ( data_dir ) and backup_datadir : # compatibility mode - restore without a set name and data directory exists self . log . info ( "Data directory already exists, moving it out of the way." ) createbackup = True # move old data away if createbackup and os . path . isdir ( data_dir ) : self . log . info ( "Move %s to %s" , data_dir , bak ) if not self . not_really : os . rename ( data_dir , bak ) # move new data, copy if setname specified self . log . info ( "%s %s to %s" , setname and "Copy" or "Move" , full_dir , data_dir ) if self . cf . getfile ( 'slave_pg_xlog' , '' ) : link_xlog_dir = True exclude_pg_xlog = '--exclude=pg_xlog' else : link_xlog_dir = False exclude_pg_xlog = '' if not self . not_really : if not setname and not link_xlog_dir : os . rename ( full_dir , data_dir ) else : rsync_args = [ "--delete" , "--no-relative" , "--exclude=pg_xlog/*" ] if exclude_pg_xlog : rsync_args . append ( exclude_pg_xlog ) rsync_args += [ os . path . join ( full_dir , "" ) , data_dir ] self . exec_rsync ( rsync_args , True ) if link_xlog_dir : os . symlink ( self . cf . getfile ( 'slave_pg_xlog' ) , "%s/pg_xlog" % data_dir ) if ( self . is_master and createbackup and os . path . isdir ( bak ) ) : # restore original xlog files to data_dir/pg_xlog # symlinked directories are dereferenced self . exec_cmd ( [ "cp" , "-rL" , "%s/pg_xlog/" % full_dir , "%s/pg_xlog" % data_dir ] ) else : # create an archive_status directory xlog_dir = os . path . join ( data_dir , "pg_xlog" ) archive_path = os . path . join ( xlog_dir , "archive_status" ) if not os . path . exists ( archive_path ) : os . mkdir ( archive_path , 0700 ) 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 ) cfdst = os . path . join ( data_dir , cf ) if os . path . exists ( cfdst ) : self . log . info ( "Already exists: %s" , cfdst ) elif os . path . exists ( cfsrc ) : self . log . debug ( "Copy %s to %s" , cfsrc , cfdst ) if not self . not_really : copy_conf ( cfsrc , cfdst ) # re-link tablespaces spc_dir = os . path . join ( data_dir , "pg_tblspc" ) tmp_dir = os . path . join ( data_dir , "tmpspc" ) if not os . path . isdir ( spc_dir ) : # 8.3 requires its existence os . mkdir ( spc_dir ) if os . path . isdir ( tmp_dir ) : self . log . info ( "Linking tablespaces to temporary location" ) # don't look into spc_dir, thus allowing # user to move them before.  re-link only those # that are still in tmp_dir list = os . listdir ( tmp_dir ) list . sort ( ) for d in list : if d [ 0 ] == "." : continue link_loc = os . path . abspath ( os . path . join ( spc_dir , d ) ) link_dst = os . path . abspath ( os . path . join ( tmp_dir , d ) ) self . log . info ( "Linking tablespace %s to %s" , d , link_dst ) if not self . not_really : if os . path . islink ( link_loc ) : os . remove ( link_loc ) os . symlink ( link_dst , link_loc ) # write recovery.conf rconf = os . path . join ( data_dir , "recovery.conf" ) cf_file = os . path . abspath ( self . cf . filename ) # determine if we can use %r in restore_command ctl = PgControlData ( self . cf . getfile ( "slave_bin" , "" ) , data_dir , True ) if ctl . pg_version > 830 : self . log . debug ( 'pg_version is %s, adding %%r to restore command' , ctl . pg_version ) restore_command = 'xrestore %f "%p" %r' else : if not ctl . is_valid : self . log . warning ( 'unable to run pg_controldata, assuming pre 8.3 environment' ) else : self . log . debug ( 'using pg_controldata to determine restart points' ) restore_command = 'xrestore %f "%p"' conf = "restore_command = '%s %s %s'\n" % ( self . script , cf_file , restore_command ) # do we have streaming replication (hot standby) primary_conninfo = self . cf . get ( "primary_conninfo" , "" ) if primary_conninfo : conf += "standby_mode = 'on'\n" conf += "trigger_file = '%s'\n" % os . path . join ( self . cf . getfile ( "completed_wals" ) , "STOP" ) conf += "primary_conninfo = '%s'\n" % primary_conninfo conf += "archive_cleanup_command = '%s %s %%r'\n" % ( os . path . join ( self . cf . getfile ( "slave_bin" ) , "pg_archivecleanup" ) , self . cf . getfile ( "completed_wals" ) ) self . log . info ( "Write %s" , rconf ) if self . not_really : print conf else : f = open ( rconf , "w" ) f . write ( conf ) f . close ( ) # remove stopfile on slave if not self . is_master : stopfile = os . path . join ( self . cf . getfile ( "completed_wals" ) , "STOP" ) if os . path . isfile ( stopfile ) : self . log . info ( "Removing stopfile: %s" , stopfile ) 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. if restore_config : self . slave_restore_config ( ) # run database in recovery mode self . log . info ( "Starting postmaster: %s" , start_cmd ) self . exec_system ( start_cmd ) else : self . log . info ( "Data files restored, recovery.conf created." ) self . log . info ( "postgresql.conf and additional WAL files may need to be restored manually." )