Beispiel #1
0
    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()
Beispiel #2
0
    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
Beispiel #3
0
    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
Beispiel #4
0
    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', ""))
Beispiel #5
0
    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
Beispiel #6
0
    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")
Beispiel #7
0
    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()
Beispiel #9
0
    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.")
Beispiel #10
0
    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.")
Beispiel #11
0
    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
Beispiel #12
0
 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.")
Beispiel #13
0
    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")
Beispiel #14
0
    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 '')))
Beispiel #15
0
    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')
Beispiel #16
0
    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")
Beispiel #17
0
 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)
Beispiel #18
0
    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)
Beispiel #19
0
    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)
Beispiel #20
0
    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")
Beispiel #21
0
    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")
Beispiel #22
0
    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
Beispiel #23
0
    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.")
Beispiel #24
0
    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()