def do_space_reclaim(self, **args: str) -> None: # pylint: disable=W0613 """ Free disk space from unused objects in tables and/or indexes """ print("Examining database...\t", end="") sys.stdout.flush() if not self._get_db_status(): time.sleep(1) raise GateException("Database must be online.") eprint("finished") time.sleep(1) operations = [ ('Analyzing database', 'vacuum analyze;'), ('Reclaiming space', 'cluster;'), ] for msg, operation in operations: print("%s...\t" % msg, end="") sys.stdout.flush() _, stderr = self.syscall("sudo", "-u", "postgres", "/bin/bash", input=self.get_scenario_template(target='psql').replace('@scenario', operation)) if stderr: eprint("failed") sys.stdout.flush() eprint(stderr) raise GateException("Unhandled underlying error occurred, see above.") print("done") sys.stdout.flush()
def check(self) -> bool: """ Check system requirements for this gate. """ msg = None minversion = [9, 6] pg_version = os.popen('/usr/bin/postmaster --version').read().strip().split(' ')[-1].split('.') if int(pg_version[0]) < minversion[0] or (int(pg_version[0]) == minversion[0] and int(pg_version[1]) < minversion[1]): raise GateException("Core component is too old version.") if not os.path.exists("/etc/sysconfig/postgresql"): raise GateException("Custom database component? Please strictly use SUSE components only!") if not os.path.exists("/usr/bin/psql"): msg = 'operations' elif not os.path.exists("/usr/bin/postmaster"): msg = 'database' elif not os.path.exists("/usr/bin/pg_ctl"): msg = 'control' elif not os.path.exists("/usr/bin/pg_basebackup"): msg = 'backup' if msg: raise GateException("Cannot find required %s component." % msg) # Prevent running this tool within the PostgreSQL data directory # See bsc#1024058 for details if self.config["pcnf_pg_data"].strip('/') in os.path.abspath("."): raise GateException("Please do not call SMDBA inside the '{0}' directory.".format(os.path.abspath("."))) return True
def check(self): """ Check system requirements for this gate. """ if not os.path.exists(self.ora_home + "/bin/sqlplus"): raise GateException( "Cannot find operation sub-component, required for the gate.") if not os.path.exists(self.ora_home + "/bin/rman"): raise GateException( "Cannot find backup sub-component, required for the gate.") return True
def _perform_archive_operation(**args: str) -> None: """ Performs an archive operation. """ if not args.get('source'): raise GateException("Source file was not specified!") if not os.path.exists(args.get('source', "")): raise GateException("File \"%s\" does not exists." % args.get('source')) if os.path.exists(args.get('backup-dir', "")): raise GateException("Destination file \"%s\"already exists." % args.get('backup-dir')) shutil.copy2(args.get('source', ""), args.get('backup-dir', ""))
def get_dbid(self, path=None, known_db_status=False): """ Get DBID and save it. """ # Get DBID from the database and save it. # If database is broken, get last saved. if not path: path = os.environ['ORACLE_BASE'] + "/smdba" if not os.path.exists(path): os.makedirs(path) # Add full filename path = self.HELPER_CONF % path stdout, _ = self.call_scenario('ora-dbid') dbid = None if stdout: try: dbid = int(stdout.split("\n")[-1]) except Exception: # Failed to get dbid anyway, let's just stay silent for now. if known_db_status: raise GateException( "The data in the database is not reachable!") if dbid: fgh = open(path, 'w') fgh.write("# Database ID of \"%s\", please don't lose it ever.\n") fgh.write(os.environ['ORACLE_SID'] + ".dbid=%s\n" % dbid) fgh.close() elif os.path.exists(path): for line in open(path).readlines(): line = line.strip() if not line or line.startswith('#') or (line.find('=') == -1): continue _, dbid = map(lambda el: el.strip(), line.split('=', 1)) if dbid: try: dbid = int(dbid) except Exception: # Failed get dbid again. pass if not dbid: raise GateException( "Looks like your backups was never taken with the SMDBA." "\n\tGood luck with the tools you used before!") return dbid
def do_space_tables(self, **args: str) -> None: # pylint: disable=W0613 """ Show space report for each table """ stdout, stderr = self.call_scenario('pg-tablesizes', target='psql') if stderr: eprint(stderr) raise GateException("Unhandled underlying error occurred, see above.") if stdout: t_index = [] t_ref = {} t_total = 0 longest = 0 for line in stdout.strip().split("\n")[2:]: line = list(filter(None, map(lambda el: el.strip(), line.split('|')))) # type: ignore if len(line) == 3: t_name, t_size_pretty, t_size = line[0], line[1], int(line[2]) t_ref[t_name] = t_size_pretty t_total += t_size t_index.append(t_name) longest = len(t_name) if len(t_name) > longest else longest t_index.sort() table = [('Table', 'Size',)] for name in t_index: table.append((name, t_ref[name],)) table.append(('', '',)) table.append(('Total', ('%.2f' % round(t_total / 1024. / 1024)) + 'M',)) print("\n", TablePrint(table), "\n")
def get_backup_info(self): """ Return list of BackupInfo objects, representing backups. """ stdout, stderr = self.call_scenario('rman-backup-info', target='rman') if stderr: eprint("Backup information listing failure:") eprint(stderr) raise GateException("Unable to get information about backups.") capture = False idx = [] info = {} for line in stdout.split("\n"): line = line.strip() if line.startswith("---"): # Table delimeter capture = True continue if capture: if not line: capture = False continue tkn = list(filter(None, line.replace("\t", " ").split(" "))) info[tkn[5]] = BackupInfo(tkn[0], tkn[5], tkn[-1]) idx.append(tkn[5]) return [info[bid] for bid in reversed(sorted(idx))]
def do_db_stop(self, **args: str) -> None: # pylint: disable=W0613 """ Stop the SUSE Manager Database """ print("Stopping database...\t", end="") sys.stdout.flush() if not self._get_db_status(): print("failed") time.sleep(1) return # Stop the db if not self.config.get('pcnf_data_directory'): raise GateException("Cannot find data directory.") cwd = os.getcwd() os.chdir(self.config.get('pcnf_data_directory', '/var/lib/pgsql')) if self._with_systemd: result = os.system('systemctl stop postgresql.service') else: # TODO: This is obsolete code, going to be removed after 2.1 EOL result = os.system( "sudo -u postgres /usr/bin/pg_ctl stop -s -D %s -m fast" % self.config.get('pcnf_data_directory', '')) print(result and "failed" or "done") os.chdir(cwd) # Cleanup self._cleanup_pids()
def _perform_enable_backups(self, **args: str) -> None: """ Turn backups on or off. """ enable = args.get('enable', 'off') conf_path = self.config['pcnf_pg_data'] + "/postgresql.conf" conf = self._get_conf(conf_path) backup_dir: str = args.get('backup-dir', "") if enable == 'on': # Enable backups if not self._get_db_status(): self.do_db_start() if not self._get_db_status(): raise GateException("Cannot start the database!") if not os.path.exists(backup_dir): os.system('sudo -u postgres /bin/mkdir -p -m 0700 %s' % backup_dir) # first write the archive_command and restart the db # if we create the base backup after this, we prevent a race conditions # and do not lose archive logs cmd = "'" + "/usr/bin/smdba-pgarchive --source \"%p\" --destination \"" + backup_dir + "/%f\"'" if conf.get('archive_command', '') != cmd: conf['archive_command'] = cmd self._write_conf(conf_path, **conf) self._apply_db_conf() # round robin of base backups if os.path.exists(backup_dir + "/base.tar.gz"): if os.path.exists(backup_dir + "/base-old.tar.gz"): os.remove(backup_dir + "/base-old.tar.gz") os.rename(backup_dir + "/base.tar.gz", backup_dir + "/base-old.tar.gz") b_dir_temp = os.path.join(backup_dir, 'tmp') cwd = os.getcwd() os.chdir(self.config.get('pcnf_data_directory', '/var/lib/pgsql')) os.system('sudo -u postgres /usr/bin/pg_basebackup -D {0}/ -Ft -c fast -X fetch -v -P -z'.format(b_dir_temp)) os.chdir(cwd) if os.path.exists("{0}/base.tar.gz".format(b_dir_temp)): os.rename("{0}/base.tar.gz".format(b_dir_temp), "{0}/base.tar.gz".format(backup_dir)) # Cleanup/rotate backup PgBackup(backup_dir, pg_data=self.config.get('pcnf_data_directory', '/var/lib/pgsql')).cleanup_backup() else: # Disable backups if enable == 'purge' and os.path.exists(backup_dir): print("INFO: Removing the whole backup tree \"%s\"" % backup_dir) shutil.rmtree(backup_dir) cmd = "'/bin/true'" if conf.get('archive_command', '') != cmd: conf['archive_command'] = cmd self._write_conf(conf_path, **conf) self._apply_db_conf() else: print("INFO: Backup was not enabled.")
def do_space_tables(self, *args, **params): # pylint: disable=W0613 """ Show space report for each table. """ dbstatus = self.get_db_status() if not dbstatus.ready: raise GateException("Database is not running!") table = [( 'Table', 'Size', )] total = 0 stdout, stderr = self.call_scenario('tablesizes', user=self.config.get( 'db_user', '').upper()) self.to_stderr(stderr) ora_error = self.has_ora_error(stdout) if ora_error: raise GateException( "Please visit http://%s.ora-code.com/ page to know more details." % ora_error.lower()) for tname, tsize in filter(None, [ filter(None, line.replace("\t", " ").split(" ")) for line in stdout.split("\n") ]): table.append(( tname, ('%.2fK' % round(float(tsize) / 1024.)), )) total += float(tsize) table.append(( '', '', )) table.append(('Total', ('%.2fM' % round(total / 1024. / 1024.)))) if table: print("\n", TablePrint(table), "\n") if stderr: eprint("Error dump:") eprint(stderr) raise Exception("Unhandled underlying error.")
def _get_conf(conf_path: typing.AnyStr) -> typing.Dict[str, typing.Any]: """ Get a PostgreSQL config file into a dictionary. """ if not os.path.exists(conf_path): raise GateException('Cannot open config at "{0}".'.format(conf_path)) conf = {} for line in open(conf_path).readlines(): line = line.strip() if not line or line.startswith('#'): continue try: key, val = [el.strip() for el in line.split('#')[0].strip().split('=', 1)] conf[key] = val except Exception: raise GateException("Cannot parse line '{0}' in '{1}'.".format(line, conf_path)) return conf
def _apply_db_conf(self) -> None: """ Reload the configuration. """ stdout, stderr = self.call_scenario('pg-reload-conf', target='psql') if stderr: eprint(stderr) raise GateException("Unhandled underlying error occurred, see above.") if stdout and stdout.strip() == 't': print("INFO: New configuration has been applied.")
def do_system_check(self, *args, **params): # pylint: disable=W0613 """ Common backend healthcheck. @help force-archivelog-off\tForce archivelog mode to off. """ print("Checking SUSE Manager database backend\n") # Set data table autoextensible. stdout, stderr = self.call_scenario('cnf-get-noautoext') if stderr: eprint("Autoextend check error:") eprint(stderr) raise GateException("Unable continue system check") if stdout: print("Autoextensible:\tOff") scenario = [] for fname in stdout.strip().split("\n"): scenario.append( "alter database datafile '{}' autoextend on;".format( fname)) self.syscall("sudo", "-u", "oracle", "/bin/bash", input=self.get_scenario_template().replace( '@scenario', '\n'.join(scenario))) print("%s table%s has been autoextended" % (len(scenario), len(scenario) > 1 and 's' or '')) else: print("Autoextensible:\tYes") # Turn on archivelog. # if 'force-archivelog-off' in args: if self.get_archivelog_mode(): self.set_archivelog_mode(status=False) else: print("Archivelog mode is not used.") else: if not self.get_archivelog_mode(): self.set_archivelog_mode(True) if not self.get_archivelog_mode(): eprint("No archive log") else: print("Database is now running in archivelog mode.") else: print("Archivelog:\tYes") # Free space on the storage. # # TBD print("\nFinished\n")
def do_backup_check(self, *args, **params): # pylint: disable=W0613 """ Check the consistency of the backup. @help autoresolve\t\tTry to automatically resolve errors and inconsistencies.\n """ self.vw_check_database_ready( "Database must be healthy and running in order to check assigned backups of it!" ) info = self.get_backup_info() if info: print("Last known backup:", info[0].completion) else: raise GateException("No backups has been found!") hbk, fbk, harch, farch = self.check_backup_info() # Display backups info if fbk: eprint("WARNING! Failed backups has been found as follows:") for bkp in fbk: eprint("\tName:", bkp.handle) eprint() else: print(("%s available backup%s seems healthy." % (len(hbk), len(hbk) > 1 and 's are' or ''))) # Display ARCHIVELOG info if farch: eprint("WARNING! Failed archive logs has been found as follows:") for arc in farch: eprint("\tName:", arc.handle) eprint() if 'autoresolve' not in args: eprint("Try using \"autoresolve\" directive.") sys.exit(1) else: self.autoresolve_backup() hbk, fbk, harch, farch = self.check_backup_info() if farch: eprint("WARNING! Still are failed archive logs:") for arc in farch: eprint("\tName:", arc.handle) eprint() if 'ignore-errors' not in args: eprint( "Maybe you want to try \"ignore-errors\" directive and... cross the fingers." ) sys.exit(1) else: print("Hooray! No failures in backups found!") else: print(("%s available archive log%s seems healthy." % (len(harch), len(harch) > 1 and 's are' or '')))
def _get_pg_data(self) -> None: """ PostgreSQL data dir from sysconfig. """ for line in open("/etc/sysconfig/postgresql").readlines(): if line.startswith('POSTGRES_DATADIR'): self.config['pcnf_pg_data'] = os.path.expanduser(line.strip().split('=', 1)[-1].replace('"', '')) if self.config.get('pcnf_pg_data', '') == '': # use default path self.config['pcnf_pg_data'] = '/var/lib/pgsql/data' if not os.path.exists(self.config.get('pcnf_pg_data', '')): raise GateException('Cannot find database component tablespace on disk')
def do_backup_hot(self, *opts: str, **args: str) -> None: # pylint: disable=W0613 """ Enable continuous archiving backup @help --enable=<value>\tEnable or disable hot backups. Values: on | off | purge --backup-dir=<path>\tDestination directory of the backup.\n """ # Part for the auto-backups # --source\tSource path of WAL entry.\n # Example: # --autosource=%p --destination=/root/of/your/backups\n # NOTE: All parameters above are used automatically!\n if args.get('enable') == 'on' and 'backup-dir' in args.keys() and not args['backup-dir'].startswith('/'): raise GateException("No relative paths please.") # Already enabled? arch_cmd: typing.List[str] = list(filter(None, eval(self._get_conf( self.config['pcnf_pg_data'] + "/postgresql.conf").get("archive_command", "''")).split(" "))) if '--destination' not in arch_cmd and args.get('enable') != 'on': raise GateException('Backups are not enabled. Please enable them first. See help for more information.') if '--destination' in arch_cmd: target = re.sub(r"/+$", "", eval(arch_cmd[arch_cmd.index("--destination") + 1].replace("%f", ''))) if re.sub(r"/+$", "", args.get('backup-dir', target)) != target: raise GateException(("You've specified \"%s\" as a destination,\n" + "but your backup is already in \"%s\" directory.\n" + "In order to specify a new target directory,\n" + "you must purge (or disable) current backup.") % (args.get('backup-dir'), target)) args['backup-dir'] = target if not args.get('enable'): args['enable'] = 'on' if args.get('enable') == 'on' and 'backup-dir' not in args.keys(): raise GateException("Backup destination is not defined. Please issue '--backup-dir' option.") if 'enable' in args.keys(): # Check destination only in case user is enabling the backup if args.get('enable') == 'on': # Same owner? if os.lstat(args['backup-dir']).st_uid != os.lstat(self.config['pcnf_pg_data']).st_uid \ or os.lstat(args['backup-dir']).st_gid != os.lstat(self.config['pcnf_pg_data']).st_gid: raise GateException("The \"%s\" directory must belong to the " "same user and group as \"%s\" directory." % (args['backup-dir'], self.config['pcnf_pg_data'])) # Same permissions? if oct(os.lstat(args['backup-dir']).st_mode & 0o777) != oct(os.lstat(self.config['pcnf_pg_data']).st_mode & 0o777): raise GateException("The \"%s\" directory must have the same permissions as \"%s\" directory." % (args['backup-dir'], self.config['pcnf_pg_data'])) self._perform_enable_backups(**args) if 'source' in args.keys(): # Copy xlog entry self._perform_archive_operation(**args) print("INFO: Finished")
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 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_overview(self, *args, **params): # pylint: disable=W0613 """ Show database space report. """ self.vw_check_database_ready( "Database must be healthy and running in order to get space overview!" ) stdout, stderr = self.call_scenario('report') self.to_stderr(stderr) ora_error = self.has_ora_error(stdout) if ora_error: raise GateException( "Please visit http://%s.ora-code.com/ page to know more details." % ora_error.lower()) table = [ ( "Tablespace", "Avail (Mb)", "Used (Mb)", "Size (Mb)", "Use %", ), ] for name, free, used, size in [ " ".join(filter(None, line.replace("\t", " ").split(" "))).split(" ") for line in stdout.strip().split("\n")[2:] ]: table.append(( name, free, used, size, str(int(float(used) / float(size) * 100)), )) print("\n", TablePrint(table), "\n")
def do_space_overview(self, **args: str) -> None: # pylint: disable=W0613 """ Show database space report """ # Not exactly as in Oracle, this one looks where PostgreSQL is mounted # and reports free space. if not self._get_db_status(): raise GateException("Database must be running.") # Get current partition partition = self._get_partition(self.config['pcnf_data_directory']) # Build info class Info: """ Info structure """ fs_dev: typing.Optional[str] = None fs_type: typing.Optional[str] = None size: int = 0 used: int = 0 available: int = 0 used_prc: typing.Optional[str] = None mountpoint: typing.Optional[str] = None info = Info() for data_line in os.popen("df -T").readlines()[1:]: data_line = data_line.strip() if not data_line.startswith(partition): continue line = list(filter(None, data_line.split(" "))) info.fs_dev = line[0] info.fs_type = line[1] info.size = int(line[2]) * 1024 # Bytes info.used = int(line[3]) * 1024 # Bytes info.available = int(line[4]) * 1024 # Bytes info.used_prc = line[5] info.mountpoint = line[6] break # Get database sizes stdout, stderr = self.syscall( "sudo", "-u", "postgres", "/bin/bash", input=self.get_scenario_template(target='psql').replace( '@scenario', 'select pg_database_size(datname), datname from pg_database;')) self.to_stderr(stderr) overview = [( 'Database', 'DB Size (Mb)', 'Avail (Mb)', 'Partition Disk Size (Mb)', 'Use %', )] for data_line in stdout.split("\n"): if "|" not in data_line or "pg_database_size" in data_line: # Different versions of postgresql continue line = list( filter(None, data_line.strip().replace('|', '').split(" "))) if len(line) != 2: continue d_size = int(line[0]) d_name = line[1] overview.append((d_name, self._bt_to_mb(d_size), self._bt_to_mb(info.available), self._bt_to_mb(info.size), '%.3f' % round( (float(d_size) / float(info.size) * 100), 3))) print("\n", TablePrint(overview), "\n")
def check_backup_info(self): """ Check if backup is consistent. """ failed_backups = [] healthy_backups = [] failed_archivelogs = [] healthy_archivelogs = [] bkpsout = None arlgout = None # Get database backups stdout, stderr = self.call_scenario('rman-backup-check-db', target='rman') if stderr: eprint("Backup information check failure:") eprint(stderr) raise GateException("Unable to check the backups.") for chunk in stdout.split("RMAN>"): chunk = chunk.strip() if not chunk: continue if chunk.find("crosschecked backup piece") > -1: bkpsout = chunk break # Get database archive logs check stdout, stderr = self.call_scenario('rman-backup-check-al', target='rman') if stderr: eprint("Archive log information check failure:") eprint(stderr) raise GateException("Unable to check the archive logs backup.") for chunk in stdout.split("RMAN>"): chunk = chunk.strip() if not chunk: continue if chunk.find("archived log file name") > -1: arlgout = chunk break # Check failed backups if bkpsout: for line in map(lambda elm: elm.strip(), bkpsout.split("crosschecked")): if not line.startswith("backup piece"): continue obj_raw = line.split("\n")[:2] if len(obj_raw) == 2: status = obj_raw[0].strip().split(" ")[-1].replace( "'", '').lower() data = dict( filter( None, map( lambda elm: "=" in elm and tuple( elm.split("=", 1)) or None, filter(None, obj_raw[-1].split(" "))))) hinfo = HandleInfo(status, handle=data['handle'], recid=data['RECID'], stamp=data['STAMP']) if hinfo.availability == 'available': healthy_backups.append(hinfo) else: failed_backups.append(hinfo) # Check failed archive logs if arlgout: for archline in map( lambda elm: elm.strip(), arlgout.split( "validation", 1)[-1].split("Crosschecked")[0].split("validation")): obj_raw = archline.split("\n") if len(obj_raw) == 2: status = obj_raw[0].split(" ")[0] data = dict( filter( None, map( lambda elm: '=' in elm and tuple( elm.split('=', 1)) or None, obj_raw[1].split(" ")))) # Ask RMAN devs why this time it is called "name" hinfo = HandleInfo(status == 'succeeded' and 'available' or 'unavailable', recid=data['RECID'], stamp=data['STAMP'], handle=data['name']) if hinfo.availability == 'available': healthy_archivelogs.append(hinfo) else: failed_archivelogs.append(hinfo) return healthy_backups, failed_backups, healthy_archivelogs, failed_archivelogs
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_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()