#!/usr/bin/python -W ignore::DeprecationWarning
# -*- coding: utf-8 -*-
# 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 os
import sys
import glob
from random import choice
import string
from optparse import OptionParser
import MySQLdb
import paramiko
from threading import Thread

# ---- This snippet of code adds the sources path and the waf configured PYTHONDIR to the Python path ----
# ---- We do this so cloud_utils can be looked up in the following order:
# ---- 1) Sources directory
# ---- 2) waf configured PYTHONDIR
# ---- 3) System Python path
for pythonpath in (
		"/usr/lib/python2.6/site-packages/",
		os.path.join(os.path.dirname(__file__),os.path.pardir,os.path.pardir,"python","lib"),
	):
		if os.path.isdir(pythonpath): sys.path.insert(0,pythonpath)
# ---- End snippet of code ----
from cloud_utils import check_call, CalledProcessError, read_properties

cfg = "/etc/cloudstack/management/db.properties"

#---------------------- option parsing and command line checks ------------------------


usage = """%prog <license file> <-a | host names / IP addresses...> 

This command deploys the license file specified in the command line into a specific XenServer host or all XenServer hosts known to the management server."""

parser = OptionParser(usage=usage)
parser.add_option("-a", "--all", action="store_true", dest="all", default=False,
                  help="deploy to all known hosts rather that a single host")

#------------------ functions --------------------

def e(msg): parser.error(msg)

def getknownhosts(host,username,password):
	conn = MySQLdb.connect(host=host,user=username,passwd=password)
	cur = conn.cursor()
	cur.execute("SELECT h.private_ip_address,d.value FROM cloud.host h inner join cloud.host_details d on (h.id = d.host_id) where d.name = 'username' and setup = 1")
	usernames = dict(cur.fetchall())
	cur.execute("SELECT h.private_ip_address,d.value FROM cloud.host h inner join cloud.host_details d on (h.id = d.host_id) where d.name = 'password' and setup = 1")
	passwords = dict(cur.fetchall())
	creds = dict( [ [x,(usernames[x],passwords[x])] for x in usernames.keys() ] )
	cur.close()
	conn.close()
	return creds

def splitlast(string,splitter):
	splitted = string.split(splitter)
	first,last = splitter.join(splitted[:-1]),splitted[-1]
	return first,last

def parseuserpwfromhosts(hosts):
	creds = {}
	for host in hosts:
		user = "root"
		password = ""
		if "@" in host:
			user,host = splitlast(host,"@")
			if ":" in user:
				user,password = splitlast(user,":")
		creds[host] = (user,password)
	return creds

class XenServerConfigurator(Thread):
	
	def __init__(self,host,user,password,keyfiledata):
		Thread.__init__(self)
		self.host = host
		self.user = user
		self.password = password
		self.keyfiledata = keyfiledata
		self.retval = None # means all's good
		self.stdout = ""
		self.stderr = ""
		self.state = 'initialized'
		
	def run(self):
		try:
			self.state = 'running'
			c = paramiko.SSHClient()
			c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
			c.connect(self.host,username=self.user,password=self.password)
			sftp = c.open_sftp()
			sftp.chdir("/tmp")
			f = sftp.open("xen-license","w")
			f.write(self.keyfiledata)
			f.close()
			sftp.close()
			stdin,stdout,stderr = c.exec_command("xe host-license-add license-file=/tmp/xen-license")
			c.exec_command("false")
			self.stdout = stdout.read(-1)
			self.stderr = stderr.read(-1)
			self.retval = stdin.channel.recv_exit_status()
			c.close()
			if self.retval != 0: self.state = 'failed'
			else: self.state = 'finished'
			
		except Exception,e:
			self.state = 'failed'
			self.retval = e
			#raise
	
	def __str__(self):
		if self.state == 'failed':
			return "<%s XenServerConfigurator on %s@%s: %s>"%(self.state,self.user,self.host,str(self.retval))
		else:
			return "<%s XenServerConfigurator on %s@%s>"%(self.state,self.user,self.host)
		
#------------- actual code --------------------

(options, args) = parser.parse_args()

try:
	licensefile,args = args[0],args[1:]
except IndexError: e("The first argument must be the license file to use")

if options.all:
	if len(args) != 0: e("IP addresses cannot be specified if -a is specified")
	config = read_properties(cfg)
	creds = getknownhosts(config["db.cloud.host"],config["db.cloud.username"],config["db.cloud.password"])
	hosts = creds.keys()
else:
	if not args: e("You must specify at least one IP address, or -a")
	hosts = args
	creds = parseuserpwfromhosts(hosts)

try:
	keyfiledata = file(licensefile).read(-1)
except OSError,e:
	sys.stderr.write("The file %s cannot be opened"%licensefile)
	sys.exit(1)

configurators = []
for host,(user,password) in creds.items():
	configurators.append ( XenServerConfigurator(host,user,password,keyfiledata ) )


for c in configurators: c.start()
	
for c in configurators:
	print c.host + "...",
	c.join()
	if c.state == 'failed':
		if c.retval:
			msg = "failed with return code %s: %s%s"%(c.retval,c.stdout,c.stderr)
			msg = msg.strip()
			print msg
		else: print "failed: %s"%c.retval
	else:
		print "done"

successes = len( [ a for a in configurators if not a.state == 'failed' ] )
failures = len( [ a for a in configurators if a.state == 'failed' ] )

print "%3s successes"%successes
print "%3s failures"%failures
