import json
import sys
import getopt
import os
import copy

VLANDEFINITIONS = "vLanDefinitions"
ENVIRONMENT = "environment"
SERVERS = "servers"
CLUSTERGROUP = "clusterGroup"
NAME = "name"
ADDPARTITIONS = "addPartitions"
DEPLOYED = "deployed"
VLANS = "vLans"
VLANACCESS = "vLanAccess"
NASMOUNTS = "nasMounts"
NASREQUESTS = "nasRequests"

configFile = ""
config_data = {}
configName = ""

def main(args):

	def abort(msg):
		""" support for fatal errors"""
		print
		print "FATAL ERROR: " + msg
		print "Please contact Ericsson CMS PDU for assistance."
		print
		sys.exit(1)

	def usage():
		print "Usage :" + sys.argv[0] + " [-d] [-h|--hostAdd] [-v|--vLanAdd] [-p|--partitionAdd]"
		print
		print "Upgrade a config.json file with additional features"
		print
		print " -d enable debug output"
		print " -h Enable hosts add"
		print " -v Enable vLan add"
		print " -p Enable partition add"
		print
		print " Version 1.0 - April 2015 - Chris Bentz"
		return

	def selectFromList(message, options, indexes):
		# present the indicated message followed by the list of options
		# return the option selected.
		# request which stock file to work on. Look for files in ./altjson and ./customjson
		if len(indexes) != len(set(indexes)):
			abort("index options give to selectFromlist method are not unique!")
		s = ""
		while not s:
			print message
			for index, option in zip(indexes, options):
				print " %s) %s" % (index, option)
			print
			s = raw_input("Select: ")
			if s:
				# then we got something
				if s not in indexes:
					print
					print "Select from " + str(indexes)
					print
					s=""
		return s

	def vmCount(data,clusterGroup):
		# how many vm's of this clustergroup are there?
		count = 0
		for server in data[ENVIRONMENT][SERVERS]:
			for vm in server["vms"]:
				if vm[CLUSTERGROUP]==clusterGroup:
					count += 1

		return count

	def printSummary(data,configName):
		# dump what we've got.
		# vLans
		print
		print "Definition Summary: "+configName
		print
		print " Virtual Networks: "
		print "  <Customer/O&M>",
		print " <Private Internal>",
		if 'vLanDefinitions' in data[ENVIRONMENT]:
			for vLan in data[ENVIRONMENT][VLANDEFINITIONS]:
				print " "+vLan[NAME],
		print
		print
		hostIndex=1
		print " Hosts/VMs"
		for server in data[ENVIRONMENT][SERVERS]:
			print "  Host #%d " % hostIndex
			for vm in server["vms"]:
				print "  +- "+vm[NAME],
				if DEPLOYED in vm:
					print " (deployed)"
				else:
					print
				if ADDPARTITIONS in vm:
					for part in vm[ADDPARTITIONS]:
						print "   +- Partition "+part[NAME]
			print
			hostIndex += 1
		print

	def findVm(data,clusterGroup):
		# find me a vm of this group
		for server in data[ENVIRONMENT][SERVERS]:
			for vm in server["vms"]:
				if vm[CLUSTERGROUP]==clusterGroup:
					return vm
		return None

	def selectJsonFileToEdit():
		# request which stock file to work on. Look for files in ./altjson and ./customjson
		files = os.walk(altJsonFolder).next()[2]
		files = files + (os.walk(customJsonFolder).next()[2])
		indexes = list(xrange(1,len(files)+1))
		indexes = [ str(i) for i in indexes]
		f = selectFromList("Select a stock json file to upgrade",files, indexes)
		return files[int(f)-1]

	def loadConfigFile():
		# get and load up a config file
		# manages the following global variables
		# configFile - string
		# config_data = json -> dictionary data
		# configName - configuration name from inside json
		global configFile, config_data, configName
		if preDeploy:
			configFile = selectJsonFileToEdit()
			# load it up!
			try:
				config_data=json.load(open(altJsonFolder + os.path.sep +configFile,'r'))
			except:
				# might be in customjson - let this throw the exception if it does.
				config_data=json.load(open(customJsonFolder + os.path.sep +configFile,'r'))
		else:
			# load existing file - we are on the cs1-installer node
			configFile = installedConfigJsonFile
			config_data = json.load(open(configFile,'r'))

		if debug:
			print "configFile: "+configFile

		# set deployed flags.
		if not preDeploy:
			for server in config_data[ENVIRONMENT][SERVERS]:
				server[DEPLOYED]="Y"
				for vm in server['vms']:
					vm[DEPLOYED]="Y"

		# build main menu options
		#if preDeploy:
		#	config_data["configName"]+=" Customized"
		#else:
		#	config_data["configName"]+=" Upgraded"

		configName=config_data["configName"]


	# parse options

	debug = False
	hostAddSupport = True
	vLanAddSupport = False
	partitionAddSupport = False

	preDeploy = True
	installedConfigJsonFile = "/root/cms-cloud-install/config.json"
	if os.path.isfile(installedConfigJsonFile):
		preDeploy = False

	altJsonFolder='./altjson'
	customJsonFolder='./customjson'
	
	if "cms-kvm-installer" in os.path.abspath(__file__):
		altJsonFolder='./G2/altjson'

	if "win" in sys.platform:
		altJsonFolder = 'C:\\git\\cms-kvm-base\\installer\\scripts\\altjson'
		customJsonFolder = 'C:\\git\\cms-kvm-base\\installer\\scripts\\customjson'

	try:
		opts, args = getopt.getopt(args, "?dhvp", ["debug", "hostAdd","vLanAdd","partitionAdd"])
	except getopt.GetoptError:
		usage()
		sys.exit(2)
	for opt, arg in opts:
		if opt == '-?':
			usage()
			sys.exit(1)
		elif opt in ("-d", "--debug"):
			debug = True
		elif opt in ( '-v', '--vLanAdd' ):
			vLanAddSupport = True
		elif opt in ( '-p', '--partitionAdd' ):
			partitionAddSupport = True

	# environment variables override the command line parameters
	if "UPGRADE_ADDPART" in os.environ:
		# if its there - it takes precedent as Y or N
		if debug:
			print " Environment for UPGRADE_ADDPART active. %s" % os.environ["UPGRADE_ADDPART"]
		if os.environ["UPGRADE_ADDPART"][0].lower()=='y':
			partitionAddSupport = True
		else:
			partitionAddSupport = False

	if not preDeploy:
		if vLanAddSupport:
			print "Warning: Add vLan Support is not possible on post deployed system. Disabling option to add vLans"
			vLanAddSupport = False

	if debug and not preDeploy:
		print "vLanAddSupport: " + str(vLanAddSupport)
		print "partitionAddSupport: " + str(partitionAddSupport)
		print "preDeploy:" + str(preDeploy)
		print "altJsonFolder: "+ str(altJsonFolder)
		print "customJsonFolder: " + str(customJsonFolder)
		print "cwd: " + str(os.getcwd())
		print

	if not preDeploy:
		print "Welcome to the CMS Upgrade Tool"
		print

	# load initial file
	loadConfigFile()
	hostAddSupport=len(config_data["environment"]["servers"]) > 1

	# display main menu
	mm_options = []
	mm_indexes = []

	# print summary
	mm_options.append("Print Summary")
	mm_indexes.append("p")

	if debug:
		mm_options.append("Debug Summary")
		mm_indexes.append('d')

	# include option to reset back to a stock.
	if preDeploy:
		mm_options.append("Reload a Stock Config (discard changes)")
	else:
		mm_options.append("Reset (discard changes)")
	mm_indexes.append('r')

	# Add host
	if hostAddSupport:
		mm_options.append("Add a Host")
		mm_indexes.append("h")
	# Add vm
	mm_options.append("Add a Virtual Machine")
	mm_indexes.append("m")
	# add vLand
	if vLanAddSupport:
		mm_options.append("Add a Virtual Network")
		mm_indexes.append("n")
	#partitions
	if partitionAddSupport:
		mm_options.append("Add a VM Disk Partition")
		mm_indexes.append("t")
	# nas mounts
	mm_options.append("Add a VM NAS Mount")
	mm_indexes.append("a")
	# cancel
	mm_options.append("Quit & Cancel Changes")
	mm_indexes.append("q")
	# save/exit
	mm_options.append("Save Changes & Exit")
	mm_indexes.append("s")

	done = False
	retval = 0
	while not done:
		f = selectFromList("Main: Select an Option",mm_options, mm_indexes)

		if f == 'p':
			# print summary
			printSummary(config_data,configName)
		if f == 'd':
			print json.dumps(config_data, indent=4, sort_keys=True)
		elif f == 'h':
			# add another host entry.  grab entries from server[0]
			print "Adding new host..."
			config_data[ENVIRONMENT][SERVERS].append(copy.deepcopy(config_data[ENVIRONMENT][SERVERS][0]))
			newHost=config_data[ENVIRONMENT][SERVERS][-1]
			# clear out some values
			newHost["accessIP"]="X.X.X.X"
			newHost["accessToken1"]=""
			newHost["accessToken2"]=""
			del newHost["vms"]
			newHost["vms"]=[]
			newHost["maxRAM"]="0"
			newHost["maxCPU"]="0"
			if DEPLOYED in newHost:
				del newHost[DEPLOYED]

			if NASREQUESTS in newHost:
				del newHost[NASREQUESTS]
			newHost[NASREQUESTS]=[]
				
			# re-add nasRequests?
			nases={}
			for index,server in enumerate(config_data[ENVIRONMENT][SERVERS]):
				if NASREQUESTS in server:
					for nas in server[NASREQUESTS]:
						nasSize=nas["size"]
						nasName=nas["name"]
						if int(nasSize) > 0:
							# ok - is this one already in there?
							if not nasName in nases:
								# add it.
								nases[nasName]={}
								nases[nasName]["size"]=nasSize
								nases[nasName]["index"]=index
							else:
								# already there - then this is the second reference, and we dont want a third. remove it
								del nases[nasName]

			# now determine lowest nas index and add the nases from THAT index.
			idx=100
			for nas in nases:
				if int(nases[nas]["index"]) <= idx:
					idx = int(nases[nas]["index"])
					
			if idx < 100:
				# then there is something to add.
				for nas in nases:
					if int(nases[nas]["index"])==idx:
						nasRequest={}
						nasRequest["name"]=nas
						nasRequest["size"]=nases[nas]["size"]

						newHost[NASREQUESTS].append(nasRequest)
					
			print "Completed Successfully"
			print

		elif f == 'r':
			# reload.
			loadConfigFile()

			print "Dropped changes and reloaded."
			print

		elif f == 'm':
			# add vm
			print "Adding new VM..."
			hostlabel=[]
			hostindex=[]
			i=1
			for _ in config_data[ENVIRONMENT][SERVERS]:
				hostlabel.append("Host #"+str(i))
				hostindex.append(str(i))
				i += 1
			hostlabel.append("Cancel")
			hostindex.append("c")

			hostNum = selectFromList("Select Host to add the new VM to.",hostlabel,hostindex)
			if hostNum != 'c':

				server=config_data[ENVIRONMENT][SERVERS][int(hostNum)-1]

				vmlabel=[]
				vmindex=[]

				# cs
				if vmCount(config_data,'cs') < 2:
					vmlabel.append('Cluster Server')
					vmindex.append('c')
				# edb
				if vmCount(config_data,'edb') < 2:
					vmlabel.append('Enterprise DB')
					vmindex.append('d')
				# es
				vmlabel.append('Elastic Search')
				vmindex.append('e')

				# add app
				vmlabel.append('Application')
				vmindex.append('a')

				# portal
				vmlabel.append('Portal')
				vmindex.append('p')

				# cancel
				vmlabel.append('Quit/Cancel add')
				vmindex.append('q')

				vmType = selectFromList("Select VM type to add to this host.",vmlabel,vmindex)
				if vmType != 'n':
					cg='cs'
					if vmType=='d':
						cg='edb'
					elif vmType=='e':
						cg='es'
					elif vmType=='a':
						cg='app'
					elif vmType=='p':
						cg='pt'

					newvm = dict(findVm(config_data,cg))
					newNum = vmCount(config_data,cg)+1
					if debug:
						print 'new VM number is '+str(newNum)
						print

					# update values of this vm.
					newvm["cname"]=cg+str(newNum)
					newvm["hostname"]="cms-"+cg+str(newNum).zfill(2)
					newvm[NAME]=newvm["cname"]
					newvm["customerIP"]=""
					if cg == 'cs' or cg == 'app' or cg == 'pt':
						newvm["customerIP"]="X.X.X.X"
					newvm["privateIP"]="dhcp"
					if cg == 'cs':
						newvm["privateIP"]="X.X.X.30"
					if DEPLOYED in newvm:
						del newvm[DEPLOYED]

					server["vms"].append(newvm)
					# now extend the server's maxRAM and maxCPU by what this VM uses...
					curRAM = int(server["maxRAM"])* 1024
					curRAM += int(newvm["ram"])
					server["maxRAM"] = str(curRAM / 1024)
					curCPU = int(server["maxCPU"])
					curCPU += int(newvm["cpu"])
					server["maxCPU"] = str(curCPU)

					print "Completed Successfully"
				else:
					print "Cancelled"

			print

		elif f == 'n':
			# add vLan
			print "Add a new Virtual network..."
			print
			vLanName=raw_input("Enter vLan Name: ")

			# check if name exists.
			exists=False
			if VLANDEFINITIONS in config_data[ENVIRONMENT]:
				for vLanDef in config_data[ENVIRONMENT][VLANDEFINITIONS]:
					if vLanName == vLanDef[NAME]:
						# no can do.
						print "Error: vLan %s already exists, specify a unique name." % vLanName
						exists=True
			if vLanName == "customer":
				print "Error: customer is a reserved vLan name."
				exists=True
			elif vLanName == "private":
				print "Error: private is a reserved vLan name."
				exists=True
			if not exists:
				# ok - create it.

				# definition
				vLanDef= {NAME: vLanName, "databaseVIP": "", "netmask": "255.255.255.0", "network": "X.X.X.X"}

				if VLANDEFINITIONS not in config_data[ENVIRONMENT]:
					config_data[ENVIRONMENT][VLANDEFINITIONS]=[]

				config_data[ENVIRONMENT][VLANDEFINITIONS].append(vLanDef)

				# host access
				vLanAccess= {NAME: vLanName, "netNic": "ethX", "netNicIP": "X.X.X.X"}

				# vm access
				vLan = {NAME: vLanName, "IP": ""}

				for server in config_data[ENVIRONMENT][SERVERS]:
					if VLANACCESS not in server:
						server[VLANACCESS]=[]
					server[VLANACCESS].append(vLanAccess)
					for vm in server["vms"]:
						if VLANS not in vm:
							vm[VLANS]=[]
						vm[VLANS].append(vLan)

				print "Completed Successfully."

			print

		elif f == 't':
			# add partition
			print "Add a partition..."
			hostlabel=[]
			hostindex=[]
			i=1
			for _ in config_data[ENVIRONMENT][SERVERS]:
				hostlabel.append("Host #"+str(i))
				hostindex.append(str(i))
				i += 1
			hostlabel.append("Cancel")
			hostindex.append("c")

			hostNum = selectFromList("Select Host to select a VM ",hostlabel,hostindex)
			if hostNum != 'c':

				vmlabel=[]
				vmindex=[]
				i=1
				for vm in config_data[ENVIRONMENT][SERVERS][int(hostNum)-1]["vms"]:
					# no adding partitions to deployed nodes or the installer.
					if DEPLOYED not in vm and vm["name"] != "cs1":
						vmlabel.append(vm[NAME])
						vmindex.append(str(i))
						i += 1
				vmlabel.append("Cancel")
				vmindex.append('c')

				if len(vmindex) == 1:
					print "No un-deployed vms on this host."
				else:

					vmNum = selectFromList("Select VM to add a partition to", vmlabel, vmindex)
					if vmNum != 'c':
						# ok - add away!
						partname=raw_input("Enter Partition Name: ")
						# find the vm.
						for v in config_data[ENVIRONMENT][SERVERS][int(hostNum)-1]["vms"]:
							if v[NAME]==vmlabel[int(vmNum)-1]:
								vm=v
						exists=False
						if ADDPARTITIONS not in vm:
							vm[ADDPARTITIONS]=[]
						else:
							# make sure its unique
							for ap in vm[ADDPARTITIONS]:
								if ap[NAME]==partname:
									# no go.
									print "Error: additional partition %s is already on this vm. Select a different name" % partname
									exists=True
						if not exists:
							addPart= {NAME: partname, "mount": "", "format": "", "size": ""}
							vm[ADDPARTITIONS].append(addPart)

							print "Completed Successfully"

					else:
						print "Cancelled"

			else:
				print "Cancelled"

			print
		elif f == 'a':
			# add a nasMount
			hostlabel=[]
			hostindex=[]
			i=1
			for _ in config_data[ENVIRONMENT][SERVERS]:
				hostlabel.append("Host #"+str(i))
				hostindex.append(str(i))
				i += 1
			hostlabel.append("Cancel")
			hostindex.append("c")

			hostNum = selectFromList("Select Host to select a VM ",hostlabel,hostindex)
			if hostNum != 'c':

				vmlabel=[]
				vmindex=[]
				i=1
				for vm in config_data[ENVIRONMENT][SERVERS][int(hostNum)-1]["vms"]:
					# no adding mounts to deployed nodes
					if DEPLOYED not in vm :
						vmlabel.append(vm[NAME])
						vmindex.append(str(i))
						i += 1
				vmlabel.append("Cancel")
				vmindex.append('c')

				if len(vmindex) == 1:
					print "No un-deployed vms on this host."
				else:

					vmNum = selectFromList("Select VM to add a nas mount to", vmlabel, vmindex)
					if vmNum != 'c':
						# ok - add away!
						nas={}
						nas["mount"]=""
						nas["name"]=""
						nas["options"]=""
						nas["server"]=""
						nas["share"]=""
						nas["type"]=""
						# find the vm.
						for v in config_data[ENVIRONMENT][SERVERS][int(hostNum)-1]["vms"]:
							if v[NAME]==vmlabel[int(vmNum)-1]:
								vm=v
						if NASMOUNTS not in vm:
							vm[NASMOUNTS]=[]
						vm[NASMOUNTS].append(nas)
						print "Completed Successfully"

					else:
						print "Cancelled"

			else:
				print "Cancelled"

			print

		elif f == 'q':
			# cancel.
			print "Not saving file - exiting."
			done = True
			retval = 2
		elif f == 's':
			# save.
			if preDeploy:
				customFile=os.path.join(customJsonFolder,os.path.splitext(os.path.basename(configFile))[0]+"_customized.json")
			else:
				customFile=os.path.join('configsToList',os.path.splitext(os.path.basename(configFile))[0]+"_upgraded.json")
			print "Saving to %s" % customFile
			json.dump(config_data,open(customFile,'w'))
			done = True

	return retval

if __name__ == "__main__":
	exit( main(sys.argv[1:]))
