예제 #1
0
    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)
예제 #2
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)
예제 #3
0
    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()
예제 #4
0
    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)
예제 #5
0
 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)
예제 #6
0
    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)
예제 #7
0
    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()
예제 #8
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)
예제 #9
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)
예제 #10
0
    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")
예제 #11
0
    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)
예제 #12
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()
예제 #13
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.")
예제 #14
0
    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)
예제 #15
0
    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()