def set_archivelog_mode(self, status=True): """ Set archive log mode status. """ print(("Turning %s archivelog mode...\t" % (status and 'on' or 'off')), end="") sys.stdout.flush() roller = Roller() roller.start() success, failed = "done", "failed" if status: destination = os.environ['ORACLE_BASE'] + "/oradata/" + os.environ[ 'ORACLE_SID'] + "/archive" self.call_scenario('ora-archivelog-on', destination=destination) else: self.call_scenario('ora-archivelog-off') success, failed = "failed", "done" if self.get_archivelog_mode(): roller.stop(success) else: roller.stop(failed) time.sleep(1)
def vw_check_database_ready(self, message, output_shift=1): """ Check if database is ready. Otherwise crash with the given message. """ print("Checking the database:" + ("\t" * output_shift), end="") roller = Roller() roller.start() dbstatus = self.get_db_status() if dbstatus.ready: roller.stop("running") time.sleep(1) else: roller.stop("failed") time.sleep(1) raise GateException(message)
def _rst_replace_new_backup(self, backup_dst: str) -> None: """ Replace new backup. """ # Archive into a tgz backup and place it near the cluster print("Restoring from backup:\t ", end="") sys.stdout.flush() # Remove cluster in general print("Remove broken cluster:\t ", end="") sys.stdout.flush() shutil.rmtree(self.config['pcnf_pg_data']) print("finished") sys.stdout.flush() # Unarchive cluster print("Unarchiving new backup:\t ", end="") sys.stdout.flush() roller = Roller() roller.start() destination_tar = backup_dst + "/base.tar.gz" temp_dir = tempfile.mkdtemp(dir=os.path.join(backup_dst, "tmp")) pguid = pwd.getpwnam('postgres')[2] pggid = grp.getgrnam('postgres')[2] os.chown(temp_dir, pguid, pggid) tar_command = '/bin/tar xf %s --directory=%s 2>/dev/null' % ( destination_tar, temp_dir) os.system(tar_command) roller.stop("finished") time.sleep(1) print("Restore cluster:\t ", end="") backup_root = self._rst_get_backup_root(temp_dir) mv_command = '/bin/mv %s %s' % ( backup_root, os.path.dirname(self.config['pcnf_pg_data']) + "/data") os.system(mv_command) print("finished") sys.stdout.flush() print("Write recovery.conf:\t ", end="") recovery_conf = os.path.join(self.config['pcnf_pg_data'], "recovery.conf") cfg = open(recovery_conf, 'w') cfg.write("restore_command = 'cp " + backup_dst + "/%f %p'\n") cfg.close() # Set recovery.conf correct ownership (SMDBA is running as root at this moment) data_owner = get_path_owner( self.config.get('pcnf_pg_data', PgBackup.DEFAULT_PG_DATA)) os.chown(recovery_conf, data_owner.uid, data_owner.gid) print("finished") sys.stdout.flush()
def _backup_rotate(self): """ Rotate backup by purging the obsolete/expired backup set. This method is internal and needs to be performed on a healthy, ensured database. """ print("Rotating the backup:\t", end="") roller = Roller() roller.start() stdout, stderr = self.call_scenario('rman-hot-backup-roll', target='rman') if stderr: roller.stop("failed") time.sleep(1) self.to_stderr(stderr) if stdout: roller.stop("finished") time.sleep(1)
def do_db_check(self, *args, **params): # pylint: disable=W0613 """ Check full connection to the database. """ print("Checking connection:\t", end="") sys.stdout.flush() roller = Roller() roller.start() login = '******' % (self.config.get('db_user'), self.config.get('db_password'), self.config.get('db_name')) roller.stop( self.get_db_status(login=login).ready and "ready" or "not available") time.sleep(1)
def do_stats_refresh(self, *args, **params): # pylint: disable=W0613 """ Gather statistics on SUSE Manager database objects. """ self.vw_check_database_ready( "Database must be healthy and running in order to get statistics of it!" ) print("Gathering statistics on SUSE Manager database...\t", end="") roller = Roller() roller.start() stdout, stderr = self.call_scenario('gather-stats', owner=self.config.get( 'db_user', '').upper()) if stdout and stdout.strip() == 'done': roller.stop('finished') else: roller.stop('failed') time.sleep(1) self.to_stderr(stderr)
def _rst_save_current_cluster(self) -> None: """ Save current tablespace """ old_data_dir = os.path.dirname(self.config['pcnf_pg_data']) + '/data.old' if not os.path.exists(old_data_dir): os.mkdir(old_data_dir) print("Created \"%s\" directory." % old_data_dir) print("Moving broken cluster:\t ", end="") sys.stdout.flush() roller = Roller() roller.start() suffix = '-'.join([str(el).zfill(2) for el in iter(time.localtime())][:6]) destination_tar = old_data_dir + "/data." + suffix + ".tar.gz" tar_command = '/bin/tar -czPf %s %s 2>/dev/null' % (destination_tar, self.config['pcnf_pg_data']) os.system(tar_command) roller.stop("finished") time.sleep(1) sys.stdout.flush()
def do_db_stop(self, *args, **params): # pylint: disable=W0613 """ Stop SUSE Manager database. """ print("Stopping the SUSE Manager database...") sys.stdout.flush() print("Stopping listener:\t", end="") sys.stdout.flush() roller = Roller() roller.start() dbstatus = self.get_status() if dbstatus.ready: self.do_listener_stop(*['quiet']) roller.stop("done") time.sleep(1) else: roller.stop("not running") time.sleep(1) print("Stopping core:\t\t", end="") sys.stdout.flush() roller = Roller() roller.start() dbstatus = self.get_db_status() if not dbstatus.ready: roller.stop("failed") time.sleep(1) raise GateException("Error: database core is already offline.") _, stderr = self.syscall("sudo", "-u", "oracle", self.ora_home + "/bin/dbshut") if stderr: roller.stop("failed") time.sleep(1) else: roller.stop("done") time.sleep(1) self.to_stderr(stderr)
def do_db_start(self, *args, **params): # pylint: disable=W0613 """ Start SUSE Manager database. """ print("Starting listener:\t", end="") sys.stdout.flush() roller = Roller() roller.start() dbstatus = self.get_status() if dbstatus.ready: roller.stop('failed') time.sleep(1) raise GateException("Error: listener is already running") self.do_listener_start('quiet') roller.stop('done') time.sleep(1) print("Starting core...\t", end="") sys.stdout.flush() roller = Roller() roller.start() stdout, stderr = self.syscall("sudo", "-u", "oracle", self.ora_home + "/bin/dbstart") roller.stop('done') time.sleep(1) self.to_stderr(stderr) if stdout and stdout.find("Database opened") > -1 and stdout.find( "Database mounted") > -1: roller.stop('done') time.sleep(1) else: roller.stop('failed') time.sleep(1) eprint("Output dump:") eprint(stdout) if stderr: eprint("Error dump:") eprint(stderr)
def do_space_reclaim(self, *args, **params): # pylint: disable=W0613 """ Free disk space from unused object in tables and indexes. """ self.vw_check_database_ready( "Database must be healthy and running in order to reclaim the used space!" ) print("Examining the database...\t", end="") roller = Roller() roller.start() self.call_scenario('shrink-segments-advisor') stderr = None if stderr: roller.stop('failed') time.sleep(1) self.to_stderr(stderr) else: roller.stop('done') time.sleep(1) print("Gathering recommendations...\t", end="") roller = Roller() roller.start() # get the recomendations stdout, stderr = self.call_scenario('recomendations') if stdout: roller.stop("done") time.sleep(1) elif stderr: roller.stop("failed") time.sleep(1) eprint("Error dump:") eprint(stderr) else: roller.stop("finished") time.sleep(1) print("\nNo space reclamation possible at this time.\n") return messages = { 'TABLE': 'Tables', 'INDEX': 'Indexes', 'AUTO': 'Recommended segments', 'MANUAL': 'Non-shrinkable tablespace', } tree = {} wseg = 0 if stdout: lines = [ tuple(filter(None, line.strip().replace("\t", " ").split(" "))) for line in stdout.strip().split("\n") ] for ssm, sname, rspace, tsn, stype in lines: tsns = tree.get(tsn, {}) stypes = tsns.get(stype, {}) ssms = stypes.get(ssm, []) ssms.append(( sname, int(rspace), )) wseg = len(sname) if len(sname) > wseg else wseg stypes[ssm] = ssms tsns[stype] = stypes tree[tsn] = tsns total = 0 for tsn in tree: print("\nTablespace:", tsn) for obj in tree[tsn].keys(): print("\n\t" + messages.get(obj, "Object: " + obj)) for stype in tree[tsn][obj].keys(): typetotal = 0 print("\t" + messages.get(stype, "Type: " + stype)) for segment, size in tree[tsn][obj][stype]: print("\t\t", (segment + ((wseg - len(segment)) * " ")), "\t", '%.2fM' % (size / 1024. / 1024.)) total += size typetotal += size total_message = "Total " + messages.get(obj, '').lower() print("\n\t\t", (total_message + ((wseg - len(total_message)) * " ")), "\t", '%.2fM' % (typetotal / 1024. / 1024.)) print("\nTotal reclaimed space: %.2fGB" % (total / 1024. / 1024. / 1024.)) # Reclaim space if tree: for tsn in tree: for obj in tree[tsn]: if tree[tsn][obj].get('AUTO', None): print("\nReclaiming space on %s:" % messages[obj].lower()) for segment, size in tree[tsn][obj]['AUTO']: print("\t", segment + "...\t", end="") sys.stdout.flush() stdout, stderr = self.syscall( "sudo", "-u", "oracle", "/bin/bash", input=self.get_scenario_template().replace( '@scenario', self.__get_reclaim_space_statement( segment, obj))) if stderr: print("failed") eprint(stderr) else: print("done") print("Reclaiming space finished")
def do_stats_overview(self, *args, **params): # pylint: disable=W0613 """ Show tables with stale or empty statistics. """ self.vw_check_database_ready( "Database must be healthy and running in order to get stats overview!" ) print("Preparing data:\t\t", end="") roller = Roller() roller.start() stdout, stderr = self.call_scenario('stats', owner=self.config.get( 'db_user', '').upper()) roller.stop('finished') time.sleep(1) self.to_stderr(stderr) stale = [] empty = [] if stdout: segment = None for line in stdout.strip().split("\n"): if line.find('stale objects') > -1: segment = 'stale' continue elif line.find('empty objects') > -1: segment = 'empty' continue line = line.split(" ")[-1].strip() if segment and segment == 'stale': stale.append(line) elif segment and segment == 'empty': empty.append(line) else: print("Ignoring", repr(line)) if stale: print("\nList of stale objects:") for obj in stale: print("\t", obj) print("\nFound %s stale objects\n" % len(stale)) else: print("No stale objects found") if empty: print("\nList of empty objects:") for obj in empty: print("\t", obj) print("\nFound %s objects that currently have no statistics.\n" % len(stale)) else: print("No empty objects found.") if stderr: eprint("Error dump:") eprint(stderr)
def do_backup_restore(self, *args, **params): """ Restore the SUSE Manager database from backup. @help force\t\t\tShutdown database prior backup, if running. start\t\t\tAttempt to start a database after restore. --strategy=<value>\tManually force strategry 'full' or 'partial'. Don't do that. """ dbid = self.get_dbid() scenario = { 'full': 'rman-recover-ctl', 'partial': 'rman-recover', } # Control file still around? strategy = None if params.get("strategy") in ['full', 'partial']: strategy = params.get("strategy") elif params.get("strategy") is not None: raise GateException("Unknown value {} for option 'strategy'. " "Please read 'help' first.".format( params.get("strategy"))) if not strategy: strategy = "full" db_path = os.environ['ORACLE_BASE'] + "/oradata/" + os.environ[ 'ORACLE_SID'] for fname in os.listdir(db_path): if fname.lower().endswith(".ctl"): strategy = "partial" break print(("Restoring the SUSE Manager Database using %s strategy" % strategy)) # Could be database just not cleanly killed # In this case great almighty RMAN won't connect at all and just crashes. :-( self.do_db_start() self.do_db_stop() print("Preparing database:\t", end="") roller = Roller() roller.start() dbstatus = self.get_db_status() if dbstatus.ready: if 'force' in args: roller.stop("running") time.sleep(1) self.do_db_stop() else: roller.stop("failed") time.sleep(1) raise GateException( "Database must be put offline. Or use options (run \"help\" for this procedure)." ) else: roller.stop("success") time.sleep(1) print("Restoring from backup:\t", end="") roller = Roller() roller.start() stdout, stderr = self.call_scenario(scenario[strategy], target='rman', dbid=str(dbid)) if stderr: roller.stop("failed") time.sleep(1) self.to_stderr(stderr) if stdout: roller.stop("finished") time.sleep(1) self.do_db_stop() if 'start' in args: self.do_db_start() self.do_listener_status()
def do_backup_hot(self, *args, **params): # pylint: disable=W0613 """ Perform hot backup on running database. """ self.vw_check_database_ready( "Database must be healthy and running in order to take a backup of it!" ) # Check DBID is around all the time (when DB is healthy!) self.get_dbid(known_db_status=True) if not self.get_archivelog_mode(): raise GateException( "Archivelog is not turned on.\n\tPlease shutdown SUSE Manager and run system-check first!" ) print("Backing up the database:\t", end="") roller = Roller() roller.start() stdout, stderr = self.call_scenario('rman-hot-backup', target='rman') if stderr: roller.stop("failed") time.sleep(1) self.to_stderr(stderr) if stdout: roller.stop("finished") time.sleep(1) files = [] arclogs = [] for line in stdout.split("\n"): line = line.strip() if line.startswith("input") and line.find('datafile') > -1: files.append(line.split("name=")[-1]) elif line.startswith("archived"): arclogs.append(line.split("name=")[-1].split(" ")[0]) print("Data files archived:") for fname in files: print("\t" + fname) print() print("Archive logs:") for arc in arclogs: print("\t" + arc) print() # Rotate and check self.autoresolve_backup() self._backup_rotate() # Finalize hbk, fbk, harch, farch = self.check_backup_info() print("Backup summary as follows:") if hbk: print("\tBackups:") for bkp in hbk: print("\t\t", bkp.handle) print() if harch: print("\tArchive logs:") for bkp in harch: print("\t\t", bkp.handle) print() if fbk: eprint("WARNING! Broken backups has been detected:") for bkp in fbk: eprint("\t\t", bkp.handle) eprint() if farch: eprint("WARNING! Broken archive logs has been detected:") for bkp in farch: eprint("\t\t", bkp.handle) eprint() print("\nFinished.")
def do_backup_purge(self, *args, **params): # pylint: disable=W0613 """ Purge all backups. Useful after successfull reliable recover from the disaster. """ self.vw_check_database_ready( "Database must be healthy and running in order to purge assigned backups of it!" ) print("Checking backups:\t", end="") roller = Roller() roller.start() info = self.get_backup_info() if not info: roller.stop("failed") time.sleep(1) eprint("No backup snapshots available.") sys.exit(1) roller.stop("finished") time.sleep(1) print("Removing %s backup%s:\t" % (len(info), len(info) > 1 and 's' or ''), end="") roller = Roller() roller.start() _, stderr = self.call_scenario('rman-backup-purge', target='rman') if stderr: roller.stop("failed") time.sleep(1) self.to_stderr(stderr) roller.stop("finished") time.sleep(1)
def do_backup_list(self, *args, **params): # pylint: disable=W0613 """ List of available backups. """ self.vw_check_database_ready("Database must be running and ready!", output_shift=2) roller = Roller() roller.start() print("Getting available backups:\t", end="") infoset = [] stdout, stderr = self.call_scenario('rman-list-backups', target='rman') self.to_stderr(stderr) roller.stop("finished") time.sleep(1) if stdout: for chunk in filter(None, [ re.sub('=+', '', c).strip() for c in stdout.split("\n=")[-1].split('BS Key') ]): try: info = InfoNode() info.files = [] piece_chnk, files_chnk = chunk.split('List of Datafiles') # Get backup place for line in [l.strip() for l in piece_chnk.split("\n")]: if line.lower().startswith('piece name'): info.backup = line.split(" ")[-1] if line.lower().find('status') > -1: status_line = list( filter( None, line.replace( ':', '').split("Status")[-1].split(" "))) if len(list(status_line)) == 5: info.status = status_line[0] info.compression = status_line[2] info.tag = status_line[4] # Get the list of files for line in [l.strip() for l in files_chnk.split("\n")]: if line.startswith('-'): continue else: line = list(filter(None, line.split(" "))) if len(list(line)) > 4: if line[0] == 'File': continue dbf = InfoNode() dbf.type = line[1] dbf.file = line[-1] dbf.date = line[-2] info.files.append(dbf) infoset.append(info) except Exception: eprint("No backup snapshots available.") sys.exit(1) # Display backup data if infoset: print("Backups available:\n") for info in infoset: print("Name:\t", info.backup) print("Files:") for dbf in info.files: print("\tType:", dbf.type, end="") print(sys.stdout, "\tDate:", dbf.date, end="") print("\tFile:", dbf.file) print()