#!/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
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 *

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


def sighandler(signum,frame):
	global last_signal
	last_signal = signum

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
	for h in logging.root.handlers: logging.root.removeHandler(h)

	if run_as_daemon:
		oldumask = os.umask(31)
		try:
			logging.basicConfig(filename="/var/log/dtc-xen.log",level=log_level)
			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

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
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")
if cert_passphrase: 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 ------


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()
		sys.stdout = localsysout
		sys.stderr = localsyserr
		try:
#			xen.xm.main.main(xmargs)
			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.
			sys.stdout = sys.__stdout__
			sys.stderr = sys.__stderr__
			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()
                sys.stdout = localsysout
                sys.stderr = localsyserr
		try:
#			xen.xm.main.main(xmargs)
			subprocess.Popen(["/usr/sbin/xm","destroy",vpsname])
			logging.info("VPS %s destroyed",vpsname)
			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()
			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()
                sys.stdout = localsysout
                sys.stderr = localsyserr
		try:
#			xen.xm.main.main(xmargs)
			subprocess.Popen(["/usr/sbin/xm","shutdown",vpsname])
			logging.info("VPS %s shut down",vpsname)
			return "OK","Shut down %s" % vpsname
		except:
                        sys.stdout = sys.__stdout__
                        sys.stderr = sys.__stderr__
                        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():
	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 writeXenPVconf(vpsname,ramsize,allipaddrs,vncpassword,howtoboot):
	username = getUser()
	if username == dtcxen_user or username == vpsname:
		cmd = "dtc_write_xenpv_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]

from threading import Thread,RLock
from subprocess import Popen,PIPE
import re
import time
import pickle
tabsplitter = re.compile("[\t ]+").split
tabcolonsplitter = re.compile("[\t :]+").split
data_collection_lock = RLock()
perfdata_dir = os.path.join("/","var","lib","dtc-xen","perfdata")

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):
	
	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()
			
			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
				vifdiscard,net_inbytes,a,a,a,a,a,a,a,net_outbytes,a,a,a,a,a,a,a=tabcolonsplitter(
					[
						re.sub("sdkljflsdkjfsdlfj.*:","",o).strip()
						for o in procnetdev_readout
						if o.startswith("vif%s.0:"%xid)
					] [0]
				)
				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:
		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

# 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

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"

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)

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

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(writeXenPVconf)
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)

signal.signal(signal.SIGINT,sighandler)
signal.signal(signal.SIGTERM,sighandler)

collector = DataCollector()
collector.start()
server = Server(soapserver)
server.start()
signal.pause()
shutdown()

