#!/usr/bin/env python
import sys, traceback
sys.path.append("/usr/lib/xen/lib/python/")
sys.path.append( '/usr/lib/python' ) #Required to import from /usr/lib/python for FC4
sys.path.append( '/usr/share/dtc-xen' )

import os
import SOAPpy
import commands
from StringIO import StringIO
from SOAPpy import *
import logging
from logging.handlers import SysLogHandler
import threading
import glob
import signal
try: import subprocess # FIXME maybe this wont work on older pythons?
except ImportError: subprocess = False
from M2Crypto import SSL
import crypt
from Properties import *
from threading import Thread,RLock
from subprocess import Popen,PIPE
import re
import time
import pickle


# some global variables
run_as_daemon = None
last_signal = None
SOAPpy.Config.debug=1

server_host = None
server_port = None
cert_passphrase = None
dtcxen_user = None
server_lvmname = None
vpsimage_mount_point = None
perfdata_dir = None
tabsplitter = None
tabcolonsplitter = None
data_collection_lock = None
keepRunning = 1

def sighandler(signum,frame):
	global last_signal
	last_signal = signum
	if last_signal == signal.SIGHUP:
		try:
			logging.info("Logging restarting...")
			logging.shutdown()
			setup_logging()
		except Exception,e:
			logging.exception("Trapped exception in function call.  Thread: %s",threading.currentThread())
			raise
	if last_signal == signal.SIGTERM:
		shutdown()
	if last_signal == signal.SIGINT:
		shutdown()

def shutdown(error=0):
	global run_as_daemon
	global last_signal
	logging.info("Shutting down due to signal %s..."%last_signal)
	if run_as_daemon:
		try: os.unlink("/var/run/dtc-xen.pid")
		except: pass
	for a in [sys.stdout,sys.stderr]:
		if a:
			try: a.flush()
			except: pass
	logging.info("DTC SOAP server shut down")
	logging.shutdown()
	sys.exit(error)

# A generalized decorator for logging exceptions
def log_exceptions(f):
   def func(*args,**kwargs):
      try:
           logging.debug("Calling function %s(%s,%s)",f.func_name,args,kwargs)
           ret = f(*args,**kwargs)
           logging.debug("Function returned %s",ret)
           return ret
      except Exception,e:
           logging.exception("Trapped exception in function call %s.  Thread: %s",f,threading.currentThread())
           raise
   func.func_name = f.func_name
   return func

def firstexisting(list):
	for m in list:
		if os.path.exists(m): return m

def firsttrue(list):
	for m in list:
		if m: return m

def provisioning_volgroup():
	return subprocess.Popen(["dtc-xen-volgroup"], 
	stdin=None,stdout=subprocess.PIPE,
	stderr=None,close_fds=True).communicate()[0].strip()


# ---------------- daemon starts here ----------------------

run_as_daemon = len(sys.argv) > 1 and "-D" in sys.argv[1:]
debug_logging = len(sys.argv) > 1 and "-v" in sys.argv[1:]

def setup_logging():
	global debug_logging
	global run_as_daemon

	if debug_logging: log_level = logging.DEBUG
	else: log_level = logging.INFO

	# nuke the old handlers -- they cause problems if kept registered after daemonization
	del logging.getLogger().handlers[:]

	if run_as_daemon:
		oldumask = os.umask(31)
		try:
			logging.basicConfig(filename="/var/log/dtc-xen.log",level=log_level,format='%(asctime)s %(levelname)s %(message)s')
			h = logging.StreamHandler()
			h.setLevel(logging.WARN)
			logging.getLogger().addHandler(h)

		except IOError:
			# oops, the log file could not be opened
			# just log everything to stderr
			logging.basicConfig(level=log_level)
		os.umask(oldumask)
	else:
		logging.basicConfig(level=log_level) # debug because started foreground

def startup():
	setup_logging()

	logging.info("Starting DTC SOAP server...")

	#if not os.path.exists("/proc/xen/privcmd"):
	#	logging.error("/proc/xen/privcmd is not accessible, cannot start dtc-xen")
	#	shutdown(1)

	# import xm stuff
	#import xen.xm.main

	# read config file
	config_file = firstexisting(['/etc/dtc-xen/dtc-xen.conf','/etc/dtc-xen/soap.conf'])
	p=Properties()
	p.load(open(config_file))
	gp = p.getProperty
	global server_host,server_port,cert_passphrase,dtcxen_user,server_lvmname,vpsimage_mount_point
	server_host= firsttrue([ gp(x) for x in ["soap_server_host","listen_address"] ])
	server_port= int(firsttrue([ gp(x) for x in ["soap_server_port","listen_port"] ]))
	cert_passphrase= firsttrue([ gp(x) for x in ["soap_server_pass_phrase","cert_passphrase"] ])
	dtcxen_user=firsttrue([ gp(x) for x in ["soap_server_dtcxen_user","admin_user"] ])
	server_lvmname=provisioning_volgroup()
	vpsimage_mount_point=firsttrue([ gp(x) for x in [
			"soap_server_mount_point",
			"provisioning_mount_point"] ])
	del gp

	logging.info("Server using this configuration:")
	logging.info("    Configuration file: %s"%config_file)
	logging.info("    Listen address: %s"%server_host)
	logging.info("    Listen port: %s"%server_port)
	if cert_passphrase: logging.info("    Certificate passphrase: present")
	else: logging.info("    Certificate passphrase: not configured")
	logging.info("    Administrator user: %s"%dtcxen_user)
	logging.info("    Provisioning from LVM volume group: %s"%server_lvmname)
	logging.info("    Provisioning mount point: %s"%vpsimage_mount_point)

	# --------------------- now we define the functions the daemon serves ------

	logging.info("Start to define functions...")

def testVPSServer(*varargs):
	  if not varargs: return "OK"
	  else: return varargs

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]
		logging.info("Starting VPS %s",vpsname)
		localsysout = StringIO()
		localsyserr = StringIO()
		try:
			subprocess.Popen(["/usr/sbin/xm","create",vpsname])
			logging.info("VPS %s started",vpsname)
			return "OK","Started %s" % vpsname
		except:
			# this is a HACK.  It should also not capture BaseException.
			returnString =  "NOTOK - %s %s" % (localsyserr.getvalue(), localsysout.getvalue())
			localsyserr.close()
			localsysout.close()
			logging.exception("VPS %s failed to start",vpsname)
			return returnString
	else:
		return "NOTOK"

def destroyVPS(vpsname):
	username = getUser()
	if username == dtcxen_user or username == vpsname:
		xmargs=['foo','destroy',vpsname]
		logging.info("Destroying VPS %s", vpsname)
		localsysout = StringIO()
                localsyserr = StringIO()
		try:
			subprocess.Popen(["/usr/sbin/xm","destroy",vpsname])
			logging.info("VPS %s destroyed",vpsname)
			return "OK","Destroyed %s" % vpsname
		except:
			returnString =  "NOTOK - %s %s" % (localsyserr.getvalue(), localsysout.getvalue())
			localsyserr.close()
			localsysout.close()
			logging.exception("VPS %s failed to be destroyed",vpsname)
			return returnString
	else:
		return "NOTOK"

def killVPS(vpsname,imagetype='lvm'):
	username = getUser()
	if username == dtcxen_user or username == vpsname:
		logging.info("Destroying vps xen%s VPS partitions", vpsname)
		cmd = "/usr/sbin/dtc_kill_vps_disk %s %s" % (vpsname, imagetype)
		output = commands.getstatusoutput(cmd)
		logging.debug("Command stdout: %s",cmd)
	else:
		return "NOTOK"

def shutdownVPS(vpsname):
	username = getUser()
	if username == dtcxen_user or username == vpsname:
		xmargs=['foo','shutdown',vpsname]
		logging.info("Shutting VPS %s down", vpsname)
		localsysout = StringIO()
                localsyserr = StringIO()
		try:
			subprocess.Popen(["/usr/sbin/xm","shutdown",vpsname])
			logging.info("VPS %s shut down",vpsname)
			return "OK","Shut down %s" % vpsname
		except:
                        returnString =  "NOTOK - %s %s" % (localsyserr.getvalue(), localsysout.getvalue())
                        localsyserr.close() # FIXME not really needed to close the fds
                        localsysout.close() # reference count drops to zero -> boom they're closed.
			logging.exception("VPS %s failed to shut down",vpsname)
                        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():
	global tabsplitter
	username = getUser()
	if username == dtcxen_user:
		# Bellow is the new tested and working code
		domains = (
			tabsplitter(d.strip())[:6]
			for d in Popen(["/usr/sbin/xm","list"],stdout=PIPE).communicate()[0].splitlines()[1:]
			if d.strip()
		)
		domlist=[]
		for domain in domains:
			if (domain[0]!="Domain-0"):
				domlist.insert(1,domain)
		domlist.sort()
		return (domlist)
	else:
		return "NOTOK"

def changeVPSxmPassword(vpsname,password):
	username = getUser()
	if username == dtcxen_user or username == vpsname:
		output = 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:
		# FIXME EGREGIOUS SECURITY ERROR, EVERYWHERE WHERE COMMANDS MODULE IS USED
		output = 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))
			# In case we are using authorized_keys2, we do it a 2nd time.
			filename = "/var/lib/dtc-xen/ttyssh_home/%s/.ssh/authorized_keys2" % 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
		status = getVPSState(vpsname)
		if status != "Not running":
			logging.warn("Status isn't good, we are already in process, or actually live")
			return "NOTOK, %s" % status
		# Write the semaphore file before proceeding
		fd2 = open(filename, 'w')
		fd2.write("fsck\n")
		logging.info("Starting file system check for %s",vpsname)
		cmd = "/sbin/fsck.ext3"
		args = [cmd, "-p","/dev/lvm1/%s" % vpsname ]
		spawnedpid = os.spawnv(os.P_NOWAIT, cmd, args ) 
		fd2.write("%s\n" % spawnedpid)
		fd2.close()
		return "OK"
	return "NOTOK"

def changeBSDkernel(vpsname,ramsize,kerneltype,allipaddrs):
	username = getUser()
	if username == dtcxen_user or username == vpsname:
		logging.info("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
		output = commands.getstatusoutput(cmd)
		return "OK"

def writeXenHVMconf(vpsname,ramsize,allipaddrs,vncpassword,howtoboot):
	username = getUser()
	if username == dtcxen_user or username == vpsname:
		cmd = "dtc_write_xenhvm_conf %s %s '%s' %s %s" % (vpsname,ramsize,allipaddrs,vncpassword,howtoboot)
		logging.info("Now calling: %s" % cmd)
		print cmd
		output = commands.getstatusoutput(cmd)
		return "OK"

def reportInstalledIso(vpsname):
	username = getUser()
	if username == dtcxen_user or username == vpsname:
		path = "/var/lib/dtc-xen/ttyssh_home/%s"%vpsname
		files = [ os.path.basename(f) for f in glob.glob(path+"/*.iso") if os.path.isfile(f) ]
		return files

def reinstallVPSos(vpsname,ostype,ramsize,password,nics=None,gateway=None,dns=None):
	# Take care! This time, the vpsname has to be only the number (eg XX and not xenXX)
	# nicspecs is a series of NIC specifications
	username = getUser()
	if username == dtcxen_user or username == vpsname:
		logging.info("Reinstalling %s on VPS %s",ostype,vpsname)  #maybe these should be notices if notices are below info severity
		filename = "/var/lib/dtc-xen/states/xen%s" % vpsname
		logging.debug("Checking %s for mkos",vpsname)
		status = getVPSState("xen%s" % vpsname)
		if status != "Not running":
			return "NOTOK, %s" % status
		# Write the semaphore file before proceeding
		fd2 = open(filename, 'w')
		fd2.write("mkos\n")

		log = file("%s/%s.log" % (vpsimage_mount_point, vpsname),"w",0)
		# FIXME idea: we could reuse the log isntead of a file, a stringio or something that reflects all the log activity into the logging module
		# that way everything goes into /var/log/dtc-soap-server.log
		# brilliant? you be the judge
		args = ["/usr/sbin/dtc_reinstall_os", "-v",
			"-vpsid", vpsname,
			"-ram", ramsize,
			"-os", ostype ]
		if gateway: args = args + [ "-gw", gateway ]
		if dns: args = args + [ "-dns", dns ]
		if type(nics) in (str,unicode):
			if "+" in nics: nics = [ s.strip() for s in nics.split("+") if s.strip() ]
			else: nics = [ s.strip() for s in nics.split("\n") if s.strip() ]
		if nics:
			nicargs = []
			for nic in nics:
				nicargs.append("-nic")
				nicargs.append(nic)
			args = args + nicargs
		
		newenv = os.environ.copy()
		newenv['PASSWORD'] = password
		logging.debug("Running %s in subprocess",args)
		if subprocess:
			proc = subprocess.Popen(args,stdout=log,stderr=subprocess.STDOUT,close_fds=True,cwd="/",env=newenv)
			spawnedpid = proc.pid
			def wait_for_child(): # watcher thread target
				try:
					proc.wait()
					if proc.returncode != 0: level = logging.warn
					else: level = logging.debug
					level("Subprocess %s (PID %s) is done -- return code: %s",
						threading.currentThread().getName(),proc.pid,proc.returncode)
				except:
					logging.exception("Watcher thread %s died because of exception",threading.currentThread().getName())
					raise
			watcher = threading.Thread(target=wait_for_child,name="dtc_reinstall_os watcher for xen%s"%vpsname)
			watcher.setDaemon(True)
			watcher.start()
			logging.debug("Subprocess %s (PID %s) started and being watched",watcher.getName(),spawnedpid)
		else:
			spawnedpid = os.spawnv(os.P_NOWAIT, cmd, args )

		fd2.write("%s\n" % spawnedpid)
		fd2.close()
		logging.info("Reinstallation process launched")
		return "OK, started mkos."
	return "NOTOK"

def setupLVMDisks(vpsname,hddsize,swapsize,imagetype='lvm'):
	username = getUser()
	if username == dtcxen_user or username == vpsname:
		logging.info("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)
		output = commands.getstatusoutput(cmd) # FIXME THIS IS FAIL -- it doesnt get stderr
		logging.debug("Command stdout: %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 startup2():
	global perfdata_dir,data_collection_lock,perfdata_dir,tabsplitter,tabcolonsplitter
	logging.info("Finished defining functions.")
	tabsplitter = re.compile("[\t ]+").split
	tabcolonsplitter = re.compile("[\t :]+").split
	data_collection_lock = RLock()
	perfdata_dir = os.path.join("/","var","lib","dtc-xen","perfdata")

	logging.info("Setup Performance Data directory at %s" % perfdata_dir)

class Server(Thread):
	
	def __init__(self,server):
		Thread.__init__(self,name="Server thread")
		self.server = server
		self.setDaemon(True)
	
	@log_exceptions
	def run(self):
		logging.info("Starting server thread")
		while True:
			try: self.server.serve_forever()
			except Exception: logging.exception("Server thread died, restarting")


class DataCollector(Thread):
	global data_collection_lock	
	def __init__(self):
		Thread.__init__(self,name="Data collector thread")
		self.setDaemon(True)
	
	@log_exceptions
	def run(self):
		"""Saves a sample to path perfdata_dir
		
		prototype sample:
		{
			"xen01" : {
				"timestamp":1237287382.45,
				"diff_cpu_time":51.5, # cputime
				"diff_net_inbytes":838375767, # network bytes in
				"diff_net_outbytes":324328402389, # network bytes out
				"diff_filesystem_sectors":5134, # fs sectors
				"diff_swap_sectors":5134, # fs sectors
				...
			},
			"xen02" : {
				"timestamp":1237287382.45,
				"diff_cpu_time":51.5, # cputime
				"diff_net_inbytes":838375767, # network bytes in
				"diff_net_outbytes":324328402389, # network bytes out
				"diff_filesystem_sectors":5134, # fs sectors
				"diff_swap_sectors":5134, # fs sectors
				...
			},
		}
		
		each item in the sample dictionary is keyed by node name, and its value contains:
		timestamp: time.time() output
		diff_cpu_time: differential CPU time, as a float (cputime column in xm list)
		diff_net_*bytes: differential network bytes for the first network device assigned to it, by Xen ID
		diff_*_sectors: differential total disk blocks read + written (both swap and file partition accesses)
		"""
		logging.info("Starting data collection thread")
		
		# load latest data file
		try: dictionary = pickle.load(file(os.path.join(perfdata_dir,"last-sample.pickle")))
		except IOError,e:
			if e.errno == 2: dictionary = {}
			else: raise
		
		while True:
			
			started_time = time.time()
			
			global tabsplitter
			old_dictionary,dictionary = dictionary,{}
			domains = (
				tabsplitter(d.strip())
				for d in Popen(["/usr/sbin/xm","list"], stdout=PIPE).communicate()[0].splitlines()[2:]
					if d.strip()
			)
			procnetdev_readout = file("/proc/net/dev").readlines()
			for domain in domains:
				name,xid,mem,cpu,state,cpu_time=domain[0:6]
				cpu_time = float(cpu_time)
				# if cpu time was measured the last time, and it was less than this one, get the difference
				findvif = [
                                                re.sub("sdkljflsdkjfsdlfj.*:","",o).strip()
                                                for o in procnetdev_readout
                                                if o.startswith("vif%s.0:"%xid)
                                          ]
                                # handle the condition where the vif interface isn't up yet, or has been shutdown
                                if len(findvif) > 0:
                                        vifdiscard,net_inbytes,a,a,a,a,a,a,a,net_outbytes,a,a,a,a,a,a,a=tabcolonsplitter(
                                                 findvif [0]
                                        )
                                else:
                                        logging.info("Could not find vif%s.0!"%xid)

				net_inbytes,net_outbytes = int(net_inbytes),int(net_outbytes)

				def get_blocks_dm(minor):
					line = file("/sys/block/dm-%s/stat" % minor,'r').readline().split()
					return int(line[2]) + int(line[6])
				
				try:	filesystem_sectors = get_blocks_dm(
						os.minor(os.stat("/dev/mapper/%s-%s" % (server_lvmname,name)).st_rdev) )
				except OSError,e:
					if e.errno == 2: filesystem_sectors = 0
					else: raise
				try:	swap_sectors = get_blocks_dm(
						os.minor(os.stat("/dev/mapper/%s-%sswap" % (server_lvmname,name)).st_rdev) )
				except OSError,e:
					if e.errno == 2: swap_sectors = 0
					else: raise
				
				# now we account for the difference if it is the sensible thing to do

				dictionary[name] = {
					"timestamp":started_time,
					"diff_cpu_time":cpu_time,
					"diff_net_inbytes":net_inbytes,
					"diff_net_outbytes":net_outbytes,
					"diff_filesystem_sectors":filesystem_sectors,
					"diff_swap_sectors":swap_sectors,
					"cpu_time":cpu_time,
					"net_inbytes":net_inbytes,
					"net_outbytes":net_outbytes,
					"filesystem_sectors":filesystem_sectors,
					"swap_sectors":swap_sectors,
				}
				# compute differences
				if name in old_dictionary:
					for reading in "cpu_time,net_inbytes,net_outbytes,filesystem_sectors,swap_sectors".split(","):
						# we basically diff old and new unless old is bigger than new
						if old_dictionary[name][reading] <= dictionary[name][reading]:
							dictionary[name]["diff_"+reading] = dictionary[name][reading] - old_dictionary[name][reading]
			
			try:
				data_collection_lock.acquire()
				try: os.mkdir(perfdata_dir)
				except OSError,e:
					if e.errno != 17: raise
				pickle.dump(dictionary,file(os.path.join(perfdata_dir,"last-sample.pickle"),"w"))
				pickle.dump(dictionary,file(os.path.join(perfdata_dir,"sample-%s.pickle"%time.time()),"w"))
			finally: data_collection_lock.release()
			
			elapsed_time = time.time() - started_time
			logging.info("Sample data collected.  Collection time: %s seconds"%elapsed_time)
			time.sleep(60 - elapsed_time)

def getCollectedPerformanceData(count=None):
	"""Returns a list with the latest samples collected by the
	DataCollector, then removes them from the disk.
	If the count argument is specified, it fetches then deletes
	a maximum of <count> samples, in chronological order.
	This allows for batched data fetches.
	"""
	username = getUser()
        if username == dtcxen_user:
		logging.info("getCollectedPerformanceData called.")
		samples = []
		try:
			data_collection_lock.acquire()
			loadfiles = glob.glob(os.path.join(perfdata_dir,"sample-*.pickle"))
			loadfiles.sort()
			if count > 0: loadfiles = loadfiles[:count]
			samples = [ pickle.load(file(p)) for p in loadfiles ]
			for p in loadfiles: os.unlink(p)
		finally: data_collection_lock.release()
		
		return samples
	else:
		return "NOTOK"

def getVPSState(vpsname):
	username = getUser()
        if username == dtcxen_user or username == vpsname:
		# fixme: sed upon rpm build time to set the correct path for /var/lib
        	filename = "/var/lib/dtc-xen/states/%s" % vpsname
        	logging.debug("Checking %s for getVPSState", filename)
        	try:
			logging.debug("Opening %s" , filename)
	        	fd = open(filename, 'r')
			command = fd.readline()
			logging.debug( "Checking fsck")
			if string.find(command,"fsck") != -1:
				fsckpid = int(fd.readline())
				logging.debug( "fsck process meant to be at %s" , fsckpid)
				try:
					returnstatus = os.waitpid(fsckpid, os.WNOHANG)
					if returnstatus[1] == 0:
						logging.debug( "Founded running fsck!")
						fd.close()
						return "fsck"
					else:
						logging.debug( "Status is %s" , returnstatus[1])
				except:
					logging.debug( "Failed to find running process... delete state file...")
				fd.close()
				os.remove(filename)
			else:			
				logging.debug( "Checking mkos")
				if string.find(command,"mkos") != -1:
					mkospid = int(fd.readline())
					logging.debug( "mkos process meant to be at %s" , mkospid)
					try:
						returnstatus = os.waitpid(mkospid, os.WNOHANG)			
						if returnstatus[1] == 0:
							logging.debug( "Founded running mkos!")
							fd.close()
							return "mkos"
						else:
							logging.debug( "Status is %s" , returnstatus[1])
					except:
						logging.debug( "Failed to find running process... delete state file...")
					fd.close()
					os.remove(filename)
				else:
					logging.debug( "Invalid state file...")
					fd.close()
					return "NOTOK, invalid state file"
		except IOError,e: # FIXME WHY is this trapped?
			if e.errno == 2: logging.debug("No semaphore (fsck/mkos): continuing")
			else: raise
		output,error = subprocess.Popen(["/usr/sbin/xm","list",vpsname],
			stdout=subprocess.PIPE,
			stderr=subprocess.PIPE).communicate()
		if error: return "Not running"
		values = [ s.strip() for s in output.splitlines() ][-1].split()
		names = ["id","domid","maxmem","vcpus","state","cpuseconds"]
		info = dict( zip(names,values) )
		for val in "maxmem vcpus domid".split(): info[val] = int(info[val])
		for val in "cpuseconds".split(): info[val] = float(info[val])
		thelist = ["domain"]
		thelist.extend( [ i for i in info.items() ] )
		return thelist
	else:
		return "NOTOK"

def getVPSInstallLog(vpsname,numlines):
	username = getUser()
        if username == dtcxen_user or username == vpsname:
		log = file("%s/%s.log" % (vpsimage_mount_point, vpsname),"r").readlines()
		numlines = int(numlines)
		if not numlines or numlines < 0: lastlines = "\n".join(log)
		else: lastlines = "\n".join(log[-numlines:])
		def toascii(char):
			if ord(char) in [10,13] or 32 <= ord(char) <= 127: return char
			return " "
		lastlines = "".join([ toascii(c) for c in lastlines ])
		return lastlines
	else:
		return "NOTOK"

def getInstallableOS():
	folderlist = os.listdir('/usr/share/dtc-xen-os/')
	return folderlist

def getInstallableAPP():
	folderlist = os.listdir('/usr/share/dtc-xen-app/')
	return folderlist

def startup3():
	# 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:
		logging.debug( "Valid user: %s", username)
		return 1
	else:
		return 0

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

	c = kw["_SOAPContext"]
	logging.debug( "**kw =%s" , str(kw) )

	# The socket object, useful for
	logging.debug( "Peer connected: %s",  c.connection.getpeername() )

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

	if not ah:
		logging.debug("NO authorization information in HTTP headers, refusing.")
		return 0

	# decode and analyze the string for the username and password
	# (we slice the string from 6 onwards to remove the "Basic ")
	try: username, password = base64.decodestring(ah[6:].strip()).split(":")
	except ValueError: # moron user did not specify the user name and password
		logging.debug("NO password information in HTTP headers, refusing.")
		return 0
	

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

	for line in fd:
		u, h = line.strip().split(':')
		if u == username:
			verify_pass = crypt.crypt(password, h[:2])
			if verify_pass == h:
				fd.close()
				logging.debug( "Password matches the one in the file!")
				return 1
			else:
				fd.close()
				logging.debug("Password didn't match the one in .htpasswd")
				return 0
    
	logging.debug("Couldn't find user in password file!")
	return 0
   

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

def main(argv=None):
	startup()
	startup2()
	startup3()

	if not Config.SSLserver:
		### This is wrong.  IT should just raise Import Error.  FIXME
		raise RuntimeError, "this Python installation doesn't have OpenSSL and M2Crypto"

	logging.info("Starting SSL context to secure webservice...")
	ssl_context = SSL.Context()

	ssl_context.load_cert(firstexisting(['/etc/dtc-xen/dtc-xen.cert.cert','/etc/pki/tls/certs/dtc-xen.crt']), firstexisting(['/etc/dtc-xen/privkey.pem','/etc/pki/tls/private/dtc-xen.key']), callback=_passphrase)

	logging.info("Starting SOAP server to serve webserivce requests...")

	soapserver = SOAPpy.SOAPServer((server_host, server_port), ssl_context = ssl_context)
	# No ssl 
	# soapserver = SOAPpy.SOAPServer((server_host, server_port))

	#let's make some functions log exceptions, arguments and retvalues
	for f in [startVPS,destroyVPS,reinstallVPSos,getVPSState,getCollectedPerformanceData]: f = log_exceptions(f)
	# this is required because really really really old python versions don't support decorators

	logging.info("Register functions with the SOAP server...")

	soapserver.registerFunction(_authorize)
	soapserver.registerFunction(testVPSServer)
	soapserver.registerFunction(startVPS)
	soapserver.registerFunction(destroyVPS)
	soapserver.registerFunction(shutdownVPS)
	soapserver.registerFunction(killVPS)
	soapserver.registerFunction(listStartedVPS)
	soapserver.registerFunction(getVPSState)
	soapserver.registerFunction(changeVPSxmPassword)
	soapserver.registerFunction(changeVPSsoapPassword)
	soapserver.registerFunction(changeVPSsshKey)
	soapserver.registerFunction(reportInstalledIso)
	soapserver.registerFunction(reinstallVPSos)
	soapserver.registerFunction(fsckVPSpartition)
	soapserver.registerFunction(changeBSDkernel)
	soapserver.registerFunction(writeXenHVMconf)
	soapserver.registerFunction(setupLVMDisks)
	soapserver.registerFunction(getCollectedPerformanceData)
	soapserver.registerFunction(getInstallableOS)
	soapserver.registerFunction(getVPSInstallLog)
	soapserver.registerFunction(getInstallableAPP)

	if run_as_daemon:
		# Daemonize requested.  Daemonize and write pid file to /var/run/dtc-xen.pid
		logging.info("Attempting to daemonize...")
		import daemon
		# unconditionally close stdin/stdout/stderr, otherwise yum reinstall fails
		for fd in [0,1,2]:
			try: os.close(fd)
			except OSError: pass
		daemon.daemonize()
		pid = os.getpid()
		pidfile =file("/var/run/dtc-xen.pid","w")
		pidfile.write("%s"%pid)
		pidfile.close()
		logging.info("Daemonization successful -- running with PID %s"%pid)
	else:
		logging.info("Not running as a daemon (running as a normal process)")
	
	signal.signal(signal.SIGINT,sighandler)
	signal.signal(signal.SIGTERM,sighandler)
	signal.signal(signal.SIGHUP,sighandler)


	collector = DataCollector()
	collector.start()
	server = Server(soapserver)
	server.start()
	global keepRunning
	while keepRunning == 1:
		signal.pause()
	shutdown()

if __name__ == "__main__":
	
	# enable the below to trace the main server process
	#import sys
	#import trace

	# create a Trace object, telling it what to ignore, and whether to
	# do tracing or line-counting or both.
	#tracer = trace.Trace(
	    #ignoredirs=[sys.prefix, sys.exec_prefix],
	    #trace=1,
	    #count=1)

	# run the new command using the given tracer
	#tracer.run('main()')

	# make a report, placing output in /tmp
	#r = tracer.results()
	#r.write_results(show_missing=True, coverdir="/tmp")

	# cleanly run main() and handle exits
    	sys.exit(main())
