def __init__(self, displayInfo=False):

        if not os.path.exists(files.db_cnfpath):
            print(
                "Database config file does not exist. Please, copy .env.sample file to your PL/SQL code path with new name as .env, change the params and try again"
            )
            exit()

        try:
            # Load database config
            load_dotenv(files.db_cnfpath)

            self.disallowed_keywords = os.getenv("DISALLOW_KEYWORDS")
            self.db_admin_user = os.getenv("DB_ADMIN_USER").upper()
            self.db_admin_password = os.getenv("DB_ADMIN_PASSWORD")
            self.db_default_table_space = os.getenv("DB_DEFAULT_TABLE_SPACE").upper()
            self.db_temp_table_space = os.getenv("DB_TEMP_TABLE_SPACE").upper()
            self.db_main_schema = os.getenv("DB_MAIN_SCHEMA").upper()
            self.service_name = os.getenv("DB_SERVICE_NAME")
            self.user = os.getenv("DB_USER").upper()
            self.password = os.getenv("DB_PASSWORD")
            self.host = os.getenv("DB_HOST")
            self.port = os.getenv("DB_PORT")

        except Exception as e:
            print("Make sure you have all params setup on .env file")

        self.types = files.objectsTypes().keys()
        self.extentions = files.objectsTypes().values()

        self.displayInfo = displayInfo
        files.displayInfo = self.displayInfo
    def metadataInsert(self, data, md5=False, db=None):
        """ Insert data into metadata table.
        
        Params: 
        data: list that contain and dict with the following keys: object_name, object_type, object_path, last_commit, last_ddl_time
        """

        localClose = False
        if not db:
            db = self.dbConnect()
            localClose = True
        cursor = db.cursor()

        for obj in data:
            md5 = files.fileMD5(obj["object_path"])

            sql = (
                "INSERT INTO %s.PLADMIN_METADATA VALUES('%s', '%s', '%s','%s', sysdate, TO_DATE('%s','RRRR/MM/DD HH24:MI:SS'))"
                % (
                    self.user,
                    obj["object_name"],
                    obj["object_type"],
                    obj["object_path"],
                    md5,
                    obj["last_ddl_time"],
                )
            )
            cursor.execute(sql)

        cursor.close()

        if localClose:
            db.commit()
            db.close()
    def getObjects(
        self,
        objectTypes=None,
        objectName=None,
        status=None,
        withPath=False,
        fetchOne=None,
        db=None
    ):
        """
        List Packages, Functions and Procedures and Views
        
        Params:
        ------
        status (string): Valid values [VALID, INVALID].
        objectTypes (list): List of object type that you want [PACKAGE, FUNCTION, PROCEDURE]
        withPath (Boolean): [True] if you want to include the path of the object

        return (dic) with all objects listed
        """

        types = "', '".join(self.types)
        if objectTypes:
            types = "', '".join(objectTypes)

        query = (
            """SELECT owner, object_id, object_name, object_type, status, last_ddl_time, created FROM dba_objects WHERE owner = '%s' AND object_type in ('%s')"""
            % (self.user, types)
        )

        if (status == "INVALID") or status == "VALID":
            query += " AND status = '%s'" % status

        if objectName:
            query += " AND object_name = '%s'" % objectName

        # Return a dic with the data
        result = self.getData(query=query, fetchOne=fetchOne, db=db)

        if fetchOne:
            return result

        if len(result) and withPath:
            for obj in result:

                p = files.findObjFileByType(
                    objectType=obj["object_type"], objectName=obj["object_name"]
                )

                obj.update({"object_path": ''})

                if len(p):
                    obj.update({"object_path": p[0]})

        return result
    def newUser(self, db, force=False):

        progressTotal = 3
        files.progress(
            count=1,
            total=progressTotal,
            status="VALIDATING...",
            title="CREATING " + self.user,
        )

        cursor = db.cursor()
        # Firts, we need to validate if the user exist
        sql = "SELECT COUNT(1) AS v_count FROM dba_users WHERE username = :db_user"
        cursor.execute(sql, {"db_user": self.user})

        user = cursor.fetchone()
        if user[0]:
            if force:
                files.progress(
                    count=2, total=progressTotal, status="DROP USER %s" % self.user
                )
                cursor.execute("DROP USER %s CASCADE" % self.user)
            else:
                return False

        # Create the user
        files.progress(
            count=2, total=progressTotal, status="CREATING USER %s" % self.user
        )
        sql = (
            "CREATE USER %s IDENTIFIED BY %s DEFAULT TABLESPACE %s TEMPORARY TABLESPACE %s QUOTA UNLIMITED ON %s"
            % (
                self.user,
                self.password,
                self.db_default_table_space,
                self.db_temp_table_space,
                self.db_default_table_space,
            )
        )
        cursor.execute(sql)

        files.progress(
            count=3, total=progressTotal, status="USER %s CREATED" % self.user, end=True
        )
        return True
    def createGramtsTo(self, originSchema, detinationSchema, db=None):
        cursor = db.cursor()
        i = 0
        permisions = [
            "GRANT CREATE PROCEDURE TO",
            "GRANT CREATE SEQUENCE TO",
            "GRANT CREATE TABLE TO",
            "GRANT CREATE VIEW TO",
            "GRANT CREATE TRIGGER TO",
            "GRANT EXECUTE ANY PROCEDURE TO",
            "GRANT SELECT ANY DICTIONARY TO",
            "GRANT CREATE SESSION TO",
            "GRANT SELECT ANY DICTIONARY TO",
            "GRANT EXECUTE ANY PROCEDURE TO",
            "GRANT EXECUTE ANY TYPE TO",
            "GRANT ALTER ANY TABLE TO",
            "GRANT ALTER ANY SEQUENCE TO",
            "GRANT UPDATE ANY TABLE TO",
            "GRANT DEBUG ANY PROCEDURE TO",
            "GRANT DEBUG CONNECT ANY to",
            "GRANT DELETE ANY TABLE TO",
            "GRANT ALTER ANY INDEX TO",
            "GRANT INSERT ANY TABLE TO",
            "GRANT READ ANY TABLE TO",
            "GRANT SELECT ANY TABLE TO",
            "GRANT SELECT ANY SEQUENCE TO",
            "GRANT UPDATE ON SYS.SOURCE$ TO",
            "GRANT EXECUTE ON SYS.DBMS_LOCK TO",
        ]

        # Prepare vars to progress bar
        progressTotal = len(permisions)
        files.progress(
            i,
            progressTotal,
            status="LISTING PERMISSIONS %s" % detinationSchema,
            title="GIVE GRANTS",
        )

        for p in permisions:
            # Write progress bar
            files.progress(i, progressTotal, status="GRANT TO %s " % detinationSchema)

            # Excute to db
            cursor.execute(p + " " + detinationSchema)

            i += 1

        # This is a special permission
        cursor.execute(
            "CREATE SYNONYM %s.FERIADOS FOR OMEGA.FERIADOS" % detinationSchema
        )
        files.progress(
            i, progressTotal, status="GRANT TO %s " % detinationSchema, end=True
        )
    def createSchema(self, force=False):
        # To create users, give permission, etc. We need to connect with admin user using param sysDBA
        db = self.dbConnect(sysDBA=True)

        # Drop and create the user
        user = self.newUser(db=db, force=force)

        if not user:
            print(
                "\n The user %s already exist, use --force option override the schema"
                % self.user
            )
            exit()

        # Give grants to the user
        self.createGramtsTo(
            originSchema=self.db_main_schema, detinationSchema=self.user, db=db
        )

        # Create table of migrations
        # self.createMetaTableScripts()

        # Create synonyms
        self.createSynonyms(
            originSchema=self.db_main_schema, detinationSchema=self.user, db=db
        )

        # Close SYS admin db connection
        db.close()

        # Create meta table
        self.createMetaTable()

        # Create o replace packages, views, functions and procedures (All elements in files.objectsTypes())
        data = files.listAllObjsFiles()
        self.createReplaceDbObject(path=data, showInfo=True)

        # If some objects are invalids, try to compile
        invalids = self.compileObjects()

        # Getting up object type, if it's package, package body, view, procedure, etc.
        data = self.getObjects(withPath=True)
        self.metadataInsert(data)

        return invalids
    def createSynonyms(self, originSchema, detinationSchema, db):
        """ Create synonyms types ('SEQUENCE', 'TABLE', 'TYPE') from originSchema to destinationSchema """

        cursor = db.cursor()
        sql = """ SELECT oo.object_name, oo.object_type, oo.status
                FROM sys.dba_objects oo
                WHERE     oo.owner = '%s'
                    AND oo.object_type IN ('SEQUENCE', 'TABLE', 'TYPE')
                    AND oo.object_name NOT LIKE 'SYS_PLSQL_%%'
                    AND oo.object_name NOT LIKE 'QTSF_CHAIN_%%'
                    AND oo.object_name <> 'PLADMIN_METADATA'
                    AND NOT EXISTS
                            (SELECT 1
                                FROM sys.dba_objects tob
                                WHERE     tob.owner = '%s'
                                    AND tob.object_name = oo.object_name)
                    AND status = 'VALID' """ % (
            originSchema,
            detinationSchema,
        )

        synonyms = self.getData(query=sql, db=db)

        # Params to process bar
        progressTotal = len(synonyms)
        i = 0
        files.progress(i, progressTotal, "LISTING TABLES", title="CREATE SYNONYMS")

        for synon in synonyms:
            # Write progress bar
            files.progress(
                i,
                progressTotal,
                status="CREATE SYNONYM %s.%s"
                % (detinationSchema, synon["object_name"]),
            )

            sql = "CREATE SYNONYM %s.%s FOR %s.%s" % (
                detinationSchema,
                synon["object_name"],
                originSchema,
                synon["object_name"],
            )
            cursor.execute(sql)

            i += 1

        files.progress(i, progressTotal, status="SYNONYMS CREATED", end=True)
        cursor.close()
Exemple #8
0
#!/usr/local/bin/python
from __future__ import absolute_import
import sys, getopt, json, os, argparse, time, hashlib, re
from termcolor import colored
from prettytable import PrettyTable

from datetime import datetime, timedelta
from pladmin.database import Database
from pladmin.files import Files
from pladmin.utils import utils
from pladmin.migrations import Migrations


db = Database(displayInfo=True)
files = Files(displayInfo=True)

# Table for wc2db and db2wc methods
info = PrettyTable(["Object", "Type", "Path", "Action", "Status", "Info"])
infoScript = PrettyTable(["Name", "GroupID", "Type", "Status", "Output"])
info.align = infoScript.align = 'l'


def watch(path_to_watch):
    """ Watch the provided path for changes in any of it's subdirectories """

    print("Watching " + path_to_watch)
    before = files.files_to_timestamp()

    while 1:
        time.sleep(0.5)
        after = files.files_to_timestamp()
    def createReplaceDbObject(self, path=None, db=None, showInfo=False):
        """
        Creates or Replaces packges, views, procedures and functions.

        params:
        ------
        path (list): path routes of the object on the file system
        db (cx_Oracle.Connection): If you opened a db connection puth here please to avoid

        return (list) with errors if some package were an error
        """

        success = []
        errors = []
        localClose = False
        if not db:
            db = self.dbConnect()
            localClose = True

        cursor = db.cursor()

        # Prepare data for progress bar
        progressTotal = len(path)
        i = 0
        files.progress(
            i,
            progressTotal,
            status="LISTING PACKAGES...",
            title="CREATE OR REPLACE PACKAGES",
        )

        for f in path:
            fname, ftype, objectType = files.getFileName(f)
            # Only valid extencions sould be processed
            if not "." + ftype in self.extentions:
                continue

            # Display progress bar
            files.progress(i, progressTotal, "CREATING %s" % fname)
            i += 1

            opf = open(f, "r")
            content = opf.read()
            opf.close()

            context = "CREATE OR REPLACE "
            if ftype == "vw":
                context = "CREATE OR REPLACE FORCE VIEW %s AS \n" % fname

            # Execute create or replace package
            try:
                cursor.execute(context + content)
                success.append(fname)
            except Exception as e:
                errors.append(e)
                if showInfo:
                    print(e)
                pass

        files.progress(
            i,
            progressTotal,
            status="OBJECTS HAS BEEN CREATED (ERRORS: %s)" % len(errors),
            end=True,
        )

        if localClose:
            db.close()

        return success, errors