#!/usr/bin/python
import sys
import glob
import re
import os
from subprocess import *

BASE_MAC_PREFIX = "DE:BE:DE:EF"
BASE_SNAPSHOT_PATH = "/tftpboot/images/centos5/diskless/snapshot"
ARPTABLE = "/proc/net/arp"

def usage():
    print "Usage:"
    print "\tbackupServerAdmin list"
    print "\tbackupServerAdmin create <HV MAC Addr> <RAM> <vCPUs> <Bridge1,Bridge2,...BridgeN>"
    print "\tbackupServerAdmin delete <HV MAC Addr> <VMname>"
    print "\tbackupServerAdmin start <HV MAC Addr> <VMname>"
    print "\tbackupServerAdmin stop <HV MAC Addr> <VMname>"
    print "\tbackupServerAdmin move <Src HV MAC Addr> <Dst HV MAC Addr> <VMname>"
    print "\tbackupServerAdmin hvnetinfo <HV MAC Addr>"
    sys.exit(0)

def getArpTable():
    d = {}
    fd = open(ARPTABLE,'r')
    for l in fd.readlines()[1:]:
        addr,t,f,mac = l.split()[0:4]
        d[re.sub(":","-",mac).lower()] = addr
    fd.close()
    return d

def parseConfig(path):
    d = {}
    fd = open(path,'r')
    for l in fd.readlines()[1:]:
        div = l.find('=')
        key = l[0:div]
        value = l[div+1:-1].strip()
        d[key] = value
    fd.close()
    return d

def parseNetConfig(confstr):
    D = {}
    entries = [x for x in confstr[1:-1].split("'") if len(x) > 1]
    for e in entries:
        d = {}
        for i in e.split(','):
            key,value = i.split('=')
            d[key] = value
        D[d['vifname']] = d
    return D

def listBridges(ip):
    bridgeinfo = remoteExec(['brctl','show'],ip).split('\n')
    bridges = []
    for line in bridgeinfo[1:-1]:
        if not re.match("^\s",line):
            bridges.append(line.split()[0])
    return bridges

def remoteExec(cmd, ip):
    cmdList = ["ssh",ip] + cmd
    process = Popen(cmdList, shell=False,stdout=PIPE,stderr=PIPE)
    out,err = process.communicate()
    return out

def localExec(cmd):
    process = Popen(cmd, shell=False,stdout=PIPE,stderr=PIPE)
    out,err = process.communicate()
    return out

def startVM(ip,mac,vmname):
    vmconf = "%s/%s/BServers/%s" % (BASE_SNAPSHOT_PATH,mac,vmname)
    if not os.path.exists(vmconf):
        print "Unable to find VM to start"
        sys.exit(1)
    config = parseConfig(vmconf)
    netdict = parseNetConfig(config['vif'])
    #print netdict

    cpIP,netmask = getBootIP() 

    path = "/.rw/BServers/%s" % vmname
    remoteExec(["xm","create",path],ip)
    for key in netdict.iterkeys():
        d = netdict[key]
        remoteExec(["/sbin/arptables","-D","FORWARD","-j","ACCEPT","-o",d['vifname']],ip)
        remoteExec(["/sbin/arptables","-D","FORWARD","-j","ACCEPT","-i",d['vifname']],ip)
        remoteExec(["/sbin/arptables","-A","FORWARD","-j","ACCEPT","-o",d['vifname']],ip)
        remoteExec(["/sbin/arptables","-A","FORWARD","-j","ACCEPT","-i",d['vifname']],ip)

    remoteExec(["/sbin/iptables","-D","FORWARD","-j","ACCEPT","-s","%s/%s"%(cpIP,netmask)],ip)
    remoteExec(["/sbin/iptables","-D","FORWARD","-j","ACCEPT","-d","%s/%s"%(cpIP,netmask)],ip)
    remoteExec(["/sbin/iptables","-D","FORWARD","-j","ACCEPT","-d","255.255.255.255"],ip)
    remoteExec(["/sbin/iptables","-A","FORWARD","-j","ACCEPT","-s","%s/%s"%(cpIP,netmask)],ip)
    remoteExec(["/sbin/iptables","-A","FORWARD","-j","ACCEPT","-d","%s/%s"%(cpIP,netmask)],ip)
    remoteExec(["/sbin/iptables","-A","FORWARD","-j","ACCEPT","-d","255.255.255.255"],ip)

def stopVM(ip,mac,vmname):
    vmconf = "%s/%s/BServers/%s" % (BASE_SNAPSHOT_PATH,mac,vmname)
    if not os.path.exists(vmconf):
        print "Unable to find VM to stop"
        sys.exit(1)

    remoteExec(["xm","destroy",vmname],ip)    
    config = parseConfig(vmconf)
    netdict = parseNetConfig(config['vif'])

    cpIP,netmask = getBootIP()
    for key in netdict.iterkeys():
        d = netdict[key]
        remoteExec(["/sbin/arptables","-D","FORWARD","-j","ACCEPT","-o",d['vifname']],ip)
        remoteExec(["/sbin/arptables","-D","FORWARD","-j","ACCEPT","-i",d['vifname']],ip)
    remoteExec(["/sbin/iptables","-D","FORWARD","-j","ACCEPT","-s","%s/%s"%(cpIP,netmask)],ip)
    remoteExec(["/sbin/iptables","-D","FORWARD","-j","ACCEPT","-d","%s/%s"%(cpIP,netmask)],ip)
    remoteExec(["/sbin/iptables","-D","FORWARD","-j","ACCEPT","-d","255.255.255.255"],ip)

def gen_uuid():
    return int(localExec(['uuidgen']).split('-')[0].encode('hex')) % 2**32

def genMac():
    suffix = localExec(['uuidgen']).split('-')[1]
    return "%s:%s:%s" % (BASE_MAC_PREFIX,suffix[0:2],suffix[2:4])

def getBootIP():
    # first try with the new dhcp conf file path
    path = "/onapp/configuration/dhcp/dhcpd.conf"
    if not os.path.exists(path):
        # if not present, set to the pre-4.0 path
        path = "/home/onapp/dhcpd.conf"
    fd = open(path,'r')
    router = ""
    netmask = ""
    for l in fd.readlines():
        if re.match("^option routers",l.strip()):
            router = l.strip()[:-1].split()[2]
        elif re.match("^option subnet-mask",l.strip()):
            netmask = l.strip()[:-1].split()[2]
    fd.close()
    return router,netmask

def isRunning(name,ip):
    try:
        int(remoteExec(["xm","domid",name],ip))
        return True
    except:
        return False

def isXenHV(mac):
    prefix = re.sub(":","-",BASE_MAC_PREFIX).lower()
    if re.match("^%s" % prefix,mac):
        return False
    pxefile = "/tftpboot/pxelinux.cfg/01-%s" % mac
    fd = open(pxefile,'r')
    l = fd.readlines()[0:1]
    fd.close()
    if re.match(".*xen",l[0]):
        return True
    return False

def genMacs(num):
    macs = []
    for i in range(0,num):
        macs.append(genMac().upper())
    return macs

def genMacString(interfaces,macs):
    st = ""
    macs.reverse()
    for intf in interfaces:
        mac = macs.pop()
        name = gen_uuid()
        st += "'bridge=%s,vifname=%s,mac=%s'," % (intf,name,mac)
    return st[:-1]

def genConfig(mac,ram,vcpus,interfaces):
    uuid = gen_uuid()
    cpIP,netmask = getBootIP()
    macs = genMacs(len(interfaces))
    bootmac = re.sub(":","-",macs[0]).lower()
    vifstring = genMacString(interfaces,macs)
    path = "%s/%s/BServers/%s" % (BASE_SNAPSHOT_PATH,mac,uuid)
    if not os.path.exists(os.path.dirname(path)):
        os.makedirs(os.path.dirname(path))
    fd = open(path,'w')
    st = "##AUTO-GENERATED: DO NOT EDIT!!\n"
    st += "name='%s'\n" % uuid
    st += "memory='%d'\n" % ram
    st += "vcpus='%d'\n" % vcpus
    st += "kernel='/cloudboot/centos5/ramdisk-xen/vmlinuz'\n"
    st += "ramdisk='/cloudboot/centos5/ramdisk-xen/initrd.img'\n"
    st += "extra='udevtimeout=2 NFSNODEID=%s NFSROOT=%s:/tftpboot/export/centos5/xen CFGROOT=%s:/tftpboot/images/centos5/diskless/snapshot'\n" % (bootmac,cpIP,cpIP)
    st += "vif=[%s]\n" % vifstring
    #print "Config:\n%s" % st
    fd.write(st)
    fd.flush()
    os.fsync(fd)
    fd.close()

if len(sys.argv)<2:
    usage()

if sys.argv[1] == "list":
    arptable = getArpTable()
    for node in glob.glob(BASE_SNAPSHOT_PATH + "/*-*"):
        mac = os.path.basename(node)
        if not isXenHV(mac):
            continue
        nodestr = "Node %s " % mac
        if arptable.has_key(mac):
            nodestr += "(%s)" % arptable[mac]
        else:
            nodestr += "()"
        print nodestr
        for bs in glob.glob(node + "/BServers/*"):
            config = parseConfig(bs)
            active = isRunning(config['name'],arptable[mac])
            print "\tBackup Server %s:" % config['name']
            print "\t\tMemory: %s" % config['memory']
            print "\t\tvCPUs: %s" % config['vcpus']
            print "\t\tNetworks: %s" % config['vif']
            print "\t\tRunning: %s" % active
        print "\n"

elif sys.argv[1] == "create":
    if len(sys.argv) != 6:
        usage()
    mac = sys.argv[2]
    if not isXenHV(mac):
        print "Unable to identify Xen HV with that MAC address"
        sys.exit(1)
    ram = int(sys.argv[3])
    vcpus = int(sys.argv[4])
    interfaces = sys.argv[5].split(',')
    config = genConfig(mac,ram,vcpus,interfaces)
    
elif sys.argv[1] == "delete":
    mac = sys.argv[2]
    vmname = sys.argv[3]
    vmconf = "%s/%s/BServers/%s" % (BASE_SNAPSHOT_PATH,mac,vmname)
    if not os.path.exists(vmconf):
        print "No such VM"
    else:
        os.unlink(vmconf)

elif sys.argv[1] == "start":
    mac = sys.argv[2]
    vmname = sys.argv[3]
    arptable = getArpTable()
    ip = arptable[mac]
    startVM(ip,mac,vmname)

elif sys.argv[1] == "stop":
    mac = sys.argv[2]
    vmname = sys.argv[3]
    arptable = getArpTable()
    ip = arptable[mac]
    stopVM(ip,mac,vmname)

elif sys.argv[1] == "move":
    srcmac = sys.argv[2]
    dstmac = sys.argv[3]
    vmname = sys.argv[4]
    arptable = getArpTable()
    srcip = arptable[srcmac]
    if isRunning(vmname,srcip):
        print "Please stop the backup server before attempting this operation"
        sys.exit(0)
    srcfile = "%s/%s/BServers/%s" % (BASE_SNAPSHOT_PATH,srcmac,vmname)
    dstfile = "%s/%s/BServers/%s" % (BASE_SNAPSHOT_PATH,dstmac,vmname)
    if not os.path.exists(srcfile):
        print "No such source VM found"
        sys.exit(1)
    if not os.path.exists(os.path.dirname(dstfile)):
        os.makedirs(os.path.dirname(dstfile))
    os.rename(srcfile,dstfile)

elif sys.argv[1] == "hvnetinfo":
    mac = sys.argv[2]
    arptable = getArpTable()
    ip = arptable[mac]
    b = listBridges(ip)
    print "HV %s [%s] has the following networks available:" % (mac,ip)
    print "\t%s" % ','.join(b)
else:
    print "Args not recognised"
    usage()
