def blockcheck(self): info("ACTION: Validating database for corruptions") # The following command will introduce some corruption to test database validation # check_call(['dd','if=/dev/urandom','of=/nfs/autorestore/mnt/data_D-ORCL_I-1373437895_TS-SOE_FNO-5_0sqov4pv','bs=8192','count=10','seek=200','conv=notrunc' ]) try: self._exec_rman(self._restoretemplate.get('validateblocks')) self._validatecorruption = True finally: self._exec_sqlplus(self._restoretemplate.get('showcorruptblocks'))
def delete_expired_datafilecopy(): output = exec_sqlplus(rmantemplateconfig.get('deletedatafilecopy'), silent=True) rmanscript = "" for line in output.splitlines(): if line.startswith('DELETECOPY: '): rmanscript += "%s\n" % line.strip()[12:] if rmanscript: info("- Deleting expired datafile copies") exec_rman(rmanscript)
def backup_missing_archlog(): output = exec_sqlplus(rmantemplateconfig.get('archivelogmissing'), silent=True) archlogscript = "" for line in output.splitlines(): if line.startswith('BACKUP force as copy'): archlogscript += "%s\n" % line.strip() if archlogscript: info("- Copying missing archivelogs") exec_rman( "run {\n%s\n%s\n}" % (rmantemplateconfig.get('allocatearchlogchannel'), archlogscript))
def generate_restore(): print "\n=============" info(rmantemplateconfig.get('headerrestore')) if registercatalog: info(rmantemplateconfig.get('headercatalog')) info(rmantemplateconfig.get('fullrestore')) info(rmantemplateconfig.get('restorefooter'))
def run(self): self.starttime = datetime.now() info("Starting to restore") # success = False self.clone() try: self._mount() except: self.cleanup() raise Exception('restore', 'Mount failed') self._set_parameters() self._createinitora() self._exec = OracleExec(oraclehome=Configuration.get('oraclehome', 'generic'), tnspath=os.path.join(scriptpath(), Configuration.get('tnsadmin', 'generic')), sid=self._dbparams['dbname']) # self._run_restore()
def configure(): rmanscript = '' # Create directory for archive logs if not os.path.exists(archdir): os.makedirs(archdir) # Register database in catalog if needed if registercatalog: alreadyregistered = False info("Checking from RMAN catalog if database is already registered") output = exec_sqlplus( rmantemplateconfig.get('isdbregisteredincatalog'), silent=True, header=False) for line in output.splitlines(): if line.startswith('DATABASE IS REGISTERED IN RC'): alreadyregistered = True if not alreadyregistered: rmanscript += rmantemplateconfig.get('registerdatabase') # Configure archivelog deletion policy if hasdataguard: rmanscript += "\n%s" % rmantemplateconfig.get('configdelaldg') else: rmanscript += "\n%s" % rmantemplateconfig.get('configdelalnodg') # configures rman default settings rmanscript += "\n%s" % rmantemplateconfig.get('config') info("Running RMAN configuration") exec_rman(rmanscript) info("Running additional configuration from SQL*Plus") exec_sqlplus(rmantemplateconfig.get('configfromsqlplus'))
def exec_restore(): global exitvalue restore.clone(False) print "Please execute the following command as root to mount the backup volume:" print "" print "mount -t nfs -o rw,bg,hard,nointr,rsize=32768,wsize=32768,tcp,vers=3,timeo=600 %s %s" % (restore.mountstring, restoreparams['mountpath']) print "" while ask_yn("Did you execute it") == "N": print "Please execute it then." # Verify that clone is mounted autorestorefile = os.path.join(restoreparams['mountpath'], 'autorestore.cfg') if not os.path.isfile(autorestorefile): print "The mounted path does not look correct, file %s not found" % autorestorefile exitvalue = 1 return # debug("Oracle home: %s" % Configuration.get("oraclehome", "generic")) debug("Clone mount path: %s" % restoreparams['mountpath']) debug("Target instance SID: %s" % restoreparams['sid']) debug("Restore target time UTC: %s" % restoreparams['timepoint'].astimezone(pytz.utc)) debug("Restore target time local: %s" % restoreparams['timepoint'].astimezone(get_localzone())) debug("Restored from snapshot: %s" % restore.sourcesnapid) info("Starting database restore") # try: restore.pit_restore(restoreparams['mountpath'], restoreparams['sid']) restore.verify(False) info("Database restore complete") info("SID: %s" % restoreparams['sid']) info("Requested target time: %s" % restoreparams['timepoint'].astimezone(get_localzone())) info("Verified restored database time: %s" % restore.verifytime) info("Difference from target: %s" % restore.verifydiff) except: exception("Database restore failed") exitvalue = 1 print "" print "Commands to clean up:" print "1. Shut down database instance %s" % restoreparams['sid'] print "2. Execute as root: umount %s" % restoreparams['mountpath'] print "3. Drop clone: BACKUPCONFIG=%s %s %s dropclone %s" % (os.path.basename(Configuration.configfilename), os.path.join(scriptpath(), 'zsnapper.py'), configname, restore.clonename)
def runrestore(database): global exitstatus # Configuration.defaultsection = database # Reinitialize logging BackupLogger.init( os.path.join( logdir, "%s-%s.log" % (datetime.now().strftime('%Y%m%dT%H%M%S'), database)), database) BackupLogger.clean() # restore = RestoreDB(database) restore.set_mount_path(mountdest) restore.set_restore_path(restoredest) # info("Logfile: %s" % BackupLogger.logfile) Configuration.substitutions.update({'logfile': BackupLogger.logfile}) cleantarget() # success = False # if validatemodulus > 0: # Validation based on modulus validationinfo = validationdate(database) validatecorruption = validationinfo[0] if not validatecorruption: debug("Next database validation in %d days: %s" % (validationinfo[1], validationinfo[2])) else: # Validation based on random validatecorruption = (validatechance > 0) and (randint( 1, validatechance) == validatechance) if validatecorruption: debug("Database will be validated during this restore session") # Start restore try: restore.run() restore.verify() if validatecorruption: restore.blockcheck() success = True except: exitstatus = 1 exception( "Error happened, but we can continue with the next database.") finally: restore.cleanup() # Log result to catalog Configuration.substitutions.update({ 'log_dbname': database, 'log_start': restore.starttime.strftime('%Y-%m-%d %H-%M-%S'), 'log_stop': restore.endtime.strftime('%Y-%m-%d %H-%M-%S'), 'log_success': '1' if success else '0', 'log_diff': restore.verifyseconds, 'log_snapid': restore.sourcesnapid, 'log_validated': '1' if validatecorruption else '0' }) debug('Logging the result to catalog.') try: oexec.sqlldr(Configuration.get('autorestorecatalog', 'autorestore'), restoretemplate.get('sqlldrlog')) except: debug("Sending the logfile to catalog failed.") try: oexec.sqlplus(restoretemplate.get('insertlog'), silent=False) except: debug("Logging the result to catalog failed.") # Finish up info("Restore %s, elapsed time: %s" % ('successful' if success else 'failed', restore.endtime - restore.starttime)) BackupLogger.close(True)
print "Validation is based on chance, probability 1/%d" % validatechance else: print "Database validation is not turned on" else: # Actions that need a lock lock = BackupLock(Configuration.get('autorestorelogdir', 'autorestore')) try: if action is not None: if action.startswith('--'): if action == '--createcatalog': BackupLogger.init( os.path.join( logdir, "%s-config.log" % (datetime.now().strftime('%Y%m%dT%H%M%S'))), 'config') info("Logfile: %s" % BackupLogger.logfile) Configuration.substitutions.update( {'logfile': BackupLogger.logfile}) oexec.sqlplus(restoretemplate.get('createcatalog'), silent=False) else: runrestore(action) else: # Loop through all sections for configname in loopdatabases(): runrestore(configname) # Run ADRCI to clean up diag adrage = int(Configuration.get('logretention', 'generic')) * 1440 f1 = mkstemp(suffix=".adi") ftmp = os.fdopen(f1[0], "w") ftmp.write("set base %s\n" % logdir)
def runrestore(database): global exitstatus # Configuration.defaultsection = database # restoredest = Configuration.get('autorestoredestination', 'autorestore') mountdest = Configuration.get('autorestoremountpoint', 'autorestore') logdir = Configuration.get('autorestorelogdir', 'autorestore') Configuration.substitutions.update({ 'logdir': logdir, 'autorestorecatalog': Configuration.get('autorestorecatalog', 'autorestore') }) if restoredest is None or not os.path.exists( restoredest) or not os.path.isdir(restoredest): print "Restore directory %s not found or is not a proper directory" % restoredest sys.exit(2) if mountdest is None or not os.path.exists(mountdest) or not os.path.isdir( mountdest): print "Clone mount directory %s not found or is not a proper directory" % mountdest sys.exit(2) # validatechance = int( Configuration.get('autorestorevalidatechance', 'autorestore')) validatemodulus = int( Configuration.get('autorestoremodulus', 'autorestore')) # Reinitialize logging BackupLogger.init( os.path.join( logdir, "%s-%s.log" % (datetime.now().strftime('%Y%m%dT%H%M%S'), database)), database) BackupLogger.clean() # restore = RestoreDB(database) restore.set_mount_path(mountdest) restore.set_restore_path(restoredest) # info("Logfile: %s" % BackupLogger.logfile) Configuration.substitutions.update({'logfile': BackupLogger.logfile}) cleantarget(restoredest) # success = False # if validatemodulus > 0: # Validation based on modulus validationinfo = validationdate(database) validatecorruption = validationinfo[0] if not validatecorruption: debug("Next database validation in %d days: %s" % (validationinfo[1], validationinfo[2])) else: # Validation based on random validatecorruption = (validatechance > 0) and (randint( 1, validatechance) == validatechance) if validatecorruption: debug("Database will be validated during this restore session") # Start restore try: restore.run() restore.verify() if validatecorruption: restore.blockcheck() success = True except: exitstatus = 1 exception( "Error happened, but we can continue with the next database.") finally: restore.cleanup() # Log result to catalog Configuration.substitutions.update({ 'log_dbname': database, 'log_start': restore.starttime.strftime('%Y-%m-%d %H-%M-%S'), 'log_stop': restore.endtime.strftime('%Y-%m-%d %H-%M-%S'), 'log_success': '1' if success else '0', 'log_diff': restore.verifyseconds, 'log_snapid': restore.sourcesnapid, 'log_validated': '1' if validatecorruption else '0' }) debug('Logging the result to catalog.') try: oexec.sqlldr(Configuration.get('autorestorecatalog', 'autorestore'), restoretemplate.get('sqlldrlog')) except: debug("Sending the logfile to catalog failed.") try: oexec.sqlplus(restoretemplate.get('insertlog'), silent=False) except: debug("Logging the result to catalog failed.") # Finish up info("Restore %s, elapsed time: %s" % ('successful' if success else 'failed', restore.endtime - restore.starttime)) # Run ADRCI to clean up diag adrage = int(Configuration.get('logretention', 'generic')) * 1440 f1 = mkstemp(suffix=".adi") ftmp = os.fdopen(f1[0], "w") ftmp.write("set base %s\n" % logdir) ftmp.write("show homes\n") ftmp.close() f2 = mkstemp(suffix=".adi") ftmp2 = os.fdopen(f2[0], "w") ftmp2.write("set base %s\n" % logdir) with TemporaryFile() as f: try: oexec.adrci(f1[1], f) f.seek(0, 0) output = f.read() startreading = False for line in output.splitlines(): if line.startswith('ADR Homes:'): startreading = True elif startreading: ftmp2.write("set home %s\n" % line.strip()) ftmp2.write("purge -age %d\n" % adrage) ftmp2.close() oexec.adrci(f2[1], f) except: print "Executing ADRCI failed." finally: os.unlink(f1[1]) os.unlink(f2[1]) # BackupLogger.close(True)
def exec_restore(): global exitvalue restore.clone(False) print "Please execute the following command as root to mount the backup volume:" print "" print "mount -t nfs -o rw,bg,hard,nointr,rsize=32768,wsize=32768,tcp,vers=3,timeo=600 %s %s" % (restore.mountstring, restoreparams['mountpath']) print "" while ask_yn("Did you execute it") == "N": print "Please execute it then." # Verify that clone is mounted autorestorefile = os.path.join(restoreparams['mountpath'], 'autorestore.cfg') if not os.path.isfile(autorestorefile): print "The mounted path does not look correct, file %s not found" % autorestorefile exitvalue = 1 return # BackupLogger.init('/tmp/restore_%s_%s.log' % (datetime.now().strftime('%Y%m%dT%H%M%S'), configname)) BackupLogger.clean() Configuration.substitutions.update({ 'logdir': '/tmp', 'logfile': BackupLogger.logfile }) print "Session log file: %s" % BackupLogger.logfile info("Starting database restore") # try: restore.pit_restore(restoreparams['mountpath'], restoreparams['sid']) restore.verify(False) info("Database restore complete") info("SID: %s" % restoreparams['sid']) info("Requested target time: %s" % restoreparams['timepoint'].astimezone(get_localzone())) info("Verified restored database time: %s" % restore.verifytime) info("Difference from target: %s" % restore.verifydiff) except: exception("Database restore failed") exitvalue = 1 print "" print "Commands to clean up:" print "1. Shut down database instance %s" % restoreparams['sid'] print "2. Execute as root: umount %s" % restoreparams['mountpath'] print "3. Drop clone: BACKUPCONFIG=%s %s %s dropclone %s" % (os.path.basename(Configuration.configfilename), os.path.join(scriptpath(), 'zsnapper.py'), configname, restore.clonename)
def imagecopywithsnap(): starttime = datetime.now() restoreparamfile = os.path.join(backupdest, 'autorestore.cfg') # info("Check if there are missing archivelogs") backup_missing_archlog() # info("Switch current log") output = exec_sqlplus(rmantemplateconfig.get('archivecurrentlogs'), silent=True) if os.path.isfile(restoreparamfile): with open(restoreparamfile, 'a') as f: for line in output.splitlines(): if line.startswith('CURRENT DATABASE SCN:'): f.write("lastscn: %s\n" % line.strip()[22:]) elif line.startswith('CURRENT DATABASE TIME:'): f.write("lasttime: %s\n" % line.strip()[23:]) elif line.startswith('BCT FILE:'): f.write("bctfile: %s\n" % line.strip()[10:]) # if dosnapshot: info("Snap the current backup area") snapid = snap.snap() debug("Created snapshot: %s" % snapid) # info("Checking for expired datafile copies") delete_expired_datafilecopy() # info("Refresh imagecopy") backup('imagecopy') exec_sqlplus(rmantemplateconfig.get('archivecurrentlogs')) # if dosnapshot: info("Clean expired snapshots") cleaningresult = snap.clean() for r in cleaningresult: debug(r['infostring']) # info("Dump additional information about the environment to the log file") if gimanaged: p = Popen([os.path.join(scriptpath, 'dbinfo.py'), configsection], stdout=PIPE, stderr=None, stdin=None) output, outerr = p.communicate() debug(output) # Write ORACLE_HOME patch information to log file p = Popen([ os.path.join(Configuration.get('oraclehome', 'generic'), 'OPatch', 'opatch'), 'lsinventory' ], stdout=PIPE, stderr=None, stdin=None) output, outerr = p.communicate() debug(output) # info("Write database parameters for autorestore") with open(restoreparamfile, 'w') as f: f.write("[dbparams]\n") output = exec_sqlplus(rmantemplateconfig.get('autorestoreparameters'), silent=True) for line in output.splitlines(): if line.startswith('dbconfig-'): f.write("%s\n" % line[9:]) # endtime = datetime.now() info("------------ TOTAL ------------") info("Total execution time: %s" % (endtime - starttime)) info("Execution started: %s" % starttime) info("Execution finished: %s" % endtime)
for line in output.splitlines(): if line.startswith('CDB-DETECT:') and line.strip() <> 'CDB-DETECT: NO': commonprefix = line.strip()[12:] Configuration.substitutions.update({'scheduleuserprefix': commonprefix}) # script = "%s\n" % rmantemplateconfig.get('createuser') script += "%s\n" % rmantemplateconfig.get('dropschedule') script += "%s\n" % rmantemplateconfig.get('createschedule') exec_sqlplus(script) ################################################ ### Main section ################################################ info("Configuration file: %s" % Configuration.configfilename) lock = BackupLock(lockdir=backupdest, maxlockwait=int(Configuration.get('maxlockwait', 'generic'))) try: # User interface action execution if scriptaction == 'config': configure() elif scriptaction == 'generaterestore': generate_restore() elif scriptaction == 'imagecopywithsnap': imagecopywithsnap() elif scriptaction == 'setschedule': setschedule() elif scriptaction == 'missingarchlog':