#!/usr/bin/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.

import xml.etree.ElementTree as ET
import os,errno,sys
from os import listdir
from os.path import isfile, join, dirname
from urllib.parse import urlparse

debugLevel = 1

SUPPORTED_COMPONENTS = [ "hdfs", "hive", "hbase", "knox", "storm" ]

#
# xmlTemplateDirectory: directory where all of the xml templates are kept here
#

xmlTemplateDirectory = './install/conf.templates/enable'

def showUsage():
	print("This script must be run with a <componentName> as parameter")
	print("USAGE: upgrade-plugin.py <componentName>")
	print(" <componentName> could be any one of the following: %s" % (SUPPORTED_COMPONENTS))

if (len(sys.argv) == 1):
	showUsage()
	sys.exit(1)

componentName = sys.argv[1]

if (componentName not in SUPPORTED_COMPONENTS):
	print("Invalid componentName passed as parameter: %s" % (componentName))
	showUsage()
	sys.exit(1)

#
# For hdfs, the componentName is hadoop (for path calculation)
#

if (componentName == 'hdfs'):
	configPath = 'hadoop'
else:
	configPath = componentName

#
# configDirectory: where OLD (champlain) configuration exists and NEW (dal) configuration is written to
#
configDirectory = '/etc/' + configPath + '/conf' 



def getXMLConfigKeys(xmlFileName):
	ret = []
	tree = ET.parse(xmlFileName)
	root = tree.getroot()
	for config in root.iter('property'):
		name = config.find('name').text
		ret.append(name)
	return ret

def getXMLConfigMap(xmlFileName):
	ret = {}
	tree = ET.parse(xmlFileName)
	root = tree.getroot()
	for config in root.iter('property'):
		name = config.find('name').text
		val = config.find('value').text 
		ret[name] = val
	return ret

def writeXMLUsingProperties(xmlTemplateFileName,prop,xmlOutputFileName):
	tree = ET.parse(xmlTemplateFileName)
	root = tree.getroot()
	for config in root.iter('property'):
		name = config.find('name').text
		if (name in prop):
			config.find('value').text = prop[name]
	tree.write(xmlOutputFileName)

def rewriteConfig(props,newProps):
	if (debugLevel > 0):
		for k,v in props.items():
			print("old config[%s] = [%s]" % (k,v))
	#
	# Derived fields
	#
	pmUrl = props['xasecure.' + componentName + '.policymgr.url']
	url = urlparse(pmUrl)
	restUrl = url[0] + "://" + url[1]
	serviceName = url[2].split("/")[-1]
	props['ranger.plugin.' + componentName + '.policy.rest.url'] = restUrl
	props['ranger.plugin.' + componentName + '.service.name'] = serviceName
	props['ranger.plugin.' + componentName + '.policy.pollIntervalMs'] = props['xasecure.' + componentName + '.policymgr.url.reloadIntervalInMillis']
	#props['ranger.plugin.' + componentName + '.policy.rest.ssl.config.file'] = props['y']
	fileLoc = props['xasecure.' + componentName + '.policymgr.url.laststoredfile']
	props['ranger.plugin.' + componentName + '.policy.cache.dir'] = dirname(fileLoc)
	if ( 'xasecure.policymgr.sslconfig.filename' in props ):
		props['ranger.plugin.' + componentName + '.policy.rest.ssl.config.file'] = props['xasecure.policymgr.sslconfig.filename']
	else:
		sslConfigFileName = join(configDirectory,'ranger-policymgr-ssl.xml') 
		props['ranger.plugin.' + componentName + '.policy.rest.ssl.config.file'] = sslConfigFileName
	#
	# Fix for KNOX ssl (missing) configuration
	#
	if ('xasecure.policymgr.clientssl.keystore.credential.file' not in props):
		props['xasecure.policymgr.clientssl.keystore.credential.file'] = 'jceks://file/tmp/keystore-' + serviceName + '-ssl.jceks'
	if ( 'xasecure.policymgr.clientssl.truststore.credential.file' not in props):
		props['xasecure.policymgr.clientssl.truststore.credential.file'] = 'jceks://file/tmp/keystore-' + serviceName + '-ssl.jceks'

	for fn in listdir(xmlTemplateDirectory):
		file = join(xmlTemplateDirectory,fn)
		if isfile(file) and fn.startswith("ranger-") and fn.endswith(".xml") :
			newConfigFile = join(configDirectory, fn)
			writeXMLUsingProperties(file, props, newConfigFile)

def main():
	props = {}
	newProps = {}
	foundFiles = []
	for fn in listdir(configDirectory):
		file = join(configDirectory,fn)
		if isfile(file) and fn.startswith("xasecure-") and fn.endswith(".xml") :
			foundFiles.append(file)
			r = getXMLConfigMap(file)
			props.update(r)
	if (len(foundFiles) == 0):
		print("INFO: Previous version of ranger is not enabled/configured for component [%s]" % (componentName))
		sys.exit(0)
	if (len(foundFiles) != 3):
		print("ERROR: Expected to find three files matching xasecure-*.xml files under the folder (%s) - found %s" % (configDirectory,foundFiles))
		sys.exit(1)
	for fn in listdir(xmlTemplateDirectory):
		file = join(xmlTemplateDirectory,fn)
		if isfile(file) and fn.startswith("ranger-") and fn.endswith(".xml") :
			r = getXMLConfigMap(file)
			newProps.update(r)
			newConfigFile = join(configDirectory,fn)
			if isfile(newConfigFile):
				print("ERROR: new config file [%s] already exists. Upgrade script can not overwrite an existing config file." % (newConfigFile))
				sys.exit(1)
	rewriteConfig(props,newProps)

main()
