"""
Virt management features

Copyright 2009, Byte-Code, srl
Simone Pucci <spucci@byte-code.com>

This software may be freely redistributed under the terms of the GNU
general public license.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
"""

# warning: virt management is rather complicated
# Some libvirt methods doesn't work, so I made some workarounds in this module...

# other modules
import os
import re
import iptables
import libvirt

# our modules
#import codes
import func_module

class Symbolic(func_module.FuncModule):
    version = "0.0.1"
    api_version = "0.0.1"
    description = "Symbolic control module"


    def dictionarize(self,regexp,logline,neededTags):
        '''Given a raw regexp and a string, it returns a dictionary containing all elements that matched the regexp'''
        fields={}
        myregex = re.compile(regexp)
        matched = myregex.search(logline)
        if matched:
            fields = matched.groupdict()
        #If a required element is missing set it to Null
        for tag in neededTags.split(" "):
            if (fields.has_key(tag) == False):
                fields[tag]="None"
        return fields


    def get_xml(self, vmid):
        """
	Recieve a Vm id as input
        Return an xml describing vm config returned by a libvirt call
	"""
	conn = libvirt.openReadOnly(None)
	if conn == None:
    		return (-1,'Failed to open connection to the hypervisor')
	try:
    		domV = conn.lookupByName(vmid)
	except:
    		return (-1,'Failed to find the main domain')
	return domV.XMLDesc(0)


    def get_graphics(self,vmid,xml='None'):
	"""
	Recieve a Vm id as input
	Read machine informations from xml config,
	return a key/val map containing only graphics properties
	"""
	out = {'autoport': 'None', 'keymap': 'None', 'type': 'vnc', 'port': 'None', 'listen': 'None'}
	myregex = '\<graphics(?P<graphics>[^"]*)/\>'
	if(xml=='None'):
	    xml = self.get_xml(vmid)
	for x in xml.split('\n'):
            tmpout = self.dictionarize(myregex, str(x), "graphics")
	    if(tmpout['graphics'] != "None"):
		myregex1 = r'port=\'(?P<port>\d*)\''
		myregex2 = r'type=\'(?P<type>vnc|sdl)\''
		myregex3 = r'autoport=\'(?P<autoport>yes|no)\''
		myregex4 = r'listen=\'(?P<listen>\d+\.\d+\.\d+\.\d+)\''
		myregex5 = r'keymap=\'(?P<keymap>[^"]*)\''
		out['port'] = self.dictionarize(myregex1, tmpout['graphics'], "port")['port']
		out['type'] = self.dictionarize(myregex2, tmpout['graphics'], "type")['type']
		out['autoport'] = self.dictionarize(myregex3, tmpout['graphics'], "autoport")['autoport']
		out['listen'] = self.dictionarize(myregex4, tmpout['graphics'], "listen")['listen']
		out['keymap'] = self.dictionarize(myregex5, tmpout['graphics'], "keymap")['keymap']
	return out


    def set_graphics(self,vmid,xml):
	"""
	Recieve a Vm id and a piece of xml as input
	Set vnc address and parameters of vm in xml config file
	Return 0 if config has been correctly written
	"""
	try:
	   conn = libvirt.openReadOnly(None)
	   tmp = conn.getType()
	except:
	   return (-1,'Failed to open connection to the hypervisor')
	strxml = self.get_graphics(vmid,xml)
	str = "vfb = [ \"vncunused=1, "
 
	for el in strxml:
	    if(strxml[el] != 'None'):
		if(el == 'port'):
		    str = "%s%s=\'%s\', " % (str,"vncdisplay",(int(strxml[el])-5900))
		else:
		    str = "%s%s=\'%s\', " % (str,el,strxml[el])
	str = "%s\" ]" % str.rstrip(' ').rstrip(',')

	if(tmp == "Xen"):
	    if(os.system("[ -f /etc/xen/%s ]" % vmid) == 0):
		return os.system("sed -i 's/^vfb.*/%s/g' /etc/xen/%s" % (str,vmid))
	    else:
		return (-1,'Config file /etc/xen/%s not found' % vmid)
	else:
	    if(os.system("[ -f /etc/libvirt/qemu/%s.xml ]" % vmid) == 0):
	        return os.system("sed -i 's/<graphics.*>$/%s/g' /etc/libvirt/qemu/%s.xml" % (xml,vmid))
	    else:
		return (-1,'Config file /etc/libvirt/qemu/%s.xml not found' % vmid)
	return (-2,'Unmatched Condition in set_graphics method')


    def runtime_set_graphics(self,vmid):
        """
	Recieve a Vm id as input
        Set vnc interface of running xen vm killing the running instance and executing a new server on public interface.
	Return needed connection info
        """
	ipt = iptables.Iptables()
	result = os.popen("ps ax").read()
	myregex = '(?P<pid>\ +\d+|\d+) (?P<tty>\?|tty\d|pts.\d) (?P<status>[^"]*) (?P<time>\d:\d\d) (?P<command>[^"]*)'
	for x in result.split('\n'):
            out = self.dictionarize(myregex, str(x),"pid tty time status command")
	    if (("/usr/lib/xen/bin/qemu-dm" in out['command']) and (vmid in out['command'])):
		conn = libvirt.open(None)
		if conn == None:
                    return (-1,'Failed to open connection to the hypervisor')
        	try:
                    domV = conn.lookupByName(vmid)
        	except:
                    return (-1,'Failed to find the main domain')
		domVID = domV.ID()
		vncport = domVID + 5900
		interface = "0.0.0.0:%d" % domVID
		ipt.run("-D INPUT -p tcp --dport %d -j ACCEPT" % vncport)
		ipt.run("-I INPUT -p tcp --dport %d -j ACCEPT" % vncport)
		#os.system("/sbin/iptables -I INPUT -p tcp --dport 590%d -j ACCEPT" % domV.ID())
		os.system("/bin/kill -9 %s" % out['pid'])
		vncservercmd = re.sub(r'(\d+\.\d+\.\d+\.\d+:\d*)', interface, out['command'])
		retval = os.system("%s &" % vncservercmd)
		if (retval != 0):
		    return (-1,"Failed to complete command, check manually the problem")
		hostname = os.popen("hostname").read()
		hostname = hostname.rstrip('\n')
		return "%s:%s" % (hostname, domVID)
	return (-1,"Failed to find process for %s machine" % vmid)

################################################################################
################################################################################
    def attach_device(self,vmid,xml):
	"""
	Attach a device described in xml
	Check bugs
	"""
        conn = libvirt.open(None)
        if conn == None:
                return 'Failed to open connection to the hypervisor'
        try:
                domV = conn.lookupByName(vmid)
        except:
                return 'Failed to find the main domain'
        return domV.attachDevice(xml)

    def detach_device(self,vmid,xml):
	"""
	Detach a device described in xml
	Check bugs
	"""
        conn = libvirt.open(None)
        if conn == None:
                return 'Failed to open connection to the hypervisor'
        try:
                domV = conn.lookupByName(vmid)
        except:
                return 'Failed to find the main domain'
        return domV.detachDevice(xml)

    def connect(self,vmid):
        """
        AT THE MOMENT IS A TEST
	Check bugs
	"""
	conn = libvirt.open(None)
	if conn == None:
    		return 'Failed to open connection to the hypervisor'
	try:
    		domV = conn.lookupByName(vmid)
	except:
    		return 'Failed to find the main domain'
	connect = domV.connect()
	return connect.getHostname()
	#return dir(connect)
################################################################################
################################################################################
#Module Usage Examples:

##!/bin/env python

#from func.overlord import client

#hostname_dom = "tt01.byte-code.lan"

#funcmachine = client.Client(hostname_dom)


###Virt Func Module methods
##funcmachine.virt.XXX
##=========================
#print "Freemem: 	%s" % funcmachine.virt.freemem()
#print "List_vms:	%s" % funcmachine.virt.list_vms()
#print "Virttype:	%s" % funcmachine.virt.virttype()
#print "State:		%s" % funcmachine.virt.state()
#print "Info:		%s" % funcmachine.virt.info()
#print "Nodeinfo:	%s" % funcmachine.virt.nodeinfo()
#print "Get_status:	%s" % funcmachine.virt.get_status("symbolic")

###Symbolic Func Module custom methods
##funcmachine.symbolic.XXX
##====================================

#dictionarize returns a map containing regex results
#print funcmachine.symbolic.dictionarize(r'(?P<graphics>[^"]*)',"prova di figa", "graphics")

##get_xml ask libvirt to get a vm xml
##print "%s" % funcmachine.symbolic.get_xml("symbolic")[hostname_dom]

##get_graphics parse vm xml and return a map with graphics info
##print "%s" % funcmachine.symbolic.get_graphics("symbolic")
##print "vncviewer %s:%s" % (hostname_dom, funcmachine.symbolic.get_graphics("station1")[hostname_dom]['port'])

##set_graphics write in config file of vm, graphic parameters described in the xml graphics section
##print "%s" % funcmachine.symbolic.set_graphics("symbolic","<graphics type='vnc' port='5904' listen='0.0.0.0'/>")

##runtime_set_graphics run vncserver of xen hosts on public interface and return relative info
##print "%s" % funcmachine.symbolic.runtime_set_graphics("symbolic")


###Symbolic Func Module Untested/Work in progress methods don't use them...
##funcmachine.symbolic.XXX
##=========================================================================
###print "%s" % funcmachine.virt.connect("test")
###print "%s" % funcmachine.virt.attach_device("test","<disk type='file' device='cdrom'>\n<source file='/tmp/rhel-5-server-i386-disc1.iso'/>\n<target dev='hdb' bus='ide'/>\n<readonly/>\n</disk>")
###print "%s" % funcmachine.virt.detach_device("test","<disk type='file' device='cdrom'>\n<source file='/tmp/rhel-5-server-i386-disc1.iso'/>\n<target dev='hdb' bus='ide'/>\n<readonly/>\n</disk>")


