#!/usr/bin/env python3
from __future__ import print_function

import yaml
import json
import os
import re
import sys
import shutil
import subprocess


#remove the dict entries whose value is null or ''
def Util_rmnullindict(mydict):
    for key in mydict.keys():
        if isinstance(mydict[key],dict):
            Util_rmnullindict(mydict[key])
            if not mydict[key].keys():
                del mydict[key]
        else:
            if not mydict[key]:
                del mydict[key]


# get the dict value mydict[a][b][c] with key path a.b.c
def Util_getdictval(mydict,keystr):
    if not  isinstance(mydict,dict):
        return None
    dictkeyregex=re.compile("([^\.]+)\.?(\S+)*")
    result=re.findall(dictkeyregex,keystr)
    if result:
        (key,remdkey)=result[0]
        if key not in mydict.keys():
            return None
        if remdkey:
            return Util_getdictval(mydict[key],remdkey)
        else:
            return mydict[key]

# get the dict value mydict[a][b][c] with key path a.b.c
def Util_setdictval(mydict,keystr,value):
    dictkeyregex=re.compile("([^\.]+)\.?(\S+)*")
    result=re.findall(dictkeyregex,keystr)
    if result:
        (key,remdkey)=result[0]
        if remdkey:
            if key not in mydict.keys():
                mydict[key]={}
            Util_setdictval(mydict[key],remdkey,value)
        else:
            mydict[key]=value


def GetAttrInFile(fpath,objtype,objname,attrpath):
    f=open(fpath,'r')
    try:
        objdict=json.loads(f)
    except TypeError,ValueError:
        try:
            objdict=yaml.load(f)
        except Exception,e:
            raise Exception("Error: (\[.*?\]: )?cannot open file "+fpath+"! "+str(e))

    f.close()
    myattrpath=objtype+"."+objname+"."+attrpath
    myvalue=Util_getdictval(objdict,myattrpath)
    return myvalue

def compattr(exptattr,rawattr):
    if isinstance(exptattr,list) and re.match(r'^\[.*\]$',rawattr):
        rawattr=eval(rawattr)
    if isinstance(rawattr,str):
        if rawattr in exptattr:
            return 0
        else:
            return 1
    else:
        return cmp(exptattr,rawattr)



def runCommand(cmd, env=None):
    """ Run one command only, when you don't want to bother setting up
        the Popen stuff.
    """
    print('Running command: %s' %cmd)
    try:
        p = subprocess.Popen(cmd,
                             env=env,
                             shell=True,
                             stdout=subprocess.PIPE,
                             stderr=subprocess.PIPE
                            )
        out, err = p.communicate()
    except OSError,e:
        print('RunCommand failed due to OSError %s' % e)
    if p.returncode:
        print('Command %s failed with return code %d : %s' % (cmd , p.returncode, err))
    print(out)
    print(err)
    return p.returncode


def UpdateAttrInFile(fpath,fformat,objtype,objname,attrpath,value):
    f=open(fpath,'r')
    try:
        objdict=json.loads(f)
    except TypeError,ValueError:
        try:
            objdict=yaml.load(f)
        except Exception,e:
            raise Exception("Error: (\[.*?\]: )?cannot open file "+fpath+"! "+str(e))

    f.close()
    myattrpath=objtype+"."+objname+"."+attrpath
    if value == "":
        Util_setdictval(objdict,myattrpath,"")
        Util_rmnullindict(objdict)
    else:
        origobjdict=Util_getdictval(objdict,myattrpath)
        origattrtype=type(origobjdict)
        newvalue=value
        if origattrtype == list and re.match(r'^\[.*\]$',value):
            #import pdb
            #pdb.set_trace()
            newvalue=eval(value)
        if isinstance(origobjdict,list) and  isinstance(newvalue,str):
            newvalue=[newvalue]
        Util_setdictval(objdict,myattrpath,newvalue)
    f=open(fpath,'w')
    if fformat  == "yaml":
        print(yaml.dump(objdict,default_flow_style=False),file=f)
    elif fformat  == "json":
        print(json.dumps(objdict, sort_keys=True, indent=4, separators=(',', ': ')),file=f)
    f.close()

def removeobj(objtype,objname):
    print("removing existing \""+objtype+"\" type object \""+objname+"\" from xCAT\n")
    if str(objtype) in ('node','osimage','network','site','route','zone','policy'):
        runCommand("rmdef -t "+objtype+" -o "+objname)
    elif str(objtype) in ('passwd'):
        runCommand("tabch -d key="+objname+" passwd")

def Usage():
    print("Usage:\n")
    print(sys.argv[0]+" [OBJTYPE OBJNAME ATTRIBUTE ATTRVALUE TMPDIR]")

if len(sys.argv) < 6:
    Usage()
    exit(1)


objtype=str(sys.argv[1])
objname=str(sys.argv[2])
attribute=str(sys.argv[3])
attrvalue=str(sys.argv[4])
tmpdir=str(sys.argv[5])
print("objtype=%s\nobjname=%s\nattribute=%s\nattrvalue=%s\ntmpdir=%s\n"%(objtype,objname,attribute,attrvalue,tmpdir))

if os.path.exists(tmpdir):
    runCommand("rm -rf "+tmpdir)

os.mkdir(tmpdir)

print("Temporary directory to hold intermediate files: "+tmpdir+"\n")

curdir=os.path.split(os.path.realpath(sys.argv[0]))[0]
yamldeftmpl=curdir+"/templates/"+objtype+'.yaml'
jsondeftmpl=curdir+"/templates/"+objtype+'.json'
yamldef=tmpdir+"/"+objtype+'.yaml'
jsondef=tmpdir+"/"+objtype+'.json'
yamldefout=tmpdir+"/"+objtype+'.out.yaml'
jsondefout=tmpdir+"/"+objtype+'.out.json'

shutil.copyfile(yamldeftmpl,yamldef)
shutil.copyfile(jsondeftmpl,jsondef)

rcfinal=0
rcyaml=0
rcjson=0
rc=0

UpdateAttrInFile(yamldef,'yaml',objtype,objname,attribute,attrvalue)
print("=================the inventory file to import: "+yamldef+"=====================\n")
runCommand("cat "+yamldef)
print("===============================================================================\n")


print("import the inventory file "+yamldef+"\n")
rc=runCommand("xcat-inventory import -t "+objtype+" -o "+objname+" -f "+yamldef)
if rc !=0:
    print("failed to import the inventory file "+yamldef)
    rcyaml=1
else:
    print("the inventory file "+yamldef+" imported successfully\n")
    print("export the \""+objtype+"\" type object \""+objname+"\" just imported\n")
    rc=runCommand("xcat-inventory export -t "+objtype+" -o "+objname+" --format=yaml 1>"+yamldefout)
    if rc!=0:
        print("failed to export the \""+objtype+"\" type object \""+objname+"\"\n")
        rcyaml=1
    else:
        print("==============the exported inventory file "+yamldefout+"======================\n")
        runCommand("cat "+yamldefout)
        print("===========================================================================\n")
        myattrvalue=GetAttrInFile(yamldefout,objtype,objname,attribute)
        if attrvalue == '':
            if myattrvalue is not None:
                rcyaml=1
        else:
            if compattr(myattrvalue,attrvalue) != 0 :
                rcyaml=1

if rcyaml == 0:
    print("yaml validation passed\n")
else:
    print("yaml validation failed\n")

removeobj(objtype,objname)

UpdateAttrInFile(jsondef,'json',objtype,objname,attribute,attrvalue)
print("=================the inventory file to import: "+jsondef+"=====================\n")
runCommand("cat "+jsondef)
print("===============================================================================\n")


print("import the inventory file "+jsondef+"\n")
rc=runCommand("xcat-inventory import -t "+objtype+" -o "+objname+" -f "+jsondef)
print("rc="+str(rc))
if rc !=0:
    print("failed to import the inventory file "+jsondef)
    rcjson=1
else:
    print("the inventory file "+jsondef+" imported successfully\n")
    print("export the \""+objtype+"\" type object \""+objname+"\" just imported\n")
    rc=runCommand("xcat-inventory export -t "+objtype+" -o "+objname+" --format=json 1>"+jsondefout)
    if rc!=0:
        print("failed to export the \""+objtype+"\" type object \""+objname+"\"\n")
        rcjson=1
    else:
        print("==============the exported inventory file "+jsondefout+"======================\n")
        runCommand("cat "+jsondefout)
        print("===========================================================================\n")
        myattrvalue=GetAttrInFile(jsondefout,objtype,objname,attribute)
        if attrvalue == '':
            if myattrvalue is not None:
                rcjson=1
        else:
            if compattr(myattrvalue,attrvalue) != 0 :
                rcjson=1

if rcjson ==0:
    print("json validation passed\n")
else:
    print("json validation failed\n")

removeobj(objtype,objname)

if rcjson !=0 or rcyaml!=0:
   rcfinal=1

print("remove intermediate directory "+tmpdir)
if os.path.exists(tmpdir):
    runCommand("rm -rf "+tmpdir)

exit(rcfinal)


