diff options
Diffstat (limited to 'pgaas/src/stage/opt/app/pgaas/lib/iDNS-responder.py')
-rwxr-xr-x | pgaas/src/stage/opt/app/pgaas/lib/iDNS-responder.py | 1105 |
1 files changed, 0 insertions, 1105 deletions
diff --git a/pgaas/src/stage/opt/app/pgaas/lib/iDNS-responder.py b/pgaas/src/stage/opt/app/pgaas/lib/iDNS-responder.py deleted file mode 100755 index f295040..0000000 --- a/pgaas/src/stage/opt/app/pgaas/lib/iDNS-responder.py +++ /dev/null @@ -1,1105 +0,0 @@ -#!/usr/bin/env 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. - - -import http.server, socket -import time, os, sys, re, subprocess, traceback, html, base64, argparse -import psycopg2 -# TODO - move lots of code to a common library to share with other python modules -# sys.path.append("/opt/app/pgaas/lib") -# import dbtools - -DEF_HOST_NAME = os.popen("hostname -f").readlines()[0].strip() -DEF_PORT_NUMBER = 8000 - -validPerDbTables = [ "pg_tables", "pg_indexes", "pg_views" ] -topButton = " <font size='1'><a href='#'>^</a></font>" - -getLogDict = { } -def openLogFile(fname): - """ - Open a log file for append and remember the file descriptor. - Remember its inode/dev pair. - If either changes, reopen it. - """ - reopen = False - try: - curstat = os.stat(fname) - except: - reopen = True - global getLogDict - # print("top: reopen(%s)=%s" % (fname, reopen)) - if not reopen and getLogDict.get(fname): - # print("found getLogDict.get(" + fname + ")") - d = getLogDict[fname] - fd = d["fd"] if "fd" in d else None - oldstat = d["stat"] if "stat" in d else None - if fd is None: - reopen = True - elif oldstat is None: - reopen = True - elif oldstat.st_ino != curstat.st_ino or oldstat.st_dev != curstat.st_dev: - reopen = True - if reopen or not getLogDict.get(fname): - # print("closing old fd") - oldd = getLogDict.get(fname) - if oldd is not None: - oldfd = oldd.get("fd") - if oldfd is not None: - oldfd.close() - # print("reopening " + fname) - fd = open(fname, "a") - st = os.stat(fname) - getLogDict[fname] = { "fd": fd, "stat": st } - return getLogDict[fname]["fd"] - -def traceMsg(msg): - """ print a trace message. By default, this goes to trace.out """ - file = sys.stderr if testOn else openLogFile("/opt/logs/dcae/postgresql/idns/trace.log") - print(time.asctime(), msg, file=file) - file.flush() - -def errTrace(msg): - """ print an error message. By default, sys.stderr is rerouted to error.log """ - file = sys.stderr if testOn else openLogFile("/opt/logs/dcae/postgresql/idns/error.log") - sys.stderr = file - print(time.asctime(), msg, file=file) - file.flush() - -def debugTrace(msg): - """ print a debug message. By default, this goes to debug.log """ - if debugOn: - file = sys.stderr if testOn else openLogFile("/opt/logs/dcae/postgresql/idns/debug.log") - print(time.asctime(), msg, file=file) - file.flush() - -def readFile(file, defStr = None, mode = "r"): - """ read a file and return its contents """ - ret = defStr - try: - with open(file, mode) as f: - ret = f.read() - except Exception as e: - if defStr is not None: - ret = defStr - pass - else: - raise e - return ret - -def readFileBinary(file, defStr = None): - return readFile(file, defStr = defStr, mode = "rb") - -def readFileHtml(file, defStr = None): - """ read a file and return its contents, escaping anything important to HTML """ - return html.escape(readFile(file, defStr)) - -def readPipe(cmd, ignoreError = False): - """ read a pipe and return its contents """ - ret = "" - try: - with os.popen(cmd) as p: - ret = p.read() - except Exception as e: - if ignoreError: - pass - else: - raise e - return ret - -def readPipeHtml(file, defStr = None): - """ read a pipe and return its contents, escaping anything important to HTML """ - return html.escape(readPipe(file, defStr)) - -def readFileOrGz(file, defStr = None): - """ read a file and return its contents. If the file ends in .gz, use gunzip on it """ - if file.endswith(".gz"): - return readPipe("gunzip < '" + file + "'", defStr) - else: - return readFile(file, defStr) - -def readFileOrGzHtml(file, defStr = None): - """ read a file and return its contents, escaping anything important to HTML. If the file ends in .gz, use gunzip on it """ - return html.escape(readFileOrGz(file, defStr)) - -def getCdfPropValue(nm, encrypted=False, cfg="/opt/app/cdf/lib/cdf.cfg", dflt=None): - """ - Return a value from the configuration file /opt/app/cdf/lib/cdf.cfg - """ - return getPropValue(nm=nm, encrypted=encrypted, cfg=cfg, dflt=dflt) - -def getPgaasPropValue(nm, encrypted=False, cfg="/opt/app/pgaas/lib/pgaas.cfg", dflt=None): - """ - Return a value from the configuration file /opt/app/pgaas/lib/pgaas.cfg - """ - return getPropValue(nm=nm, encrypted=encrypted, cfg=cfg, dflt=dflt) - -getPropDict = { } - -def getPropValue(nm, encrypted=False, cfg=None, dflt=None): - """ - Return a value from the specified configuration file - """ - if cfg is None: - return None - global getPropDict - if getPropDict.get(cfg): - savedDate = getPropDict[cfg] - debugTrace("getPropValue: savedDate[" + cfg + "]=" + str(savedDate)) - cfgDate = os.path.getmtime(cfg) - debugTrace("getPropValue: cfgDate=" + str(cfgDate)) - if float(savedDate) >= float(cfgDate): # cfg has not changed - val = getPropDict.get(cfg + ":" + nm) - debugTrace("getPropValue: val=" + str(val)) - if val is not None: - debugTrace("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] - debugTrace("getPropValue: cmd=%s" % 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() - errTrace("Error decoding string because {0}".format(e)) - return None - else: - if stderrString: - if not re.search("Configuration property .* must be defined", stderrString.decode('utf-8')): # and dflt is not None: - errTrace("Error decoding string because: {0} ".format(stderrString)) - return dflt - else: - debugTrace("getPropValue() => '%s'" % str(origString)) - getPropDict[cfg] = os.path.getmtime(cfg) - val = origString.decode('utf-8').rstrip('\n') - debugTrace("getPropValue() => '%s'" % val) - getPropDict[cfg + ":" + nm] = val - return val - -def checkFileAge(full_path,number_of_days): - """ - return True if the file is >= number_of_days old from right now - """ - time_n_days_ago = time.time() - (number_of_days * 24 * 60 * 60) - stat = os.stat(full_path) - return time_n_days_ago >= stat.st_mtime - -def jumpTable(prefix, *args): - """ - Return a string consisting of a series of <a href='#prefix-xxx'>xxx</a>. - Include <font size='1'></font> around all of it. - """ - header = "<font size='1'>" - sep = "" - for table in args: - header = header + sep + "<a href='#" + prefix + "-" + table + "'>" + table + "</a> " - sep = " | " - header = header + "</font>" - return header - -def addFilenameHrefs(prefix, str): - """ - for each line in a list of filenames, change the last two elements of the path to an anchor. - """ - ret = "" - for line in str.splitlines(): - line = re.sub("/([^/]+)/([^/]+)$", '/\g<1>' + "/<a href='" + prefix + '\g<1>/\g<2>' + "'>" + '\g<2>' + "</a>", line) - ret = ret + line + "\n" - return ret - -def ifEmpty(str, defStr): - """ if a string is empty, return the defStr in its place """ - if str is None or str == "": - str = defStr - return str - -def isExe(fname): - """ check if a path exists and is executable """ - return os.path.exists(fname) and os.access(fname, os.X_OK) - -def replaceQuoteNewline(str): - return re.sub('"', "'", re.sub("\n", " ", str)) - -class MyHandler(http.server.BaseHTTPRequestHandler): - - def isServerUp(self): - """ - Check if the postgres server is up and running by calling pg_ctl and - looking for "server is running" (or "no server running"). - Then call ps -fu postgres and make sure we're not waiting on a master: - postgres 20815 20812 0 15:52 ? 00:00:00 postgres: startup process waiting for 000000010000000000000001 - """ - PGCTLPATH1 = "/usr/lib/postgresql/9.6/bin/pg_ctl" - PGCTLPATH2 = "/usr/lib/postgresql/9.5/bin/pg_ctl" - PGCTLPATH3 = "/opt/app/postgresql-9.5.2/bin/pg_ctl" - if isExe(PGCTLPATH1): - statusLines = readPipe(PGCTLPATH1 + " status -D /dbroot/pgdata/main/") - elif isExe(PGCTLPATH2): - statusLines = readPipe(PGCTLPATH2 + " status -D /dbroot/pgdata/main/") - else: - statusLines = readPipe(PGCTLPATH3 + " status -D /dbroot/pgdata/main/") - debugTrace("isServerUp(): statusLines = %s" % statusLines) - psLines = readPipe("ps -fu postgres") - debugTrace("isServerUp(): ps -fu postgres = %s" % psLines) - ret = len(statusLines) > 0 and re.search("server is running", statusLines, re.MULTILINE) and not re.search("startup process\\s+waiting", psLines, re.MULTILINE) - debugTrace("isServerUp(): returning = %s" % ret) - return ret - - def isRepmgrdUp(self): - """ - Check if the repmgrd server is up and running by calling "pgrep repmgrd" and - looking for a process id. - """ - statusLines = readPipe("pgrep repmgrd") - debugTrace("isServerUp(): statusLines = %s" % statusLines) - ret = len(statusLines) > 0 and re.search("[0-9]+", statusLines, re.MULTILINE) != None - debugTrace("isServerUp(): returning = %s" % ret) - return ret - - def isMaster(self): - """ - Check if the postgresql server is a master by asking the server if it is in recovery (meaning not a master) - """ - ret = None - con = None - try: - pwd = getCdfPropValue("postgres", True) - # debugTrace("using pwd=%s" % pwd) - con = psycopg2.connect(database = "postgres", user="postgres", password=pwd, host= HOST_NAME) - str = dbGetFirstRowOneValue(con, "select pg_is_in_recovery()") - debugTrace("pg_is_in_recovery() <= %s" % str) - ret = not str - - except psycopg2.DatabaseError as e: - errTrace('Database Error %s' % e) - - except Exception as e: - traceback.print_exc() - errTrace(str(e)) - - finally: - if con is not None: - con.close() - - debugTrace("isMaster(): returning = %s" % ret) - return ret - - def hasRepmgr(self): - """ - Check if the postgresql server is a master by asking the server if it is in recovery (meaning not a master) - """ - ret = None - con = None - try: - pwd = getCdfPropValue("postgres", True) - # debugTrace("using pwd=%s" % pwd) - con = psycopg2.connect(database = "postgres", user="postgres", password=pwd, host= HOST_NAME) - str = dbGetFirstRowOneValue(con, "select * from pg_database where datname = 'repmgr'") - debugTrace("repmgr database check() <= %s" % str) - ret = str - - except psycopg2.DatabaseError as e: - errTrace('Database Error %s' % e) - - except Exception as e: - traceback.print_exc() - errTrace(str(e)) - - finally: - if con is not None: - con.close() - - debugTrace("isMaster(): returning = %s" % ret) - return ret - - def isValidPgHost(self, host): - """ - Check a hostname against the list of nodes stored in the pgnodes CDF configuration file. - """ - pgnodes = getCdfPropValue("pgnodes", "").split('|') - ret = host in pgnodes - debugTrace("isValidPgHost(): looking for host='%s' in pgnodes='%s' => %s" % (host, str(pgnodes), ret)) - return ret - - def checkAuth(self): - """ - HTTP/1.1 401 Unauthorized - Date: Mon, 04 Feb 2014 16:50:53 GMT - WWW-Authenticate: Basic realm="WallyWorld" - - Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ== - """ - pswd = getCdfPropValue("wgetpswd", True) - b64pswd = base64.b64encode(("pgaas:" + pswd).encode("ascii")) - basicPlusPswd = "Basic %s" % b64pswd.decode("ascii") - - if self.headers['Authorization'] == None: - return False - elif self.headers['Authorization'] == basicPlusPswd: - return True - else: - return False - - def pgStatus(self, *pgargs): - """ return a table(s), using the system database of postgres """ - return self.pgStatusDBx("postgres", *pgargs) - - def pgStatusDB(self, DB, *pgargs): - """ return a table(s), using the given database """ - return self.pgStatusDBx(DB, *pgargs) - - def pgStatusDBx(self, DB, *pgargs): - """ return a table(s), using the given database """ - debugTrace("pgStatusDBx(DB=" + DB + ")") - con = None - ret = None - try: - con = psycopg2.connect(database = DB, user="postgres", password=getCdfPropValue("postgres", True), host= HOST_NAME) - ret = getTableHtmls(con, DB, pgargs) - - except psycopg2.DatabaseError as e: - errTrace('Database Error %s' % e) - - except Exception as e: - traceback.print_exc() - errTrace(str(e)) - - finally: - if con is not None: - con.close() - - return ret - - def do_HEAD(self): - """Respond to a HEAD request.""" - self.doHEADandGET(False) - - def do_GET(self): - """Respond to a GET request.""" - self.doHEADandGET(True) - - def doHEADandGET(self, sendMsg): - resp = 400 - msg = "" - sendBinary = False - contentType = "text/plain" - global debugOn - - if self.path == "/statusall": - self.path = "/all/status/pgstatus" - elif self.path == "/pgstatusall": - self.path = "/pgstatus" - - if self.path == '/ro': - if os.path.isfile("/var/run/postgresql/force-ro-off"): - isrw = "FORCE-RO-OFF" - elif os.path.isfile("/var/run/postgresql/force-ro-on"): - isrw = "Secondary" - else: - isrw = readFile("/var/run/postgresql/isrw", "n/a") - debugTrace("/ro: isrw returns %s" % isrw) - if re.match("Secondary", isrw) or re.match("Master", isrw): - resp = 200 - msg = "server is up" - else: - msg = "server is not up " + isrw - errTrace("/ro: isrw returns %s" % isrw) - - elif self.path == '/rw': - isrw = readFile("/var/run/postgresql/isrw", "n/a") - debugTrace("/rw: isrw returns %s" % isrw) - if re.match("Master", isrw): - resp = 200 - msg = "master server is up" - elif re.match("Secondary", isrw): - msg = "non-master server is up" - else: - msg = "server is not up " + isrw - errTrace("/ro: isrw returns %s" % isrw) - - elif self.path == '/isrw': - isrw = readFile("/var/run/postgresql/isrw", "n/a") - debugTrace("/isrw: returns %s" % isrw) - resp = 200 - msg = isrw - - elif self.path == '/healthcheck/status': - hs = readFile("/var/run/postgresql/check_cluster", "n/a") - debugTrace("/healthcheck/status: returns %s" % hs) - resp = 429 if hs == "n/a" else 200 - msg = '{ "output": "%s" }' % replaceQuoteNewline(hs) - - elif self.path == '/healthcheck/writablestatus': - isrw = readFile("/var/run/postgresql/isrw", "n/a") - debugTrace("/rw: isrw returns %s" % isrw) - resp = 429 - if re.match("Master", isrw): - resp = 200 - msg = '{ "output": "master server is up" }' - elif re.match("Secondary", isrw): - msg = '{ "output": "non-master server is up" }' - else: - msg = '{ "output": "server is not up %s" }' % replaceQuoteNewline(isrw) - errTrace("/ro: isrw returns %s" % isrw) - - elif self.path == '/healthcheck/readonlystatus': - if os.path.isfile("/var/run/postgresql/force-ro-off"): - isrw = "FORCE-RO-OFF" - elif os.path.isfile("/var/run/postgresql/force-ro-on"): - isrw = "Secondary" - else: - isrw = readFile("/var/run/postgresql/isrw", "n/a") - debugTrace("/ro: isrw returns %s" % isrw) - resp = 429 - if re.match("Secondary", isrw) or re.match("Master", isrw): - resp = 200 - msg = '{ "output": "server is up" }' - else: - msg = '{ "output": "server is not up %s" }' % replaceQuoteNewline(isrw) - errTrace("/ro: isrw returns %s" % isrw) - - elif self.path == '/': - ui = readFile("/opt/app/pgaas/man/iDNS-responder.swagger.json", "n/a") - debugTrace("/: returns %s" % ui) - msg = ui - if ui != "n/a": - resp = 200 - contentType = "application/json" - - elif not self.checkAuth(): - resp = 401 - msg = "not authenticated" - - elif self.path == '/ismaster': - masterYes = self.isMaster() - msg = "" - if masterYes: - resp = 200 - msg = "master server" - else: - msg = "non-master server" - - elif self.path == '/issecondary': - masterYes = self.isMaster() - msg = "" - if not masterYes: - resp = 200 - msg = "secondary server" - else: - msg = "non-secondary server" - - elif self.path == '/ismaintenance': - msg = "" - if os.path.exists("/var/run/postgresql/inmaintenance"): - resp = 200 - msg = "in maintenance mode" - else: - msg = "not in maintenance mode" - - elif self.path == '/getpubkey': - try: - resp = 200 - msg = readFile(os.path.expanduser("~postgres/.ssh/id_rsa.pub")) - except: - traceback.print_exc() - resp = 404 - msg = "key does not exist" - - elif re.match("/getssh/", self.path): - # getssh/hostname - push ssh pub/private keys across - host = re.sub("^/getssh/", "", self.path) - debugTrace("#1: /getssh/ host='%s'" % host) - host = re.sub("[^a-zA-Z0-9_.-]", "", host) - debugTrace("#2: /getssh/ host='%s'" % host) - if self.isValidPgHost(host): - p = readPipe("scp -o StrictHostKeyChecking=no -i ~postgres/.ssh/id_rsa ~postgres/.ssh/id_rsa* postgres@" + host + ":.ssh/ 2>&1") - debugTrace("#3: /getssh/ to '%s' returns '%s'" % (host, p)) - msg = "OK " + p - resp = 200 - else: - msg = "NOT OK INVALID HOST" - resp = 404 - - elif re.match("/getcdf/", self.path): - # getcdf/hostname - push cdf.cfg file across - fi = "/opt/app/cdf/lib/cdf.cfg" - # make sure that the file exists and contains the encrypted postgres password - if re.search("postgres.x", readFile(fi, "n/a")) and re.search("repmgr.x", readFile(fi, "n/a")): - host = re.sub("^/getcdf/", "", self.path) - debugTrace("#1: /getcdf/ host='%s'" % host) - host = re.sub("[^a-zA-Z0-9_.-]", "", host) - debugTrace("#2: /getcdf/ host='%s'" % host) - if self.isValidPgHost(host): - p = readPipe("scp -o StrictHostKeyChecking=no -i ~postgres/.ssh/id_rsa " + fi + " postgres@" + host + ":/opt/app/cdf/lib/cdf.cfg 2>&1") - debugTrace("#3: /getcdf/ to '%s' returns '%s'" % (host, p)) - msg = "OK " + p - resp = 200 - else: - msg = "NOT OK INVALID HOST" - resp = 404 - else: - msg = "NOT OK YET" - resp = 404 - - elif self.path == '/hasrepmgr': - repmgrYes = self.hasRepmgr() - msg = "" - if repmgrYes: - resp = 200 - msg = "OK" - else: - msg = "NOT OK YET" - - elif self.path == '/status': - resp = 200 - contentType = "text/html" - isServerUp = self.isServerUp() - isRepmgrdUp = self.isRepmgrdUp() - isMaster = self.isMaster() - color = "green" if (isServerUp and isRepmgrdUp) else "yellow" if (isServerUp or isRepmgrdUp) else "red" - dashed = "solid" if isMaster else "dashed" - jump = jumpTable("status", "ps", "repmgr", "df", "uptime", "loadavg", "cpuinfo", "meminfo", "pgaas-failures", "pgaas-inst-report", "nslookup", "ip-addr-show", "who-br") - - msg = """<table style='border: 10px %s %s' width='100%%'><tr><td> - <b>isServerUp</b> %s - <b>isRepmgrdUp</b> %s - <b>isMaster</b> %s - <b>isrw</b> %s %s\n<br/> - %s - <h2><a name='status-ps'>ps</a>%s</h2>\n<pre>\n%s\n</pre>\n - <h2><a name='status-repmgr'>repmgr cluster show</a>%s</h2>\n<pre>\n%s\n</pre>\n - <h2><a name='status-df'>df</a>%s</h2>\n<pre>\n%s\n</pre>\n - <h2><a name='status-uptime'>uptime</a>%s</h2>\n<pre>\n%s\n</pre>\n - <h2>/proc/uptime%s</h2>\n<pre>\n%s\n</pre>\n - <h2><a name='status-loadavg'>loadavg</a>%s</h2>\n<pre>\n%s\n</pre>\n - <h2><a name='status-cpuinfo'>cpuinfo</a>%s</h2>\n<pre>\n%s\n</pre>\n - <h2><a name='status-meminfo'>meminfo</a>%s</h2>\n<pre>\n%s\n</pre>\n - <h2><a name='status-pgaas-failures'>pgaas-failures</a>%s</h2>\n<pre>\n%s\n</pre>\n - <h2><a name='status-pgaas-inst-report'>pgaas.inst.report</a>%s</h2>\n<pre>\n%s\n</pre>\n - <h2><a name='status-nslookup'>nslookup</a>%s</h2>\n<pre>\n%s\n</pre>\n - <h2><a name='status-ip-addr-show'>ip addr</a>%s</h2>\n<pre>\n%s\n</pre>\n - <h2><a name='status-who-br'>who -br</a>%s</h2>\n<pre>\n%s\n</pre>\n - </td></tr></table>""" % (color, dashed, isServerUp, isRepmgrdUp, isMaster, - readFileHtml("/var/run/postgresql/isrw", "n/a"), - readPipeHtml("hostname -f"), jump, - topButton, readPipeHtml("ps -fu postgres"), - topButton, readPipeHtml("/opt/app/pgaas/bin/repmgrc cluster show"), - topButton, readPipeHtml("df -h"), - topButton, readPipeHtml("uptime", defStr="n/a"), - topButton, readFileHtml("/proc/uptime", defStr="n/a"), - topButton, readFileHtml("/proc/loadavg", defStr="n/a"), - topButton, readFileHtml("/proc/cpuinfo", defStr="n/a"), - topButton, readFileHtml("/proc/meminfo", defStr="n/a"), - topButton, readFileHtml("/tmp/pgaas-failures", defStr="n/a"), - topButton, readFileHtml("/tmp/pgaas.inst.report", defStr="n/a"), - topButton, readPipeHtml("nslookup $(hostname -f)", defStr="n/a"), - topButton, readPipeHtml("ip addr show", defStr="n/a"), - topButton, readPipeHtml("who -br", defStr="n/a")) - - elif self.path == '/stoplight': - isServerUp = self.isServerUp() - isRepmgrdUp = self.isRepmgrdUp() - isMaster = self.isMaster() - color = "green" if (isServerUp and isRepmgrdUp) else "yellow" if (isServerUp or isRepmgrdUp) else "red" - masterSecondary = "master" if isMaster else "secondary" - sendBinary = True - contentType = "image/gif" - msg = readFileBinary("/opt/app/postgresql/lib/gif/stoplight-" + masterSecondary + "-" + color + ".gif", "") - - elif re.match("/perdb-", self.path): - # /perdb- - rest = re.sub("^/perdb-", "", self.path) - debugTrace("#1: /perdb- others='%s'" % rest) - rest = re.sub("[^a-zA-Z0-9_./-]", "", rest) - debugTrace("#2: /perdb- rest='%s'" % rest) - pgothers = [ x for x in rest.split('-') if x in validPerDbTables ] - resp = 200 - contentType = "text/html" - con = None - try: - pwd = getCdfPropValue("postgres", True) - con = psycopg2.connect(database = "postgres", user="postgres", password=pwd, host= HOST_NAME) - databases = dbGetFirstColumn(con, "select datname from pg_database") - debugTrace("after select datname from pg_database") - databases[:] = [DB for DB in databases if not re.match("template[0-9]", DB)] - msg = msg + jumpTable("db", *databases) + "<br/>" - for DB in databases: - debugTrace("looking at DB=" + DB) - msg = msg + "<h1><a name='db-" + DB + "'>" + DB + "</a>" + topButton + "</h1>\n" - msg = msg + jumpTable(DB + "-table", *pgothers) - msg = msg + self.pgStatusDB(DB, *pgothers) - - except psycopg2.DatabaseError as e: - errTrace('Database Error %s' % e) - msg = "DB is down" - - except Exception as e: - traceback.print_exc() - errTrace(str(e)) - - finally: - if con is not None: - con.close() - - elif self.path == '/pgstatus': - tables = [ "pg_stat_activity", "pg_stat_archiver", "pg_stat_bgwriter", "pg_stat_database", "pg_stat_database_conflicts", "pg_stat_user_tables", "pg_stat_user_indexes", "pg_statio_user_tables", "pg_statio_user_indexes", "pg_statio_user_sequences", "pg_roles", "pg_database", "pg_tables", "pg_namespace", "pg_roles", "pg_group" ] - header = jumpTable("postgres-table", *tables) - msg = self.pgStatus(*tables) - if msg is not None: - contentType = "text/html" - resp = 200 - msg = header + msg - - elif self.path == '/pg_stat_activity': - msg = self.pgStatus("pg_stat_activity") - if msg is not None: - contentType = "text/html" - resp = 200 - - elif self.path == '/pg_stat_archiver': - msg = self.pgStatus("pg_stat_archiver") - if msg is not None: - contentType = "text/html" - resp = 200 - - elif self.path == '/pg_stat_bgwriter': - msg = self.pgStatus("pg_stat_bgwriter") - if msg is not None: - contentType = "text/html" - resp = 200 - - elif self.path == '/pg_stat_database': - msg = self.pgStatus("pg_stat_database") - if msg is not None: - contentType = "text/html" - resp = 200 - - elif self.path == '/pg_stat_database_conflicts': - msg = self.pgStatus("pg_stat_database_conflicts") - if msg is not None: - contentType = "text/html" - resp = 200 - - elif self.path == '/pg_stat_user_tables': - msg = self.pgStatus("pg_stat_user_tables") - if msg is not None: - contentType = "text/html" - resp = 200 - - elif self.path == '/pg_stat_user_indexes': - msg = self.pgStatus("pg_stat_user_indexes") - if msg is not None: - contentType = "text/html" - resp = 200 - - elif self.path == '/pg_statio_user_tables': - msg = self.pgStatus("pg_statio_user_tables") - if msg is not None: - contentType = "text/html" - resp = 200 - - elif self.path == '/pg_statio_user_indexes': - msg = self.pgStatus("pg_statio_user_indexes") - if msg is not None: - contentType = "text/html" - resp = 200 - - elif self.path == '/pg_statio_user_sequences': - msg = self.pgStatus("pg_statio_user_sequences") - if msg is not None: - contentType = "text/html" - resp = 200 - - elif self.path == '/pg_roles': - msg = self.pgStatus("pg_roles") - if msg is not None: - contentType = "text/html" - resp = 200 - - elif self.path == '/pg_database': - msg = self.pgStatus("pg_database") - if msg is not None: - contentType = "text/html" - resp = 200 - - elif self.path == '/pg_tables': - msg = self.pgStatus("pg_tables") - if msg is not None: - contentType = "text/html" - resp = 200 - - elif self.path == '/pg_namespace': - msg = self.pgStatus("pg_namespace") - if msg is not None: - contentType = "text/html" - resp = 200 - - elif self.path == '/pg_group': - msg = self.pgStatus("pg_group") - if msg is not None: - contentType = "text/html" - resp = 200 - - elif re.match("/all/", self.path) or re.match("/small/", self.path): - if re.match("/small/", self.path): - height = 40 - else: - height = 400 - # /all/others - rest = re.sub("^/all/", "", self.path) - rest = re.sub("^/small/", "", self.path) - rest = re.sub("[^a-zA-Z0-9_./-]", "", rest) - debugTrace("/all/ rest='%s'" % rest) - others = rest.split('/') - try: - resp = 200 - contentType = "text/html" - pgnodes = getCdfPropValue("pgnodes", "").split('|') - msg = msg + jumpTable("node", *pgnodes) - for node in pgnodes: - hnode = html.escape(node) - msg = msg + "<h2><a name='node-" + hnode + "'>" + hnode + "</a>" + topButton + "</h2>\n" - msg = msg + jumpTable(hnode + "-other", *others) - for other in others: - msg = msg + "<h3><a name='" + hnode + "-other-" + other + "'>" + other + "</a>" + topButton + "</h3>\n" - msg = msg + "<iframe src='http://" + hnode + ":" + str(PORT_NUMBER) + "/" + other + "' frameborder='1' scrolling='yes' height='" + str(height) + "' width='1200'></iframe>\n" - except Exception as e: - traceback.print_exc() - errTrace(str(e)) - - # ATT-ONLY CODE BEGIN - elif self.path == '/swmstatus': - resp = 200 - contentType = "text/html" - msg = "<pre>\n%s\n</pre>" % readPipeHtml("set -x;sed -n '/^STARTING/,/^ENDING/p' /opt/app/aft/aftswmnode/stage/*/com/att/ecomp/dcae/storage/pgaas/*/*/proc_out") - # ATT-ONLY CODE END - - elif self.path == '/debugon': - msg = "ON" - resp = 200 - debugOn = True - - elif self.path == '/debugoff': - msg = "OFF" - resp = 200 - debugOn = False - - elif self.path == '/log' or self.path == '/log/': - msg = "<h2>%s</h2><pre>\n%s\n</pre>" % (self.path, addFilenameHrefs("/log/", readPipeHtml("ls -l /opt/logs/dcae/postgresql/*/*"))) - resp = 200 - contentType = "text/html" - - elif self.path == '/mlog' or self.path == '/mlog/': - # /opt/app/dcae-controller-service-common-vm-manager/logs - msg = "<h2>%s</h2><pre>\n%s\n</pre>" % (self.path, addFilenameHrefs("/mlog/", readPipeHtml("ls -l /opt/app/dcae-controller-service-common-vm-manager/logs/*"))) - resp = 200 - contentType = "text/html" - - elif self.path == '/tmp' or self.path == '/tmp/': - msg = "<h2>%s</h2><pre>\n%s\n</pre>" % (self.path, addFilenameHrefs("/tmp/", readPipeHtml("ls -l /tmp/*"))) - resp = 200 - contentType = "text/html" - - elif re.match("/log/", self.path): - # /log/dir/path - rest = re.sub("^/log/", "", self.path) - debugTrace("#1: /log/ others='%s'" % rest) - rest = re.sub("[^a-zA-Z0-9_./-]", "", rest) - rest = re.sub("/[.][.]/", "", rest) - debugTrace("#2: /log/ rest='%s'" % rest) - msg = "<h2>%s</h2><pre>\n%s\n</pre>" % (rest, ifEmpty(readFileOrGzHtml("/opt/logs/dcae/postgresql/" + rest, "n/a"), "<i>empty</i>")) - resp = 200 - contentType = "text/html" - - elif re.match("/mlog/", self.path): - # /log/dir/path - rest = re.sub("^/mlog/", "", self.path) - debugTrace("#1: /mlog/ others='%s'" % rest) - rest = re.sub("[^a-zA-Z0-9_./-]", "", rest) - rest = re.sub("/[.][.]/", "", rest) - rest = re.sub("^logs/", "", rest) - debugTrace("#2: /mlog/ rest='%s'" % rest) - msg = "<h2>%s</h2><pre>\n%s\n</pre>" % (rest, ifEmpty(readFileOrGzHtml("/opt/app/dcae-controller-service-common-vm-manager/logs/" + rest, "n/a"), "<i>empty</i>")) - resp = 200 - contentType = "text/html" - - elif re.match("/tmp/", self.path): - # /log/dir/path - rest = re.sub("^/tmp/", "", self.path) - debugTrace("#1: /tmp/ others='%s'" % rest) - rest = re.sub("[^a-zA-Z0-9_./-]", "", rest) - rest = re.sub("/[.][.]/", "", rest) - rest = re.sub("^tmp/", "", rest) - debugTrace("#2: /tmp/ rest='%s'" % rest) - msg = "<h2>%s</h2><pre>\n%s\n</pre>" % (rest, ifEmpty(readFileOrGzHtml("/tmp/" + rest, "n/a"), "<i>empty</i>")) - resp = 200 - contentType = "text/html" - - elif self.path == '/oldro': - serverYes = self.isServerUp() - if serverYes: - resp = 200 - msg = "server is up" - else: - msg = "server is not up" - - elif self.path == '/oldrw': - serverYes = self.isServerUp() - masterYes = self.isMaster() - msg = "" - if serverYes: - if masterYes: - resp = 200 - msg = "master server is up" - elif masterYes is not None: - msg = "non-master server is up" - else: - msg = "master status is up, but not answering" - else: - if masterYes: - msg = "status is down, but responded as master server" - elif masterYes is not None: - msg = "status is down, but responded as non-master" - else: - msg = "status is down, server is not up" - - elif self.path == "/help": - resp = 200 - contentType = "text/html" - msg = """<pre> - <a href='/statusall'>statusall</a> == all/status/pgstatus - <a href='/ro'>ro</a> == iDNS readonly - <a href='/rw'>rw</a> == iDNS readwrite - <a href='/isrw'>isrw</a> == /var/run/postgresql/isrw - <a href='/ismaster'>ismaster</a> == is master - <a href='/issecondary'>issecondary</a> == is secondary - <a href='/ismaintenance'>ismaintenance</a> == is in maintenance mode - <a href='/healthcheck/status'>healthcheck/status</a> == Consul healthcheck - <a href='/healthcheck/writablestatus'>healthcheck/writablestatus</a> == Consul writable healthcheck - <a href='/healthcheck/readonlystatus'>healthcheck/readonlystatus</a> == Consul readonly healthcheck - <a href='/getpubkey'>getpubkey</a> == retrieve public key - <a href='/hasrepmgr'>hasrepmgr</a> == repmgr id and database are set up - <a href='/status'>status</a> == lots of info - <a href='/perdb-pg_tables-pg_indexes-pg_views'>perdb</a>-{<a href='/perdb-pg_tables'>pg_tables</a>-<a href='/perdb-pg_indexes'>pg_indexes</a>-<a href='/perdb-pg_views'>pg_views</a>} == info per DB - <a href='/pgstatus'>pgstatus</a> == lots of database info - <a href='/pg_stat_activity'>pg_stat_activity</a>, <a href='/pg_stat_archiver'>pg_stat_archiver</a>, <a href='/pg_stat_bgwriter'>pg_stat_bgwriter</a>, - <a href='/pg_stat_database'>pg_stat_database</a>, <a href='/pg_stat_database_conflicts'>pg_stat_database_conflicts</a>, <a href='/pg_stat_user_tables'>pg_stat_user_tables</a>, - <a href='/pg_stat_user_indexes'>pg_stat_user_indexes</a>, <a href='/pg_statio_user_tables'>pg_statio_user_tables</a>, <a href='/pg_statio_user_indexes'>pg_statio_user_indexes</a>, - <a href='/pg_statio_user_sequences'>pg_statio_user_sequences</a>, - <a href='/pg_roles'>pg_roles</a>, <a href='/pg_database'>pg_database</a>, - <a href='/pg_tables'>pg_tables</a>, <a href='/pg_namespace'>pg_namespace</a>, - <a href='/pg_group'>pg_group</a>, - <a href='/swmstatus'>swm proc_out files</a> - <a href='/log'>log</a> == ls -l logs - log/foo == log foo - <a href='/mlog'>mlog</a> == ls -l manager logs - mlog/foo == mlog foo - <a href='/tmp'>tmp</a> == ls -l /tmp - </pre>""" - else: - resp = 404 - msg = "path does not exist" - - # TODO == encode msg when binary - if sendBinary: - debugTrace("%s: Returning %d/%d/%s" % (self.path, resp, len(msg), "--binary--")) - else: - debugTrace("%s: Returning %d/%d/%s" % (self.path, resp, len(msg), msg)) - traceMsg("- %s - \"%s %s %s\" %d %d" % (self.client_address[0], self.command, self.path, self.request_version, resp, len(msg))) - self.send_response(resp) - if resp == 401: - self.send_header('WWW-Authenticate', 'Basic realm="PGaaS"') - self.send_header("Content-type", contentType) - self.end_headers() - if sendMsg: - if msg is None: - msg = "" - if sendBinary: - self.wfile.write(msg) - else: - self.wfile.write((msg + "\n").encode("utf-8")) - sys.stderr.flush() - -""" -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) - debugTrace("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 - traceOutput = sys.stderr if testOn else openLogFile("/opt/logs/dcae/postgresql/idns/debug.log") - 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 getTableHtmls(con, DB, tableNames): - """ - Retrieve a dump of all specified tables, in HTML format - """ - ret = "" - for tn in tableNames: - ret = ret + getTableHtml(con, DB, tn) - return ret - -def getTableHtml(con, DB, tableName, max=-1): - """ - Retrieve a dump of a given table, in HTML format - """ - # errTrace("getting %s" % str(tableName)) - ret = "<h2><a name='" + DB + "-table-" + tableName + "'>" + DB + " " + tableName + "</a>" + topButton + "</h2>\n" - ret = ret + "<table border='1'>\n" - # ret = ret + "<tr><th colspan='" + str(len(cols)+1) + "'>" + tableName + "</th></tr>\n" - cols = dbGetFirstColumn(con, "select column_name from information_schema.columns where table_name='" + tableName + "'", skipTrace=True) - - ret = ret + "<tr><th>num</th>" - for col in cols: - ret = ret + "<th>" + str(col) + "</th>" - ret = ret + "</tr>\n" - - 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: - ret = ret + "<tr><th>" + str(i) + "</th>" - i = i + 1 - for col in row: - ret = ret + "<td>" + str(col) + "</td>" - ret = ret + "</tr>\n" - ret = ret + "</table>\n" - return ret - -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: - debugTrace("executing:" + str(stmt)) - cursor.execute(stmt) - if not skipTrace: - debugTrace("statusmessage=" + cursor.statusmessage + ", rowcount=" + str(cursor.rowcount)) - return cursor - -class HTTPServerIPv6(http.server.HTTPServer): - address_family = socket.AF_INET6 - -if __name__ == '__main__': - parser = argparse.ArgumentParser(description="Respond to HTTP requests") - parser.add_argument("-d","--debug",help="turn on debugging",action="store_true") - parser.add_argument("-t","--test",help="turn on test mode",action="store_true") - parser.add_argument("-H", "--host", type=str, help="Hostname, defaults to '%s'" % DEF_HOST_NAME, - default=DEF_HOST_NAME) - parser.add_argument("-P", "--port", type=int, help="Port to listen on, defaults to '%d'" % DEF_PORT_NUMBER, - default=DEF_PORT_NUMBER) - args = parser.parse_args() - - global debugOn, testOn, HOST_NAME, PORT_NUMBER - debugOn = args.debug - testOn = args.test - HOST_NAME = args.host - PORT_NUMBER = args.port - - if not debugOn: - sys.stderr = openLogFile("/opt/logs/dcae/postgresql/idns/error.log") - - # server_class = http.server.HTTPServer - # httpd = server_class(("0.0.0.0", PORT_NUMBER), MyHandler) - server_class = HTTPServerIPv6 - httpd = server_class(("", PORT_NUMBER), MyHandler) - errTrace("Server Starts - %s:%s" % (HOST_NAME, PORT_NUMBER)) - try: - httpd.serve_forever() - except KeyboardInterrupt: - pass - httpd.server_close() - errTrace("Server Stops - %s:%s" % (HOST_NAME, PORT_NUMBER)) |