#!/usr/bin/env python
import sys
import os
import SOAPpy
import commands
#import sqlite
from StringIO import StringIO
from SOAPpy import *

# debug
SOAPpy.Config.debug=1

from M2Crypto import SSL

import crypt
sys.path.append( '/usr/share/dtc-xen' )
from Properties import *

# import xm stuff
sys.path.append( '/usr/lib/python' ) #Required to import from /usr/lib/python for FC4
import xen.xm.main as xenxm

# Checking for Xen version
print "Checking for Xen version"
xen_version = 2
try:
	func = getattr(xenxm.server, "xend_domain")
	if func:
		xen_version = 2
except:
	xen_version = 3

print "Detected Xen vesrion %s" % xen_version

# read config file
p=Properties()
p.load(open('/etc/dtc-xen/soap.conf'))
server_host=p.getProperty("soap_server_host");
server_port=int(p.getProperty("soap_server_port"));
cert_passphrase=p.getProperty("soap_server_pass_phrase");
dtcxen_user=p.getProperty("soap_server_dtcxen_user");

# server_host = "mirror.tusker.net"
# server_host = "dtc.xen650202.gplhost.com"
# server_port = 8089

def testVPSServer():
	  return "OK"

def startVPS(vpsname):
	username = getUser()
	if username == dtcxen_user or username == vpsname:
		# Lookup the database to see if we are running a process (mkfs/fsck/etc...) on the VM instance...
		xmargs=['foo', 'create', vpsname]
		print "Starting %s..." % vpsname
		localsysout = StringIO()
		localsyserr = StringIO()
		sys.stdout = localsysout
		sys.stderr = localsyserr
		try:
			xenxm.main(xmargs)
			return "OK","Started %s" % vpsname
		except:
			sys.stdout = sys.__stdout__
			sys.stderr = sys.__stderr__
			returnString =  "NOTOK - %s %s" % (localsyserr.getvalue(), localsysout.getvalue())
			localsyserr.close()
			localsysout.close()
			return returnString
	else:
		return "NOTOK"

def destroyVPS(vpsname):
	username = getUser()
	if username == dtcxen_user or username == vpsname:
		xmargs=['foo','destroy',vpsname]
		print "Destroying %s..." % vpsname
		localsysout = StringIO()
                localsyserr = StringIO()
                sys.stdout = localsysout
                sys.stderr = localsyserr
		try:
			xenxm.main(xmargs)
			return "OK","Destroyed %s" % vpsname
		except:
			sys.stdout = sys.__stdout__
                        sys.stderr = sys.__stderr__
			returnString =  "NOTOK - %s %s" % (localsyserr.getvalue(), localsysout.getvalue())
			localsyserr.close()
			localsysout.close()
			return returnString
	else:
		return "NOTOK"

def shutdownVPS(vpsname):
	username = getUser()
	if username == dtcxen_user or username == vpsname:
		xmargs=['foo','shutdown',vpsname]
		print "Shutting down %s..." % vpsname
		localsysout = StringIO()
                localsyserr = StringIO()
                sys.stdout = localsysout
                sys.stderr = localsyserr
		try:
			xenxm.main(xmargs)
			return "OK","Shutdown %s" % vpsname
		except:
                        sys.stdout = sys.__stdout__
                        sys.stderr = sys.__stderr__
                        returnString =  "NOTOK - %s %s" % (localsyserr.getvalue(), localsysout.getvalue())
                        localsyserr.close()
                        localsysout.close()
                        return returnString
	else:
		return "NOTOK"

def infoVPS(vpsname):
	username = getUser()
        if username == dtcxen_user or username == vpsname:
		infos=['vpsname']
		return "OK",infos
	else:
		return "NOTOK"

def listStartedVPS():
	username = getUser()
	if username == dtcxen_user:
		# first check to see if we have a xend_domain method (for 2.x)
		try:
			func = getattr(xenxm.server, "xend_domains")
			if func:
				doms = xenxm.server.xend_domains()
			else:
				print "Couldn't find xend_domains method"
		except:
			doms = xenxm.server.xend.domains(1)
		doms.sort()
		return doms
	else:
		try:
			func = getattr(xenxm.server, "xend_domain")
			if func:
				dom = xenxm.server.xend_domain(username)
			else:
				print "Couldn't find xend_domain method"
		except:
			dom = xenxm.server.xend.domain(username)
		return dom

def changeVPSxmPassword(vpsname,password):
	username = getUser()
	if username == dtcxen_user or username == vpsname:
		commands.getstatusoutput("(echo %s; sleep 1; echo %s;) | passwd %s" % (password,password,vpsname))
		return "OK"
	else:
		return "NOTOK"

def changeVPSsoapPassword(vpsname,password):
	username = getUser()
	if username == dtcxen_user or username == vpsname:
		commands.getstatusoutput("htpasswd -b /etc/dtc-xen/htpasswd %s %s" % (vpsname,password))
		return "OK"
	else:
		return "NOTOK"

def changeVPSsshKey(vpsname,keystring):
	username = getUser()
	if username == dtcxen_user or username == vpsname:
		try:
			# create the directory if it doesn't exist
			if not os.path.isdir("/var/lib/dtc-xen/ttyssh_home/%s/.ssh/" % vpsname):
				os.makedirs("/var/lib/dtc-xen/ttyssh_home/%s/.ssh/" % vpsname)
			os.chown("/var/lib/dtc-xen/ttyssh_home/%s/.ssh/" % vpsname, getuserid(vpsname), getusergroup(vpsname))
			# open file stream
			filename = "/var/lib/dtc-xen/ttyssh_home/%s/.ssh/authorized_keys" % vpsname
			file = open(filename, "w")
			file.write(keystring)
			file.close()
			os.chown(filename, getuserid(vpsname), getusergroup(vpsname))
		except IOError:
			return "NOTOK - There was an error writing to", filename
		return "OK"
	else:
		return "NOTOK"

def fsckVPSpartition(vpsname):
	username = getUser()
	if username == dtcxen_user or username == vpsname:
		filename = "/var/lib/dtc-xen/states/%s" % vpsname
		try:
			fd = open(filename, 'r')
			return "NOTOK"
		except:
			# Write the semaphore file before proceeding
			fd2 = open(filename, 'w')
			fd2.write("fsck\n")
			fd2.close()
			# Fork the daemon
			pid = os.fork()
			if pid > 0:
				return "Ok, started fsck."
			else:
				# Do the fsck
				print "Starting file system check for %s" % vpsname
				commands.getstatusoutput("/sbin/fsck.ext3 -p /dev/lvm1/%s" %vpsname)
				print "fsck for VPS %s finished" % vpsname
				# Delete the semaphore file
				os.remove(filename)
				os._exit(0)
#				sys.exit(0)
	else:
		return "NOTOK"

def changeBSDkernel(vpsname,ramsize,kerneltype,allipaddrs):
	username = getUser()
	if username == dtcxen_user or username == vpsname:
		print "Changing kernel of a BSD VM: vps: %s ram: %s kernel: %s" % (vpsname,ramsize,kerneltype)
		cmd = "dtc_change_bsd_kernel %s %s %s '%s'" % (vpsname,ramsize,kerneltype,allipaddrs)
		print cmd
		commands.getstatusoutput(cmd)
		return "OK"

# Take care! This time, the vpsname has to be only the number (eg XX and not xenXX)
def reinstallVPSos(vpsname,ostype,hddsize,ramsize,ipaddr,imagetype='lvm'):
	username = getUser()
	if username == dtcxen_user or username == vpsname:
		filename = "/var/lib/dtc-xen/states/xen%s" % vpsname
		print "Checking %s for mkos" % vpsname
		try:
			print "Semaphore file existed: abording"
			fd = open(filename, 'r')
			return "NOTOK"
		except:
			fd2 = open(filename, 'w')
			fd2.write("mkos\n")
			pid = os.fork()
			if pid > 0:
				return "Ok, started mkos."
			else:
				print "Starting reinstallation of operating system for xen%s" % vpsname
				cmd = "/usr/sbin/dtc_reinstall_os %s %s %s '%s' %s %s" % (vpsname,hddsize,ramsize,ipaddr,ostype,imagetype)
				print cmd
				commands.getstatusoutput(cmd)
				print "mkos for VPS %s finished: removing file" % vpsname
				os.remove(filename)
				os._exit(0)
	else:
		return "NOTOK"

def setupLVMDisks(vpsname,hddsize,swapsize,imagetype='lvm'):
	username = getUser()
	if username == dtcxen_user or username == vpsname:
		print "Starting disk setup for xen%s: %s HHD, %s SWAP, %s imagetype" % (vpsname,hddsize,swapsize,imagetype)
		cmd = "/usr/sbin/dtc_setup_vps_disk %s %s %s %s" % (vpsname,hddsize,swapsize,imagetype)
		commands.getstatusoutput(cmd)
		print "Commande: %s" % cmd
		return "OK"
	else:
		return "NOTOK"

def getFreeSpace():
	free_space_disk = commands.getstatusoutput('/usr/sbin/vgdisplay_free_size')
	free_space_mem = commands.getstatusoutput('/usr/sbin/xm_info_free_memory')
	return (free_space_disk,free_space_mem)

def getuserid(user):
	import pwd
	if isinstance(user, int):
		return user
	entry = pwd.getpwnam(user)
	return entry[2]

def getusergroup(user):
	import pwd
	import grp
	return grp.getgrgid(pwd.getpwnam(user)[3])[2]

def getgroupid(group):
	import grp
	if isinstance(group, int):
		return group
	entry = grp.getgrnam(group)
	return entry[2]

def getNetworkUsage(vpsname):
	username = getUser()
	if username == dtcxen_user or username == vpsname:
		info = getVPSState(vpsname)
		id = info[1][1]
		if vpsname=="Domain-0":
			devinfo=os.popen2("cat /proc/net/dev | grep ' eth0'")[1].read()
		else:
			devinfo=os.popen2("cat /proc/net/dev | grep 'vif"+id+"\.'")[1].read()
		rows=re.split("\n", devinfo)
		incount = 0
		outcount = 0
		for row in rows:
			columns=re.split("[\t ]+",row)
			if (len(columns)==16):
				one,a,a,a,a,a,a,a,eight,a,a,a,a,a,a,a=columns
			elif (len(columns)==17):
				a,one,a,a,a,a,a,a,a,eight,a,a,a,a,a,a,a=columns
			elif (len(columns)==1):
				# this is an empty row, continue
				continue
			else:
				# this is an unknown row, continue
				print str(columns) + str(len(columns))
				continue
			# parse away any remaining : in one variable
			if re.search(":",one):
				one=re.split(":",one)[1]
			# print vpsname +' '+ id +' '+ one +' '+ eight
			incount = incount + int(one)
			outcount = outcount + int(eight)
		return "%d,%d" % (incount,outcount)
        else:
                return "NOTOK"

def getVPSState(vpsname):
	username = getUser()
        if username == dtcxen_user or username == vpsname:
        	filename = "/var/lib/dtc-xen/states/%s" % vpsname
        	print "Checking %s for getVPSState" % filename
        	try:
			print "Opening %s" % filename
	        	fd = open(filename, 'r')
	        	for line in fd:
				print "Checking fsck"
	        		if string.find(line,"fsck") != -1:
	        			print "Founded running fsck!"
					fd.close()
	        			return "fsck"
				else:
					print "Checking mkos"
					if string.find(line,"mkos") != -1:
						print "Founded running mkos!"
						fd.close()
						return "mkos"
			fd.close()
			print "State file exists, but couldn't find content!"
			return "Error in state file!"
		except:
			print "No semaphore (fsck/mkos): continuing"
		try:
			if xen_version == 3:
				info = xenxm.server.xend.domain(vpsname)
				return info
			else:
				print "Calling xenxm.server xend_domain"
				func = getattr(xenxm.server, "xend_domain")
				print "After xenxm.server"
				if func:
					print "Calling xenxm.server.xend.domain(%s)" % vpsname
					info = xenxm.server.xend_domain(vpsname)
					return info
				else:
					print "Couldn't find xend_domain method"
		except:
			return "Not running"
	else:
		return "NOTOK"
#	d = {}
#	d['dom'] = int(xenxm.sxp.child_value(info, 'id', '-1'))
#	d['name'] = xenxm.sxp.child_value(info, 'name', '??')
#	d['mem'] = int(xenxm.sxp.child_value(info, 'memory', '0'))
#	d['cpu'] = int(xenxm.sxp.child_value(info, 'cpu', '0'))
#	d['state'] = xenxm.sxp.child_value(info, 'state', '??')
#	d['cpu_time'] = float(sxp.child_value(info, 'cpu_time', '0'))
#	return d

# ask for returned SOAP responses to be converted to basic python types
Config.simplify_objects = 1

# specify name of authorization function
Config.authMethod = "_authorize"

def getUser():
	c = GetSOAPContext()
	# get authorization info from HTTP headers
	ah = c.httpheaders.get("Authorization","")

	if ah:
		# decode and analyze the string for the username and password
		# (we slice the string from 6 onwards to remove the "Basic ")
		username, password = base64.decodestring(ah[6:].strip()).split(":")
		return username
	else:
		return 0

def isUserValid(vpsname):
	username = getUser()    
	if vpsname == username:
		print "Valid user: ", username
		return 1
	else:
		return 0

def _authorize(*args, **kw):
	global Config
	print "_authorize called..."

	c = kw["_SOAPContext"]
	print "**kw =%s" % str(kw)

	# The socket object, useful for
	print "Peer connected: ",  c.connection.getpeername()

	# get authorization info from HTTP headers
	ah = c.httpheaders.get("Authorization","")

	if ah:
		# decode and analyze the string for the username and password
		# (we slice the string from 6 onwards to remove the "Basic ")
		username, password = base64.decodestring(ah[6:].strip()).split(":")
		print "Authorization string: \"%s\"" % (ah,)
		print "Username: \"%s\" Password: \"%s\"" % (username, password)

		print "Loading /etc/dtc-xen/htpasswd..."
		fd = open('/etc/dtc-xen/htpasswd', 'r') 

		for line in fd:
			u, h = line.strip().split(':')
			if u == username:
				print "Found user: ",username
				print "Password from file: ", h
				verify_pass = crypt.crypt(password, h[:2])
				print "Check hash password: ",verify_pass
				if verify_pass == h:
					fd.close()
					print "Password matches the one in the file!"
					return 1
				else:
					fd.close()
					print "Password didn't match the one in .htpasswd"
					return 0
    
		print "Couldn't find user in password file!"
		return 0
    
	else:
		print "NO authorization information in HTTP headers, refusing."
		return 0

def _passphrase(cert):
	print "Pass phrase faked..."
	return cert_passphrase

if not Config.SSLserver:
	raise RuntimeError, "this Python installation doesn't have OpenSSL and M2Crypto"

ssl_context = SSL.Context()
ssl_context.load_cert('/etc/dtc-xen/dtc-xen.cert.cert', '/etc/dtc-xen/privkey.pem', callback=_passphrase)

soapserver = SOAPpy.SOAPServer((server_host, server_port), ssl_context = ssl_context)
# No ssl 
# soapserver = SOAPpy.SOAPServer((server_host, server_port))
soapserver.registerFunction(_authorize)
soapserver.registerFunction(testVPSServer)
soapserver.registerFunction(startVPS)
soapserver.registerFunction(destroyVPS)
soapserver.registerFunction(shutdownVPS)
soapserver.registerFunction(listStartedVPS)
soapserver.registerFunction(getVPSState)
soapserver.registerFunction(changeVPSxmPassword)
soapserver.registerFunction(changeVPSsoapPassword)
soapserver.registerFunction(changeVPSsshKey)
soapserver.registerFunction(reinstallVPSos)
soapserver.registerFunction(fsckVPSpartition)
soapserver.registerFunction(changeBSDkernel)
soapserver.registerFunction(setupLVMDisks)
soapserver.registerFunction(getNetworkUsage)
print "Starting dtc-xen python SOAP server at https://%s:%s/ ..." % (server_host, server_port)
while True:
	try:
		soapserver.serve_forever()
	except KeyboardInterrupt:
		print "Shutting down..."
		sys.exit(0)
	except Exception, e:
		print "Caught exception handling connection: ", e
