aboutsummaryrefslogtreecommitdiffstats
path: root/pgaas/src/stage/opt/app/pgaas/bin/dcae_admin_db.py
diff options
context:
space:
mode:
authorHansen, Tony (th1395) <th1395@att.com>2018-03-29 12:57:06 +0000
committerHansen, Tony (th1395) <th1395@att.com>2018-03-29 12:57:23 +0000
commitbd5129dbad044907bd1818b2f8cda3c60da9b3e8 (patch)
treeedc355d63e440a87a1c97ad65357681f279e6ba0 /pgaas/src/stage/opt/app/pgaas/bin/dcae_admin_db.py
parent4e1b45a2e3e474606965ca3b66bd5f82f526b0ce (diff)
remove most of this code for R2 Beijing
This package is not being used in R2 Beijing because of the move to Kubernetes with OOM. If you need this package, use the R1 Amsterdam release packages. I'm not removing the repo though because it is likely that we will need to add things in R3 Casablanca. Change-Id: I40a46886cfa2f3803f97918ad5cea149f5fcf696 Signed-off-by: Hansen, Tony (th1395) <th1395@att.com> Issue-ID: CCSDK-231
Diffstat (limited to 'pgaas/src/stage/opt/app/pgaas/bin/dcae_admin_db.py')
-rwxr-xr-xpgaas/src/stage/opt/app/pgaas/bin/dcae_admin_db.py892
1 files changed, 0 insertions, 892 deletions
diff --git a/pgaas/src/stage/opt/app/pgaas/bin/dcae_admin_db.py b/pgaas/src/stage/opt/app/pgaas/bin/dcae_admin_db.py
deleted file mode 100755
index e1ac744..0000000
--- a/pgaas/src/stage/opt/app/pgaas/bin/dcae_admin_db.py
+++ /dev/null
@@ -1,892 +0,0 @@
-#!/usr/bin/python3
-# -*- indent-tabs-mode: nil -*-
-# Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this code except in compliance
-# with the License. You may obtain a copy of the License
-# at http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-# implied. See the License for the specific language governing
-# permissions and limitations under the License.
-
-
-"""
-
-NAME
- dcae_admin_db.py - given a database description json file, update the current VM accordingly
-
-USAGE
- dcae_admin_db.py [options] configurationChanged json-file
- dcae_admin_db.py [options] suspend
- dcae_admin_db.py [options] resume
- dcae_admin_db.py [options] test
-
- options:
-
- -H / --dbhost= - host name, defaults to CFG['dcae_admin_db_hostname']
- -d / --dbdir= - database directory path, defaults to CFG['db_directory']
- -c / --dbconf= - database configuration path, defaults to CFG['db_configuration']
- -D / --dbname= - database name, defaults to CFG['dcae_admin_db_databasename']
- -U / --user= - user to login as, defaults to CFG['dcae_admin_db_username']
- -P / --password= - password for user, defaults to CFG['dcae_admin_db_password']
- -B / --bindir= - postgresql bin directory, defaults to CFG['pg_bin_directory']
- -i / --ignorefile= - skip configuration if this file is present, defaults to CFG['skip_configuration_file']
- -n / --nocreate - do not create the databases / users
- -I / --ignoredb - ignore current state of database
- -R / --remove - remove old databases / users
- -J / --jsontop= - top of json tree, as in \"['pgaas']\"
- -e / --errors= - where to redirect error output, defaults to CFG['dcae_admin_db_errors_file'] then stderr
- -t / --trace= - where to redirect trace output, defaults to CFG['dcae_admin_db_trace_file'] then stderr
- -v / --verbose - verbose, defaults to CFG['dcae_admin_db_verbosity']
-
-DESCRIPTION
- This program is intended to be executed by the DCAE controller manager.
-
-When creating a database and set of users, execute the equivalent of this:
-
- CREATE USER tstdb_admin WITH PASSWORD 'tst';
- CREATE USER tstdb_user WITH PASSWORD 'tst';
- CREATE USER tstdb_viewer WITH PASSWORD 'tst';
-
- CREATE ROLE testdb_common_user_role;
- CREATE ROLE testdb_common_viewer_role;
-
- CREATE DATABASE testdb with owner tstdb_admin;
-
- \connect testdb
-
- REVOKE ALL on DATABASE testdb FROM testdb_common_viewer_role;
- REVOKE ALL on DATABASE testdb FROM testdb_common_user_role;
- REVOKE ALL on DATABASE testdb FROM tstdb_user;
- REVOKE ALL on DATABASE testdb FROM tstdb_viewer;
-
- GRANT testdb_common_viewer_role TO testdb_common_user_role; /* user can do everything viewer can */
- GRANT testdb_common_user_role TO tstdb_admin; /* admin can do everything user and viewer can */
-
- GRANT CONNECT ON DATABASE testdb TO testdb_common_viewer_role; /* viewer, user, admin can connect */
-
- CREATE SCHEMA testdb_db_common AUTHORIZATION tstdb_admin; /* create a schema we can optionally use */
-
- ALTER ROLE tstdb_admin IN DATABASE testdb SET search_path = public, testdb_db_common; /* search_path is not inherited, so set it here */
- ALTER ROLE testdb_common_user_role IN DATABASE testdb SET search_path = public, testdb_db_common; /* search_path is not inherited, so set it here */
- ALTER ROLE testdb_common_viewer_role IN DATABASE testdb SET search_path = public, testdb_db_common; /* search_path is not inherited, so set it here */
-
- GRANT USAGE ON SCHEMA testdb_db_common TO testdb_common_viewer_role; /* viewer,user can select from schema */
- GRANT CREATE ON SCHEMA testdb_db_common TO tstdb_admin; /* admin can create on schema */
-
- ALTER DEFAULT PRIVILEGES FOR ROLE tstdb_admin GRANT SELECT ON TABLES TO testdb_common_viewer_role; /* viewer, user, admin can select on tables */
- ALTER DEFAULT PRIVILEGES FOR ROLE tstdb_admin GRANT INSERT, UPDATE, DELETE, TRUNCATE ON TABLES TO testdb_common_user_role; /* user, admin can ins/upd/del/tru on tables */
- ALTER DEFAULT PRIVILEGES FOR ROLE tstdb_admin GRANT USAGE, SELECT, UPDATE ON SEQUENCES TO testdb_common_user_role; /* user, admin can update on sequences */
-
- GRANT TEMP ON DATABASE testdb TO testdb_common_user_role; /* user, admin can create temp tables */
-
- GRANT testdb_common_user_role TO tstdb_user;
- GRANT testdb_common_viewer_role TO tstdb_viewer;
- ALTER ROLE tstdb_user IN DATABASE testdb SET search_path = public, testdb_db_common; /* search_path is not inherited, so set it here */
- ALTER ROLE tstdb_viewer IN DATABASE testdb SET search_path = public, testdb_db_common; /* search_path is not inherited, so set it here */
-
-"""
-
-import getopt
-import psycopg2
-import sys
-import re
-import subprocess
-import json
-import os
-import time
-
-sys.path.append("/opt/app/pgaas/lib")
-sys.path.append("/opt/app/dcae-commonlogging/python")
-import CommonLogger
-
-verbose = 0
-quiet = False
-errorOutput = sys.stderr
-traceOutput = sys.stderr
-errorLogger = debugLogger = auditLogger = metricsLogger = None
-
-def usage(msg = None):
- """
- Print a usage message and exit
- """
- sys.stdout = sys.stderr
- if msg != None:
- print(msg)
- print("Usage:")
- print("dcae_admin_db.py [options] configurationChanged json-file")
- print("dcae_admin_db.py [options] suspend")
- print("dcae_admin_db.py [options] resume")
- print("dcae_admin_db.py [options] test")
- print("dcae_admin_db.py [options] newdb dbname admin-pswd user-pswd viewer-pswd")
- print("")
- print("options:")
- print("-H / --dbhost= - host name, defaults to CFG['dcae_admin_db_hostname']")
- print("-d / --dbdir= - database directory path, defaults to CFG['db_directory']")
- print("-c / --dbconf= - database directory path, defaults to CFG['db_configuration']")
- print("-D / --dbname= - database name, defaults to CFG['dcae_admin_db_databasename']")
- print("-n / --nocreate - do not create the databases / users")
- print("-I / --ignoredb - ignore current state of database")
- print("-U / --user= - user to login as, defaults to CFG['dcae_admin_db_username']")
- print("-P / --password= - password for user, defaults to CFG['dcae_admin_db_password']")
- print("-B / --bindir= - postgresql bin directory, defaults to CFG['pg_bin_directory']")
- print("-i / --ignorefile= - skip configuration if this file is present, defaults to CFG['skip_configuration_file']")
- print("-R / --remove - remove old databases / users")
- print("-J / --jsontop= - top of json tree, as in \"['pgaas']\"")
- print("-l / --logcfg= - ECOMP DCAE Common Logging configuration file")
- print("-e / --errors= - where to redirect error output, defaults to CFG['dcae_admin_db_errors_file'] then stderr")
- print("-t / --trace= - where to redirect trace output, defaults to CFG['dcae_admin_db_trace_file'] then stderr")
- print("-v - verbose")
- sys.exit(2)
-
-def checkOption(options, name, propname, optletter, encrypted=False, cdfPropname = None):
- """
- Check if the specified option exists. If not, grab it from the configuration file.
- Complain if it still does not exist.
- """
- if name not in options:
- ret = getPgaasPropValue(propname, encrypted=encrypted, dflt=None, skipComplaining=True)
- if ret is None and cdfPropname is not None:
- ret = getCdfPropValue(cdfPropname, encrypted=encrypted)
- options[name] = ret
- requireOption("either %s or config[%s]" % (optletter, propname), options[name])
-
-def reviewOpts():
- """
- Parse the options passed to the command, and return them in the dictionary
- """
- try:
- opts, args = getopt.getopt(sys.argv[1:], "B:c:D:d:e:H:IJ:l:nP:Rt:U:hv?",
- [ "dbhost=", "dbdir=", "dbconf=",
- "dbname=", "dbuser=", "dbpassword=",
- "bindir=", "errors=", "trace=", "logcfg=",
- "nocreate", "ignoredb", "remove", "ignorefile=",
- "jsontop=",
- "help", "verbose"])
- except getopt.GetoptError as err:
- usage(str(err))
-
- propVerbosity = getPgaasPropValue("dcae_admin_db_verbosity", dflt='0')
- if propVerbosity is not None:
- global verbose
- verbose = int(propVerbosity)
- retOptions = { }
- ignoreFile = getPgaasPropValue("skip_configuration_file", dflt=None)
- for o, a in opts:
- if o in ("-v", "--verbose"):
- # global verbose
- verbose += 1
- elif o in ("-c", "--dbconf"):
- retOptions["dbconf"] = a
- elif o in ("-H", "--dbhost"):
- retOptions["dbhost"] = a
- elif o in ("-d", "--dbdir"):
- retOptions["dbdir"] = a
- elif o in ("-D", "--dbname"):
- retOptions["dbname"] = a
- elif o in ("-U", "--dbuser"):
- retOptions["dbuser"] = a
- elif o in ("-P", "--dbpassword"):
- retOptions["dbpassword"] = a
- elif o in ("-B", "--bindir"):
- retOptions["bindir"] = a
- elif o in ("-n", "--nocreate"):
- retOptions["nocreate"] = True
- elif o in ("-I", "--ignoredb"):
- retOptions["ignoredb"] = True
- elif o in ("-R", "--remove"):
- retOptions["noremove"] = True
- elif o in ("-J", "--jsontop"):
- retOptions["jsontop"] = a
- elif o in ("-l", "--logcfg"):
- retOptions["logcfg"] = a
- elif o in ("-e", "--errors"):
- retOptions["errors"] = a
- elif o in ("-i", "--ignorefile"):
- ignoreFile = a
- elif o in ("-t", "--trace"):
- retOptions["trace"] = a
- elif o in ("-h", "--help"):
- usage()
- else:
- usage("unhandled option: %s" % o)
- if "errors" not in retOptions:
- retOptions["errors"] = getPgaasPropValue("dcae_admin_db_errors_file")
- if "errors" in retOptions and retOptions["errors"] is not None:
- try:
- errorOutput = open(retOptions["errors"], "a")
- except Exception as e:
- die("Cannot open errors file '%s': %s" % (retOptions["errors"], e))
- if ignoreFile is not None:
- trace("checking to see if skip_configuration_file(%s) exists" % ignoreFile)
- retOptions["ignorefile"] = "yes" if os.path.isfile(ignoreFile) else "no"
- trace("ignorefile=%s" % retOptions["ignorefile"])
- else:
- retOptions["ignorefile"] = None
- if "trace" not in retOptions:
- retOptions["trace"] = getPgaasPropValue("dcae_admin_db_trace_file")
- if "trace" in retOptions and retOptions["trace"] is not None:
- try:
- traceOutput = open(retOptions["trace"], "a")
- except Exception as e:
- die("Cannot open trace file '%s': %s" % (retOptions["trace"], e))
- if "logcfg" not in retOptions:
- retOptions["logcfg"] = getPgaasPropValue("dcae_admin_db_common_logger_config")
- if "logcfg" in retOptions and retOptions["logcfg"] is not None:
- logcfg = retOptions["logcfg"]
- import uuid
- instanceUUID = uuid.uuid1()
- serviceName = "DCAE/pgaas"
- # print(">>>>>>>>>>>>>>>> using common logger. UUID=%s, serviceName=%s, cfg=%s" % (instanceUUID, serviceName, logcfg))
- global errorLogger, debugLogger, auditLogger, metricsLogger
- errorLogger = CommonLogger.CommonLogger(logcfg, "error", instanceUUID=instanceUUID, serviceName=serviceName)
- debugLogger = CommonLogger.CommonLogger(logcfg, "debug", instanceUUID=instanceUUID, serviceName=serviceName)
- auditLogger = CommonLogger.CommonLogger(logcfg, "audit", instanceUUID=instanceUUID, serviceName=serviceName)
- metricsLogger = CommonLogger.CommonLogger(logcfg, "metrics", instanceUUID=instanceUUID, serviceName=serviceName)
- auditLogger.info("using common logger. UUID=%s, serviceName=%s, cfg=%s" % (instanceUUID, serviceName, logcfg))
-
- checkOption(retOptions, "dbname", "dcae_admin_db_databasename", "-D")
- checkOption(retOptions, "dbuser", "dcae_admin_db_username", "-U")
- checkOption(retOptions, "dbpassword", "dcae_admin_db_password", "-P", encrypted=True, cdfPropname="postgres")
- checkOption(retOptions, "dbhost", "dcae_admin_db_hostname", "-H")
- checkOption(retOptions, "dbdir", "db_directory", "-d")
- checkOption(retOptions, "bindir", "pg_bin_directory", "-B")
- if "jsontop" not in retOptions:
- retOptions["jsontop"] = getPgaasPropValue("dcae_admin_db_jsontop")
- trace("env=%s" % str(os.environ))
- trace("ignorefile=%s" % ignoreFile)
- return retOptions, args
-
-def main():
- keyedOptions, args = reviewOpts()
- trace("Invoked as: %s" % str(sys.argv))
- audit("Invoked as: %s" % str(sys.argv))
-
- if len(args) == 0:
- usage("no operation specified")
- elif args[0] == "configurationChanged":
- if len(args) != 2:
- usage("too many arguments")
- configurationChanged(keyedOptions, args[1])
- elif args[0] == "suspend":
- if len(args) != 1:
- usage("too many arguments")
- suspendOperations(keyedOptions)
- elif args[0] == "resume":
- if len(args) != 1:
- usage("too many arguments")
- resumeOperations(keyedOptions)
- elif args[0] == "test":
- if len(args) != 1:
- usage("too many arguments")
- testOperations(keyedOptions)
- elif args[0] == "newdb":
- if len(args) != 5:
- usage("wrong number of arguments")
- newDb(keyedOptions, args[1], args[2], args[3], args[4])
- else:
- usage("unrecognized operation '%s'" % args[0])
-
-def suspendOperations(options):
- """
- Execute the "suspend" sub-command.
- """
- runProgram(["pkill", "repmgrd"])
- program = options["bindir"] + "/pg_ctl"
- cmd = [program, "stop", "-D", options["dbdir"]]
- runProgram(cmd)
- audit("suspendOperations")
-
-def resumeOperations(options):
- """
- Execute the "resume" sub-command.
- """
- cmd = [options["bindir"] + "/pg_ctl", "start", "-D", options["dbdir"], "-o", "configfile=" + options["dbconf"]]
- runProgram(cmd)
- runProgram(["/opt/app/pgaas/bin/repmgrcd", "-d"])
- audit("resumeOperations")
-
-def testOperations(options):
- """
- Respond to the "test" sub-command.
- """
- program = options["bindir"] + "/pg_ctl"
- cmd = [program, "status", "-D", options["dbdir"]]
- ret = runProgram(cmd)
- # pg_ctl: no server running
- # pg_ctl: server is running (PID: 13988)
- # does /var/run/postgresql/inmaintenance exist? -> YELLOW
- cmdRepmgr = ["pgrep", "repmgrd"]
- retRepmgr = runProgram(cmdRepmgr)
-
- msg = "????"
- if os.path.isfile("/var/run/postgresql/inmaintenance"):
- msg = "YELLOW: in maintenance mode"
- elif re.search("no server running", ret):
- msg = "RED: no PG server running"
- elif re.search("server is running", ret) and re.search("[0-9]+", retRepmgr):
- msg = "GREEN"
- elif re.search("server is running", ret):
- msg = "YELLOW: no repmgrd running"
- elif re.search("[0-9]+", retRepmgr):
- msg = "YELLOW: no PG server running"
- else:
- msg = "YELLOW: neither PG server nor repmgrd are running"
- audit("test: " + msg)
- print(msg, end="")
-
-def runProgram(cmd):
- """
- Run the given command, returning the standard output as a string.
- If there is an error, return None.
- """
- try:
- p=subprocess.Popen(cmd,shell=False,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
- (stdout, stderr) = p.communicate()
- except Exception as e:
- print("Error running program because: {0}".format(e), file=errorOutput)
- return None
- else:
- if stderr:
- print("Error running program because: {0} ".format(stderr), file=errorOutput)
- return None
- else:
- trace("runProgram() => " + str(stdout), minLevel=2)
- return stdout.decode('utf-8').rstrip('\n')
-
-def configurationChanged(options, jsonFile):
- """
- We received a new JSON configuration file
- """
- audit("configurationChanged " + jsonFile)
- if options["ignorefile"] == "yes":
- trace("skipping database reconfiguration because skip_configuration_file exists")
- return
-
- if not os.path.isfile(jsonFile):
- die("json file %s does not exist" % jsonFile)
-
- try:
- inp = json.load(open(jsonFile,"r"))
- except Exception as e:
- die("Cannot open jsonFile '%s': %s" % (jsonFile, e))
-
- if verbose:
- dumpJSON(inp, "incoming JSON")
-
- jsonTop = options["jsontop"]
- if not jsonTop is None:
- e = "inp" + jsonTop
- trace("eval(%s)" % e)
- inp = eval(e,{"__builtins__":None},{"inp":inp})
- if verbose:
- dumpJSON(inp, "modified JSON")
-
- setupDictionaryDatabases(options, inp)
-
-def setupDictionaryDatabases(options, inp):
- """
- Set up the databases listed in the dictionary
- """
-
- # trace("version=%s" % requireJSON("version", inp, "version"))
- requireJSON("databases", inp, "databases")
- con = None
- try:
- con = dbConnect(database = options["dbname"], user = options["dbuser"], password = options["dbpassword"], host = options["dbhost"])
- setupDatabases(con, options, requireJSON("databases", inp, "databases"))
-
- except psycopg2.DatabaseError as e:
- die('Error %s' % e)
-
- finally:
- if con:
- con.commit()
- con.close()
-
-def newDb(options, dbName, adminPswd, userPswd, viewerPswd):
- """
- Given the database name and passwords, set up a database and corresponding users.
- For example, with dbname="foo", adminPswd="fooa", userPswd="foou" and viewerPswd="foov",
- act the same as if we had received the json configuration:
- {
- 'databases': {
- 'foo': {
- 'ownerRole': 'foo_admin',
- 'roles': {
- 'foo_admin': {
- 'password': 'fooa',
- 'role': 'admin'
- },
- 'foo_user': {
- 'password': 'foou',
- 'role': 'writer'
- },
- 'foo_viewer': {
- 'password': 'foov',
- 'role': 'reader'
- }
- }
- }
- }
- }
- """
- if not re.match("^[A-Za-z][A-Za-z0-9_]*$", dbName):
- errorPrint("'%s' is not a valid database name" % dbName)
- return
-
- adminName = dbName + "_admin"
- userName = dbName + "_user"
- viewerName = dbName + "_viewer"
-
- setupDictionaryDatabases(options, {
- 'databases': {
- dbName: {
- 'ownerRole': adminName,
- 'roles': {
- adminName: {
- 'password': adminPswd,
- 'role': 'admin'
- },
- userName: {
- 'password': userPswd,
- 'role': 'writer'
- },
- viewerName: {
- 'password': viewerPswd,
- 'role': 'reader'
- }
- }
- }
- }
- })
-
-def dumpJSON(js, msg):
- tracePrint("vvvvvvvvvvvvvvvv %s" % msg)
- tracePrint(json.dumps(js, indent=4))
- tracePrint("^^^^^^^^^^^^^^^^ %s" % msg)
-
-def setupDatabases(con, options, dbList):
- """
- Do what is needed to set up all of the databases
- """
- currentDatabases = dbGetFirstColumnAsMap(con, "select datname from pg_database where datistemplate = false")
- currentRolenames = dbGetFirstColumnAsMap(con, "select rolname from pg_roles")
- trace("currentDatabases = " + str(currentDatabases))
- for dbName in dbList:
- trace("dbName='%s'" % str(dbName))
- setupDatabase(con, options, currentDatabases, currentRolenames, dbName, dbList[dbName])
-
-def setupDatabase(con, options, currentDatabases, currentRolenames, dbName, dbInfo):
- """
- Do what is needed to set up a given databases and its users
- """
-
- dbOwnerRole = requireJSON("databases[].ownerRole", dbInfo, "ownerRole")
- trace("dbName='%s', dbOwnerRole='%s'" % (dbName, dbOwnerRole))
- doesDbExist = dbName in currentDatabases
- trace("does %s exist? %s" % (dbName, doesDbExist))
- foundOwnerRole = False
- dbRoles = dbInfo["roles"]
- for name in dbRoles:
- u = dbRoles[name]
- if name == dbOwnerRole and u["role"] == "admin":
- foundOwnerRole = True
- if u["role"] not in ("admin","writer","reader"):
- die("For database %s, the role '%s' is not one of admin/writer/reader" % (dbName, u.role))
- if not foundOwnerRole:
- die("For database %s, information on the ownerRole '%s' was not found" % (dbName, dbOwnerRole))
- for name in dbRoles:
- userInfo = dbRoles[name]
- if name in currentRolenames and ("ignoredb" not in options or not options["ignoredb"]):
- trace("The role %s already exists, skipping" % name)
- updatePassword(con, options, dbName, name, userInfo)
- else:
- setupUser(con, options, dbName, name, userInfo)
- if doesDbExist and ("ignoredb" not in options or not options["ignoredb"]):
- trace("The database %s already exists, skipping" % dbName)
- else:
- makeDatabase(con, options, dbName, dbOwnerRole, dbInfo, dbRoles)
- for name in dbRoles:
- userInfo = dbRoles[name]
- if name in currentRolenames and ("ignoredb" not in options or not options["ignoredb"]):
- trace("The role %s already exists, skipping grants" % name)
- else:
- modifyGrants(con, options, dbName, name, userInfo)
-
-def makeDatabase(con, options, dbName, dbOwnerRole, dbInfo, dbRoles):
- """
- Execute the SQL to create a database
-
- TODO: verify grants against what is actually there
- """
- ownerRole = dbInfo["ownerRole"]
- userRole = "{0}_common_user_role".format(dbName)
- viewerRole = "{0}_common_viewer_role".format(dbName)
-
- optionalDbExecute(con, options, "CREATE ROLE {0}".format(userRole))
- optionalDbExecute(con, options, "CREATE ROLE {0}".format(viewerRole))
-
- trace("Creating database %s with owner '%s'" % (dbName, ownerRole))
- optionalDbExecute(con, options, "CREATE DATABASE %s WITH OWNER %s" % (dbName, ownerRole))
- con2 = None
- try:
- con2 = dbConnect(database = dbName, user = options["dbuser"], password = options["dbpassword"], host = options["dbhost"])
-
- optionalDbExecute(con2, options, "REVOKE ALL on DATABASE {0} FROM {1}".format(dbName, viewerRole))
- optionalDbExecute(con2, options, "REVOKE ALL on DATABASE {0} FROM {1}".format(dbName, userRole))
- for name in dbRoles:
- userInfo = dbRoles[name]
- if userInfo["role"] == "writer" or userInfo["role"] == "reader":
- optionalDbExecute(con2, options, "REVOKE ALL on DATABASE {0} FROM {1}".format(dbName, name))
-
- # user can do everything viewer can
- optionalDbExecute(con2, options, "GRANT {0} TO {1}".format(viewerRole, userRole))
- # admin can do everything user and viewer can
- optionalDbExecute(con2, options, "GRANT {0} TO {1}".format(userRole, ownerRole))
-
- # viewer, user, admin can connect
- optionalDbExecute(con2, options, "GRANT CONNECT ON DATABASE {0} TO {1}".format(dbName, viewerRole))
-
- # create a schema we can optionally use *
- schemaName = "{0}_db_common".format(dbName)
- optionalDbExecute(con2, options, "CREATE SCHEMA if not exists {0} AUTHORIZATION {1}".format(schemaName, ownerRole))
-
- # search_path is not inherited, so set it here
- for role in [ ownerRole, userRole, viewerRole ]:
- optionalDbExecute(con2, options, "ALTER ROLE {1} IN DATABASE {0} SET search_path = public, {2}".format(dbName, role, schemaName))
-
- # viewer,user can select from schema
- optionalDbExecute(con2, options, "GRANT USAGE ON SCHEMA {0} TO {1}".format(schemaName, viewerRole))
- # admin can create on schema
- optionalDbExecute(con2, options, "GRANT CREATE ON SCHEMA {0} TO {1}".format(schemaName, ownerRole))
-
- # viewer, user, admin can select on tables
- optionalDbExecute(con2, options, "ALTER DEFAULT PRIVILEGES FOR ROLE {1} GRANT SELECT ON TABLES TO {0}".format(viewerRole, ownerRole))
- # user, admin can ins/upd/del/tru on tables
- optionalDbExecute(con2, options, "ALTER DEFAULT PRIVILEGES FOR ROLE {1} GRANT INSERT, UPDATE, DELETE, TRUNCATE ON TABLES TO {0}".format(userRole, ownerRole))
- # user, admin can update on sequences
- optionalDbExecute(con2, options, "ALTER DEFAULT PRIVILEGES FOR ROLE {1} GRANT USAGE, SELECT, UPDATE ON SEQUENCES TO {0}".format(userRole, ownerRole))
-
- # user, admin can create temp tables
- optionalDbExecute(con2, options, "GRANT TEMP ON DATABASE {0} TO {1}".format(dbName, userRole))
-
- for name in dbRoles:
- userInfo = dbRoles[name]
- if userInfo["role"] == "writer":
- optionalDbExecute(con2, options, "GRANT {0} TO {1}".format(userRole, name))
- elif userInfo["role"] == "reader":
- optionalDbExecute(con2, options, "GRANT {0} TO {1}".format(viewerRole, name))
-
- # search_path is not inherited, so set it here
- optionalDbExecute(con2, options, "ALTER ROLE {1} IN DATABASE {0} SET search_path = public, {2}".format(dbName, name, schemaName))
-
- except psycopg2.DatabaseError as e:
- die('Error %s' % e)
-
- finally:
- if con2:
- con2.commit()
- con2.close()
-
-def checkUsername(userName):
- """
- A value of type name is a string of 63 or fewer characters1. A name must start
- with a letter or an underscore; the rest of the string can contain letters,
- digits, and underscores.
- """
- trace("checkUsername(%s)" % userName)
- if re.match("[A-Za-z_][A-Za-z0-9_]*$", userName):
- return True
- else:
- errorPrint("%s is not a valid userName" % userName)
- return False
-
-def setupUser(con, options, dbName, userName, userInfo):
- """
- Do what is needed to to set up a user for a database
- """
- if checkUsername(userName):
- trace("For dbName='%s', create user '%s'" % (dbName, userName))
- userPassword = userInfo["password"]
- optionalDbExecute(con, options, "create user %s with password '%s'" % (userName, userPassword))
-
-def updatePassword(con, options, dbName, userName, userInfo):
- """
- Do what is needed to update a user's password
- """
- if checkUsername(userName):
- trace("For dbName='%s', alter user '%s' password" % (dbName, userName))
- userPassword = userInfo["password"]
- optionalDbExecute(con, options, "alter user %s with password '%s'" % (userName, userPassword))
-
-def modifyGrants(con, options, dbName, userName, userInfo):
- """
- Do what is needed to to set up a user for a database with the proper grants
-
- TODO: if user exist, verify current grants
- """
- if checkUsername(userName):
- userRole = userInfo["role"]
- trace("For dbName='%s', set up user '%s' as a '%s'" % (dbName, userName, userRole))
- if userRole == "writer":
- optionalDbExecute(con, options, "grant %s_common_user_role to %s" % (dbName, userName))
- elif userRole == "reader":
- optionalDbExecute(con, options, "grant %s_common_viewer_role to %s" % (dbName, userName))
- # elif userRole == "admin":
- # optionalDbExecute(con, options, "grant %s_common_admin_role to %s" % (dbName, userName))
- else:
- trace("nothing to grant %s" % userName)
-
-def optionalDbExecute(con, options, cmd):
- if "nocreate" in options and options["nocreate"]:
- print(cmd)
- else:
- audit("Running: " + cmd)
- dbExecute(con, cmd)
-
-"""
-database utility functions
-"""
-
-# def dbGetMap(con, cmd, args=[], skipTrace=False):
-# def dbGetOneRowMap(con, cmd, args=[], skipTrace=False):
-
-def dbGetFirstRowOneValue(con, cmd, args=[], skipTrace=False):
- """
- Do a select and return a single value from the first row
- """
- row = dbGetFirstRow(con, cmd, args, skipTrace)
- trace("row=" + str(row))
- if row is not None and len(row) > 0:
- return row[0]
- return None
-
-def dbGetFirstRow(con, cmd, args=[], skipTrace=False):
- """
- Do a select and return the values from the first row
- """
- cursor = dbExecute(con, cmd, args, skipTrace)
- return cursor.fetchone()
-
-def dbGetFirstColumn(con, cmd, args=[], skipTrace=False):
- """
- Do a select and return the first column's value from each row
- """
- ret = []
- cursor = dbExecute(con, cmd, args, skipTrace)
- for row in cursor:
- for col in row:
- ret.append(col)
- break
- return ret
-
-def dbGetFirstColumnAsMap(con, cmd, args=[], skipTrace=False, val=1):
- """
- Do a select and return the first column's value from each row
- """
- ret = {}
- cursor = dbExecute(con, cmd, args, skipTrace)
- for row in cursor:
- for col in row:
- ret[col] = val
- break
- return ret
-
-def dumpTable(con, tableName, max=-1):
- """
- If being extra verbose, print out the entire table
- """
- if verbose < 2:
- return
- print("================ " + tableName + " ================", file=traceOutput)
-
- cols = dbGetFirstColumn(con, "select column_name from information_schema.columns where table_name='" + tableName + "'", skipTrace=True)
- print("num", end="|", file=traceOutput)
- for col in cols:
- print(col, end="|", file=traceOutput)
- print("", file=traceOutput)
-
- if max > -1:
- cursor = dbExecute(con, "select * from " + tableName + " limit " + str(max), skipTrace=True)
- else:
- cursor = dbExecute(con, "select * from " + tableName, skipTrace=True)
- i = 0
- for row in cursor:
- print("%d" % i, end="|", file=traceOutput)
- i += 1
- for col in row:
- print("%s" % (col), end="|", file=traceOutput)
- print("", file=traceOutput)
- print("================================================", file=traceOutput)
-
-def dbExecute(con, statement, args=[], skipTrace=False):
- """
- Create a cursor, instantiate the arguments into a statement, trace print the statement, and execute the statement.
- Return the cursor
- """
- cursor = con.cursor()
- stmt = cursor.mogrify(statement, args);
- if not skipTrace:
- trace("executing:" + str(stmt))
- cursor.execute(stmt)
- global quiet
- if not skipTrace:
- trace("statusmessage=" + cursor.statusmessage + ", rowcount=" + str(cursor.rowcount))
- return cursor
-
-def dbConnect(database, user, password, host, autocommit = True):
- """
- Create a connection, logging it in the process
- Return the connection
- """
- trace("connecting to database %s as %s on host %s" % (database, user, host))
- con =psycopg2.connect(database = database, user = user, password = password, host = host)
- con.autocommit = autocommit
- return con
-
-"""
-Utility functions
-"""
-
-def die(msg):
- """
- Print a message to the error file and exit.
- """
- errorPrint(msg)
- sys.exit(1)
-
-def errorPrint(msg, file=errorOutput):
- """
- Print a message to the error file.
- """
- global errorLogger
- # print("----------------> errorLogger=%s" % str(errorLogger))
- if errorLogger is not None:
- errorLogger.error(msg)
- else:
- taggedPrint("ERROR", msg, file=file)
-
-
-def tracePrint(msg, file=traceOutput):
- """
- Print a message to the trace file.
- """
- global debugLogger
- # print("----------------> debugLogger=%s" % str(debugLogger))
- if debugLogger is not None:
- debugLogger.debug(msg)
- else:
- taggedPrint("DEBUG", msg, file=file)
-
-def taggedPrint(tag, msg, file):
- """
- Print a message to the trace file.
- """
- dt = time.strftime('%Y-%m-%d %T', time.localtime())
- print("%s %s: %s" % (dt, tag, msg), file=file)
-
-def requireOption(nm, val):
- """
- Die if a program parameter is not set
- """
- return require("option", nm, val)
-
-def requireJSON(prnm, dict, nm):
- """
- Die if a JSON value is not set
- """
- if nm not in dict:
- die("The JSON value '%s' is missing" % prnm)
- return dict[nm]
-
-def require(type, nm, val):
- """
- Die if a value is not set
- """
- if val is None:
- die("The %s '%s' is missing" % (type, nm))
- return val
-
-def trace(msg, minLevel=1):
- """
- Print a message to trace output if verbose is turned on.
- """
- global verbose
- if verbose >= minLevel:
- tracePrint(msg)
-
-def audit(msg):
- """
- Print a message to audit log if one is being used
- """
- global auditLogger
- if auditLogger is not None:
- auditLogger.info(msg)
-
-def getCdfPropValue(nm, encrypted=False, cfg="/opt/app/cdf/lib/cdf.cfg", dflt=None, skipComplaining=False):
- """
- Return a value from the configuration file /opt/app/cdf/lib/cdf.cfg
- """
- return getPropValue(nm=nm, encrypted=encrypted, cfg=cfg, dflt=dflt, skipComplaining=skipComplaining)
-
-def getPgaasPropValue(nm, encrypted=False, cfg="/opt/app/pgaas/lib/pgaas.cfg", dflt=None, skipComplaining=False):
- """
- Return a value from the configuration file /opt/app/pgaas/lib/pgaas.cfg
- """
- return getPropValue(nm=nm, encrypted=encrypted, cfg=cfg, dflt=dflt, skipComplaining=skipComplaining)
-
-getPropDict = { }
-
-def getPropValue(nm, encrypted=False, cfg=None, dflt=None, skipComplaining=False):
- """
- Return a value from the specified configuration file
- """
- if cfg is None:
- return None
- global getPropDict
- if getPropDict.get(cfg):
- savedDate = getPropDict[cfg]
- # trace("getPropValue: savedDate[" + cfg + "]=" + str(savedDate))
- cfgDate = os.path.getmtime(cfg)
- # trace("getPropValue: cfgDate=" + str(cfgDate))
- if float(savedDate) >= float(cfgDate): # cfg has not changed
- val = getPropDict.get(cfg + ":" + nm)
- # trace("getPropValue: val=" + val)
- if val is not None:
- # trace("getPropValue: getPropValue(saved) => '%s'" % str(val))
- return val
- else: # clear out any previously saved keys
- cfgcolon = cfg + ":"
- for k in list(getPropDict.keys()):
- if re.match(cfgcolon, k):
- del getPropDict[k]
- getPropValueProgram = '/opt/app/cdf/bin/getpropvalue'
- if encrypted:
- cmd = [getPropValueProgram, "-f", cfg, "-x", "-n", nm]
- else:
- cmd = [getPropValueProgram, "-f", cfg, "-n", nm]
- # trace("getPgaasPropValue: cmd=" + str(cmd))
-
- try:
- with subprocess.Popen(cmd,shell=False,stdout=subprocess.PIPE,stderr=subprocess.PIPE) as p:
- (origString, stderrString) = p.communicate()
- except Exception as e:
- traceback.print_exc()
- print("Error decoding string because {0}".format(e), file=errorOutput)
- return None
- else:
- if stderrString:
- if not re.search("Configuration property .* must be defined", stderrString.decode('utf-8')) and not skipComplaining:
- print("Error decoding string because: {0} ".format(stderr), file=errorOutput)
- return dflt
- else:
- trace("getPgaasPropValue() => " + str(origString), minLevel=2)
- return origString.decode('utf-8').rstrip('\n')
-
-if __name__ == "__main__":
- main()