Saturday, October 6, 2018

Maintenance Portal | For use with OBIEE or other BI Tools (Uses Python)

So very recently I came across a Development Request with one of my clients, more sort of an issue where they wanted to show a Maintenance Portal when there BI Application like OBIEE is unable to handle requests when the back end Database is down.

This Maintenance Portal would be mostly static, one HTML page , which would display a banner saying application is under maintenance. Something like below:




Seems simple hunh..?

Problem really was when your BI Tool, like OBIEE for e.g. is unable to handle requests or is down, it won't be able to show even a simple web page like this. Probably because the web server that came with it is also down.

Solution... Python comes to rescue!!

Recently I came to know that Python has a capability to host web pages using a light weight HTTP server which comes with it. All you have to do is run below command:

python -m http.server 8000

where 8000 is the port at which the Python HTTP Server will run. It will show all contents of the directory in web format from where this is run or would show an HTML page if you have Index.html file present in that folder.


So this is command line, how do we do this in a script??

Same thing as above you could invoke from a Python Script (.py). I give here a sample script which I used in my project to show Maintenance Portal and check Database for connectivity.

webserver.py
import os
import time
import argparse
import threading
import http.server
import socketserver
import cx_Oracle #Need to install this package using Python PIP
from datetime import datetime

#Define Defaults
PORT = 80
NUM_SECS_TO_WAIT = 900 #Checks every 15 minutes

parser = argparse.ArgumentParser()
parser.add_argument("--dir", "-d", default=os.getcwd(), type=str)
parser.add_argument("--tns", "-t", default="", type=str)
parser.add_argument("--user", "-u", default="", type=str)
parser.add_argument("--passw", "-p", default="", type=str)
args = parser.parse_args()

server_address = ("", PORT)
os.chdir(args.dir)
 
class MyRequestHandler(http.server.SimpleHTTPRequestHandler):
 def do_GET(self):
  if not (self.path == "/CompanyLogo.png" or self.path == "/favicon.ico"):
   self.path = "/index.html"
  return http.server.SimpleHTTPRequestHandler.do_GET(self)

httpd = socketserver.TCPServer(server_address, MyRequestHandler)
httpd.allow_reuse_address = True

# Thread start Http Server, this is where the magic happens
thread1 = threading.Thread(target = httpd.serve_forever)
thread1.daemon = True
thread1.start()

con_str = args.user + "/" + args.passw + "@" + args.tns
con_status = ""

# Wait initially 2 time before checking database for connectivity
time.sleep(NUM_SECS_TO_WAIT)
time.sleep(NUM_SECS_TO_WAIT)

while con_status != "success":
 try:
  con = cx_Oracle.connect(con_str)
  cur = con.cursor()
  cur.execute("SELECT SYSDATE FROM DUAL")
  rec = cur.fetchmany(numRows=1)
  if rec is not None:
   print(datetime.now().strftime("%Y-%m-%d %H:%M:%S"), "- Connection Succeeded")
   con_status = "success"
   httpd.shutdown()
  else:
   print(datetime.now().strftime("%Y-%m-%d %H:%M:%S"), "- Connection Failed, Trying Again in ", NUM_SECS_TO_WAIT, " seconds")
   con_status = "failed"
   time.sleep(NUM_SECS_TO_WAIT)
  cur.close()
  con.close()
 except:
  print(datetime.now().strftime("%Y-%m-%d %H:%M:%S"), "- Connection Failed, Trying Again in ", NUM_SECS_TO_WAIT, " seconds")
  con_status = "failed"
  time.sleep(NUM_SECS_TO_WAIT)




I scheduled this script on my Windows Server in Task Scheduler using below wrapper script.

Maintenance_Batch.cmd
@ECHO OFF

ECHO  
ECHO %DATE% Stopping OBIEE Services...
call "D:\Obiee\scripts\stop.cmd"
ECHO %DATE% ...Stopped OBIEE Services
ECHO  

ECHO  
ECHO %DATE% Starting Maintenance Portal...
D:\Python\Python37\python.exe D:\Maintenance_Portal\webserver.py %*
ECHO %DATE% ...Stopping Maintenance Portal
ECHO  

ECHO  
ECHO %DATE% Starting OBIEE Services...
call "D:\Obiee\scripts\start.cmd"
ECHO %DATE% ...Started OBIEE Services
ECHO  



I invoke the above wrapper script using below command:

Maintenance_Batch.cmd --dir "D:\Maintenance_Portal" --tns DBTNS --user DBUSER --passw DBPASS

D:\Maintenance_Portal is where my Index.html is present.

So once this script becomes active, it stops the Application Server (OBIEE in my case), Starts Maintenance Portal on port 80, keeps checking Database for connectivity every 15 minutes and as soon as it is up it Stops the Maintenance Portal and Starts the Application Server.

You could obviously tweak around the script more to achieve a custom functionality but in really a few simple steps and by using the HTTP Server provided by Python, you could very easily host a Maintenance portal of your own.