#!/usr/bin/env python
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements.  See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership.  The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file 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.

# pxf_manual_failover.py
# This python script will adapt the PXF external tables to the new NameNode in case
# of High Availability manual failover. 
# The script receives as input the new namenode host and then goes over each external
# table entry in the catalog table pg_exttable and updates the LOCATION field - 
# replaces the old Namenode host with the new one. 

import sys
from gppylib.db import dbconn

def wrongUsage():
   'Print usage string and leave'
   print "usage: pxf_manual_failover <new_namenode_host> <database> [-h <hawq_master_host>] [-p <hawq_master_port>]"
   exit()

def getNewHost():
   'reads new NameNode from command line - exits if wrong input'
   if len(sys.argv) < 2:
      wrongUsage()
   return sys.argv[1]
   
def getDatabase():
   'reads database from command line - exits if wrong input'
   if len(sys.argv) < 3:
      wrongUsage()
   return sys.argv[2]
   
def getOptionalInput(flag, default):
   """generic function - retrieves optional parameters from the input
      If [flag <value>] is not on the command line, we use default 
      Explaining the parsing. This is how the command line string that
	  sys.argv returns, looks like:
	  ['./pxf_manual_failover.py', 'localhost', 'films', '-h', 'isenshackamac.corp.emc.com', '-p', '5432']
   """
   input = list(sys.argv)
   if input.count(flag) == 0:
      return default
   flag_idx = input.index(flag)
   if len(input) < flag_idx +1:
      wrongUsage()
   return input[flag_idx + 1]
   
def getMasterHost():
   'reads hawq_master_host from command line - optional'
   return getOptionalInput("-h", "localhost")
   
def getMasterPort():
   'reads hawq_master_port from command line - optional'
   return getOptionalInput("-p", 5432)
      
def isPxfTable(location):
   'decide if this is a PXF table by analyzing the LOCATION field for the table entry in pg_exttable'
   return cmp(location[1:7],"pxf://") == 0

def makeNewLocation(new_host, location):
   'replaces [host] substring in [location] with [new_host]'
   start = location.find("//")
   end = location.find(":", start)
   size = len(location)
   new_location = location[:start] + "//" + new_host + location[end:size]
   return new_location
   
def promptUser(new_host, database, hawq_master_host, hawq_master_port):
   'Give user a last chance to change his mind'
   print "Will replace the current Namenode hostname with [" + new_host + "] in database [" + database + "]"
   print "Hawq master is: [" + hawq_master_host + "] and Hawq port is: [" + str(hawq_master_port) + "]"
   reply = raw_input('Do you wish to continue: Yes[Y]/No[N] ?')
   reply = reply.lower()
   if not(cmp(reply, 'yes') == 0 or cmp(reply, 'y') == 0):
      print "User decided to cancel operation. Leaving..."
      exit() 

def connectToDb(hawq_master_host, hawq_master_port, database):
   'connect to database'
   url = dbconn.DbURL(hawq_master_host
                     ,port = hawq_master_port
				     ,dbname = database
                     )
   return dbconn.connect(dburl   = url)
   
def updateOneRecord(conn, new_host, row):
   'Updates the LOCATION field of one record'
   if not(isPxfTable(row[0])):
	   return
	   
   new_location = makeNewLocation(new_host, row[0])
   dbconn.execSQL(conn, "UPDATE pg_exttable SET location = '" + new_location + "' WHERE reloid = "
                        +  str(row[1]))
						
   print "Updated LOCATION for table ", row[2], "oid: ", row[1], \
		 "\n Old LOCATION: ", row[0], "\n New LOCATION: ", new_location
   
def updateNnHost(conn, new_host):
   'update the LOCATION field for each record in pg_exttable'
   dbconn.execSQL(conn, "set allow_system_table_mods = 'DML'")
   dbconn.execSQL(conn, "START TRANSACTION")
   cursor = dbconn.execSQL(conn, "SELECT location, reloid, relname FROM pg_exttable, pg_class WHERE reloid = relfilenode")
   for row in cursor:
      updateOneRecord(conn, new_host, row)
   conn.commit()
	  
def main():
   'The driver function of this module'
   new_host = getNewHost()
   database = getDatabase()
   hawq_master_host = getMasterHost()
   hawq_master_port = getMasterPort()
   promptUser(new_host, database, hawq_master_host, hawq_master_port)

   conn = connectToDb(hawq_master_host, hawq_master_port, database)
   updateNnHost(conn, new_host)

   conn.close()

if __name__ == "__main__":
   main()   	  
