#===============================================================#
#                                                               #
# ID                                                            #
#                                                               #
# dataset.py                                                    #
#                                                               #
# Copyright (c) 2011 NetApp, Inc. All rights reserved.          #
# Specifications subject to change without notice.              #
#                                                               #
#  Sample code to demonstrate how to:                           #
#        - list/create/delete a dataset                         #
#        - list/add/delete a member in a dataset                #
#        - attach resourcepools, provisioning policy,           #
#          protection policy and multistore to a dataset        #
#        - provision storage from a dataset                     #
#                                                               #
# This Sample code is supported from DataFabric Manager 3.8     #
# onwards.                                                      #
# However few of the functionalities of the sample code may     #
# work on older versions of DataFabric Manager.                 #
#===============================================================#
import sys
sys.path.append("../../../../../../../lib/python/NetApp")
from NaServer import *

def print_usage() :
    print (" Usage:\n")
    print (" dataset.py <dfmserver> <user> <passwd> list [<name>]\n")
    print (" dataset.py <dfmserver> <user> <passwd> create <name> [<vfiler> <prov_pol> <prot-pol>]\n")
    print (" dataset.py <dfmserver> <user> <passwd> destroy <name>\n")
    print (" dataset.py <dfmserver> <user> <passwd> update <name> <prov_pol> <prot_pol> <pri_rp> <sec_rp> [<ter_rp>]\n")
    print (" dataset.py <dfmserver> <user> <passwd> member list <name>\n")
    print (" dataset.py <dfmserver> <user> <passwd> member add <name> <mem_add>\n")
    print (" dataset.py <dfmserver> <user> <passwd> member del <name> <mem_del>\n")
    print (" dataset.py <dfmserver> <user> <passwd> provision <name> <mem_prov_name> <size> [<snap-size>]\n")
    print (" <dfmserver>     -- Name/IP Address of the DFM server\n")
    print (" <user>          -- DFM server User name\n")
    print (" <passwd>        -- DFM server User Password\n")
    print (" <name>          -- Name of the dataset\n")
    print (" <vfiler>        -- Attach newly provisioned member to this vfiler\n")
    print (" <prov_pol>      -- name or id of an exisitng nas provisioning policy\n")
    print (" <prot-pol>      -- name or id of an exisitng protection policy\n")
    print (" <mem_prov_name> -- member name to be provisioned\n")
    print (" <size>          -- size of the new member to be provisioned in bytes\n")
    print (" <snap-size>     -- maximum size in bytes allocated to snapshots in SAN envs\n")
    print (" <mem_add>       -- member to be added\n")
    print (" <mem_del>       -- member to be removed\n")
    print (" <pri_rp>        -- Primary resource pool\n")
    print (" <sec_rp>        -- Secondary resource pool\n")
    print (" <ter_rp>        -- Tertiary resource pool\n")
    print (" If the protection policy is 'Mirror', specify only pri_rp and sec_rp.\n")
    print (" If protection policy is 'Back up, then Mirror', specify pri_rp, sec_rp and ter_rp\n")
    sys.exit(2)

# check for valid number of parameters
cmd_args = len(sys.argv) - 1

if ( cmd_args < 4 ) :
    print_usage()

# Variables declaration
dfmserver = sys.argv[1]
dfmuser   = sys.argv[2]
dfmpw     = sys.argv[3]
command   = sys.argv[4]

# Setup DFM server connection
s = NaServer( dfmserver, 1, 0 )
s.set_style("LOGIN")
s.set_transport_type("HTTP")
s.set_server_type("DFM")
s.set_port(8088)
s.set_admin_user( dfmuser, dfmpw )

if ( command == "list" ) :

    if(cmd_args > 4):
        dsName = sys.argv[5]
    else :
        dsName = None

    out = s.invoke( "dataset-list-info-iter-start", "object-name-or-id", dsName )
    
    if ( out.results_status() == "failed" ) :
        print( "Error : " + out.results_reason() + "\n" )
        sys.exit(2)
    
    input = NaElement("dataset-list-info-iter-next")
    input.child_add_string( "maximum", out.child_get_int("records") )
    input.child_add_string( "tag",     out.child_get_string("tag") )
    out = s.invoke_elem(input)
    
    if ( out.results_status() == "failed" ) :
        print( "Error : " + out.results_reason() + "\n" )
        sys.exit(2)
    
    print ("\nDATASETS:\n")
    print ("===================================================================\n")

    if ( out.child_get_int("records") == 0 ) :
        print ("Error: No Datasets!\n")
        sys.exit(2)
    
    rps     = out.child_get("datasets")
    rpInfos = rps.children_get()

    if(rpInfos != None):
        for rpi in rpInfos :
                print ("Dataset Name\t: " + rpi.child_get_string("dataset-name"))
                dsstatus = rpi.child_get("dataset-status")
                print ("Overall Status\t: " + dsstatus.child_get_string("resource-status"))
                print ("# of Members\t: " + rpi.child_get_string("member-count"))
                value = "-Not Configured-"

                if ( rpi.child_get_string("vfiler-name") != None ) :
                    value = rpi.child_get_string("vfiler-name")
        
                print ("VFiler unit\t: " + value )
                value = "-Not Configured-"
        
                if ( rpi.child_get_string("protection-policy-name") != None ) :
                    value = rpi.child_get_string("protection-policy-name")
        
                print ("Prot. Policy\t: " + value)
                value = "-Not Configured-"
        
                if ( rpi.child_get_string("provisioning-policy-name") != None ) :
                    value = rpi.child_get_string("provisioning-policy-name")
        
                print ("Prov. Policy\t: " + value)
                print ("Res. pools(Pri)\t: " )
                rps = rpi.child_get("resourcepools")
    
                if ( rps == None ) :
                    print ("No attached Resourcepool!\n")
        
                else :
                    dsrpi = rps.children_get()
                    for rp in dsrpi:
                        print (rp.child_get_string("resourcepool-name"))
                        print (" ")
            
                print ("\n")
                print ("===================================================================\n")
    
elif ( command == "create" ) :
    
        if ( cmd_args < 5 ) :
            print_usage()
    
        dsName = sys.argv[5]
    
        if(cmd_args > 7):
            vfiler = sys.argv[6]
            prov   = sys.argv[7]
            prot   = sys.argv[8]
        else :
            vfiler = None
            prov = None
            prot = None    
        input = NaElement("dataset-create")
        input.child_add_string( "dataset-name", dsName )
    
        if ( prot != None ) :
            input.child_add_string( "protection-policy-name-or-id", prot )
    
        if ( prov != None ) :
            input.child_add_string( "provisioning-policy-name-or-id", prov )
    
        if ( vfiler != None ) :
            input.child_add_string( "vfiler-name-or-id", vfiler )
    
        out = s.invoke_elem(input)
    
        if ( out.results_status() == "failed" ) :
            print( "Error : " + out.results_reason() + "\n" )
            sys.exit(2)
    
        print ("Dataset " + dsName  + " created with ID " + str(out.child_get_int("dataset-id")) + "!\n")

elif ( command == "destroy" ) :
    if ( cmd_args < 5 ) :
        print ("Usage: dataset.py <dfmserver> <user> <password> destroy <dataset_name>\n")
    
    dsName = sys.argv[5]
    out = s.invoke( "dataset-destroy", "dataset-name-or-id", dsName )
    
    if ( out.results_status() == "failed" ) :
        print( "Error : " + out.results_reason() + "\n" )
        sys.exit(2)
    
    print ("Dataset " + dsName + " destroyed!\n")

elif ( command == "update" ) :
    if ( cmd_args < 9 ) :
        print_usage()
    
    dsName = sys.argv[5]
    provp  = sys.argv[6]
    protp  = sys.argv[7]
    priRp  = sys.argv[8]
    secRp  = sys.argv[9]
    
    if(args > 9):
        terRp  = sys.argv[10]
    
    out = s.invoke( "dataset-edit-begin", "dataset-name-or-id", dsName )
    
    if ( out.results_status() == "failed" ) :
        print( "Error : " + out.results_reason() + "\n" )
        sys.exit(2)
    
    editLock = out.child_get_int("edit-lock-id")
    print ("Adding protection policy...\n")
    out = s.invoke( "dataset-modify", "edit-lock-id", editLock, "protection-policy-name-or-id", protp )
    
    if ( out.results_status() == "failed" ) :
        print( "Error : " + out.results_reason() + "\n" )
        print("Attempting to roll-back...\n")
        out = s.invoke( "dataset-edit-rollback", "edit-lock-id", editLock )
        sys.exit(2)
    
    print ("Adding provisioning policy...\n")
    out = s.invoke( "dataset-modify-node", "edit-lock-id", editLock, "provisioning-policy-name-or-id", provp )
    
    if ( out.results_status() == "failed" ) :
        print( "Error : " + out.results_reason() + "\n" )
        print("Attempting to roll-back...\n")
        out = s.invoke( "dataset-edit-rollback", "edit-lock-id", editLock )
        sys.exit(2)
    
    print ("Gathering Node names from protection policy...\n")
    out = s.invoke( "dp-policy-list-iter-start", "dp-policy-name-or-id", protp )
    
    if ( out.results_status() == "failed" ) :
        print( "Error : " + out.results_reason() + "\n" )
        print("Attempting to roll-back...\n")
        out = s.invoke( "dataset-edit-rollback", "edit-lock-id", editLock )
        sys.exit(2)
    
    out = s.invoke(    "dp-policy-list-iter-next", "maximum", out.child_get_string("records"), "tag", out.child_get_string("tag"))
    
    if ( out.results_status() == "failed" ) :
        print( "Error : " + out.results_reason() + "\n" )
        print("Attempting to roll-back...\n")
        out = s.invoke( "dataset-edit-rollback", "edit-lock-id", editLock )
        sys.exit(2)
    
    dps = out.child_get("dp-policy-infos")
    
    if ( dps == None ) :
        print ("Error: No Provisioning Policies!\n")
        print("Attempting to roll-back...\n")
        out = s.invoke( "dataset-edit-rollback", "edit-lock-id", editLock )
        sys.exit(2)
    
    dpInfo     = dps.child_get("dp-policy-info")
    dpContent  = dpInfo.child_get("dp-policy-content")
    dpNodes    = dpContent.child_get("dp-policy-nodes")
    dpNodeInfo = dpNodes.children_get()
    count = 1
    rpool = priRp
    size  = len(dpNodeInfo)

    if ( size != ( cmd_args - 7 ) ) :
        print("Error: Missing resource pool! No of resource pools required are : size \n")
        print("Attempting to roll-back...\n")
        out = s.invoke( "dataset-edit-rollback", "edit-lock-id", editLock )
        sys.exit(2)
    
    for dpni in dpNodeInfo :
        if ( count == 2 ) :
            rpool = secRp 
            
        if ( count == 3 ) :
            rpool = terRp 

        dpNode = dpni.child_get_string("name")
        print ("Adding Resourcepool "  + rpool  + " to DP Node Name "  + dpNode + "\n")
        out = s.invoke( "dataset-add-resourcepool", "edit-lock-id", editLock, "dp-node-name", dpNode, "resourcepool-name-or-id", rpool )
    
        if ( out.results_status() == "failed" ) :
            print( "Error : " + out.results_reason() + "\n" )
            print("Attempting to roll-back...\n")
            out = s.invoke( "dataset-edit-rollback", "edit-lock-id", editLock )
            sys.exit(2)
        
        count = count + 1
    
    print ("Committing... \n")
    out = s.invoke( "dataset-edit-commit", "edit-lock-id", editLock )
    
    if ( out.results_status() == "failed" ) :
        print( "Error : " + out.results_reason() + "\n" )
        print("Attempting to roll-back...\n")
        out = s.invoke( "dataset-edit-rollback", "edit-lock-id", editLock )
        sys.exit(2)
    
elif ( command == "member" ) :
    if(cmd_args < 5):
        print_usage()
        
    subCommand = sys.argv[5]
    
    if ( subCommand == "list" ) :
        
        if ( cmd_args < 6 ) :
            print_usage()
        
        dsName = sys.argv[6]

        out = s.invoke("dataset-member-list-info-iter-start", "include-exports-info", "true", 
                        "include-indirect",    "true", "include-space-info", "true", "dataset-name-or-id",    dsName )
        
        if ( out.results_status() == "failed" ) :
            print( "Error : " + out.results_reason() + "\n" )
            sys.exit(2)
        
        input = NaElement("dataset-member-list-info-iter-next")
        input.child_add_string( "maximum", out.child_get_int("records") )
        input.child_add_string( "tag",     out.child_get_string("tag") )
        out = s.invoke_elem(input)
        
        if ( out.results_status() == "failed" ) :
            print( "Error : " + out.results_reason() + "\n" )
            sys.exit(2)
        
        print ("\nDATASET : " + dsName + "\n")
        print ("===================================================================\n")
        
        if ( out.child_get_int("records") == 0 ) :
            print ("Error: No Members input this Dataset!\n")
            sys.exit (2)
        
        dms  = out.child_get("dataset-members")
        dmis = dms.children_get()
         
        if(dmis != None):
            for dmi in dmis :
                        member_name = dmi.child_get_string("member-name")

            # Display all member details.  Avoid displaying the non-qtree member i.e members ending with "-"
            
                        if(member_name[-1:1] != "-"):
                            print ("Member Name\t\t: " + member_name)
                            print ("\n")
                            print ("Member Status\t\t: " + dmi.child_get_string("member-status"))
                            print ("\n")
                            print ("DP node name\t\t: "  + dmi.child_get_string("dp-node-name"))
                            print ("\n")
                            mtype = dmi.child_get_string("member-type")
                            print ("Member Type\t\t: " + mtype)
                            print ("\n")

                            if ( mtype != "qtree" ) :
                                spinfo = dmi.child_get("space-info")
                                print ("Space used\t\t: " + ( str(spinfo.child_get_int("used-space")) ) + " ("  +
                                    (str(spinfo.child_get_int("used-space") / ( 1024 * 1024 )))  + "MB)\n")
                                print ("Space(Avail/Total)\t: " + ( str(spinfo.child_get_int("available-space") / ( 1024 * 1024 )))
                                    + "MB / " + (str(spinfo.child_get_int("total-space") / ( 1024 * 1024 ))) + "MB")
                                print ("\n")
                
                            print ("\n")
                            print ("===================================================================\n")
            
    elif ( subCommand == "add" ) :
        if ( cmd_args < 7 ) :
            print_usage()
        
        dsName = sys.argv[6]
        mem    = sys.argv[7]
        out = s.invoke( "dataset-edit-begin", "dataset-name-or-id", dsName )
    
        if ( out.results_status() == "failed" ) :
            print( "Error : " + out.results_reason() + "\n" )
            sys.exit(2)
    
        editLock = out.child_get_int("edit-lock-id")
        input = NaElement("dataset-add-member")
        input.child_add_string( "edit-lock-id", editLock )
        dmps = NaElement("dataset-member-parameters")
        
        i = 7
        while ( i <= cmd_args ) :
            print ("Adding member " + mem + "...\n")
            dmp = NaElement("dataset-member-parameter")
            dmp.child_add_string( "object-name-or-id", mem )
            mem = sys.argv[i]
            i = i +1
            dmps.child_add(dmp)
    
        input.child_add(dmps)
        out = s.invoke_elem(input)
    
        if ( out.results_status() == "failed" ) :
            print( "Error : " + out.results_reason() + "\n" )
            print("Attempting to roll-back...\n")
            out = s.invoke( "dataset-edit-rollback", "edit-lock-id", editLock )
            sys.exit(2)
    
        print ("Committing... \n")
        out = s.invoke( "dataset-edit-commit", "edit-lock-id", editLock )
    
        if ( out.results_status() == "failed" ) :
            print( "Error : " + out.results_reason() + "\n" )
            print("Attempting to roll-back...\n")
            out = s.invoke( "dataset-edit-rollback", "edit-lock-id", editLock )
            sys.exit(2)
        
        print ("Addition of Members to Dataset " + dsName + " Successful!\n")
    
    elif ( subCommand == "del" ) :
        if ( cmd_args < 7 ) :
            print_usage()
        
        dsName = sys.argv[6]
        mem    = sys.argv[7]
        out = s.invoke( "dataset-edit-begin", "dataset-name-or-id", dsName )
        
        if ( out.results_status() == "failed" ) :
            print( "Error : " + out.results_reason() + "\n" )
            sys.exit(2)
        
        editLock = out.child_get_int("edit-lock-id")
        input = NaElement("dataset-remove-member")
        input.child_add_string( "edit-lock-id", editLock )
        dmps = NaElement("dataset-member-parameters")
        i = 7
        
        while ( i <= cmd_args) :
            print ("Removing member " + mem + "...\n")
            dmp = NaElement("dataset-member-parameter")
            dmp.child_add_string( "object-name-or-id", mem )
            mem = sys.argv[i]
            i = i+1
            dmps.child_add(dmp)
        
        input.child_add(dmps)
        out = s.invoke_elem(input)
        
        if ( out.results_status() == "failed" ) :
            print( "Error : " + out.results_reason() + "\n" )
            print("Attempting to roll-back...\n")
            out = s.invoke( "dataset-edit-rollback", "edit-lock-id", editLock )
            sys.exit(2)
        
        print ("Committing... \n")
        out = s.invoke( "dataset-edit-commit", "edit-lock-id", editLock )
        
        if ( out.results_status() == "failed" ) :
            print( "Error : " + out.results_reason() + "\n" )
            print("Attempting to roll-back...\n")
            out = s.invoke( "dataset-edit-rollback", "edit-lock-id", editLock )
            sys.exit(2)
        
        print ("Removal of Members from Dataset " + dsName + " Successful!\n")
    
    else :
        print ("Invalid Option selected...\n")
        print_usage()
    
elif ( command == "provision" ) :
    if ( cmd_args < 7 ) :
        print_usage()
    
    dsName  = sys.argv[5]
    name    = sys.argv[6]
    size    = sys.argv[7]
    
    if(cmd_args > 7):
        ssspace = sys.argv[8]

    #Determine the provisioning policy attached to the dataset
    out = s.invoke( "dataset-list-info-iter-start", "object-name-or-id", dsName )
    
    if ( out.results_status() == "failed" ) :
        print( "Error : " + out.results_reason() + "\n" )
        sys.exit(2)
    
    input = NaElement("dataset-list-info-iter-next")
    input.child_add_string( "maximum", out.child_get_int("records") )
    input.child_add_string( "tag",     out.child_get_string("tag") )
    out = s.invoke_elem(input)
    
    if ( out.results_status() == "failed" ) :
        print( "Error : " + out.results_reason() + "\n" )
        sys.exit(2)
    
    rps = out.child_get("datasets")

    if ( rps == None ) :
        print ("Error: No Datasets!\n")
        sys.exit(2)
    
    rpInfos = rps.child_get("dataset-info")
    provpId = rpInfos.child_get_string("provisioning-policy-id")
    print ("Prov Policy\t: " + rpInfos.child_get_string("provisioning-policy-name") + "\n")
    input = NaElement("provisioning-policy-list-iter-start")
    input.child_add_string( "provisioning-policy-name-or-id", provpId )
    out = s.invoke_elem(input)
    
    if ( out.results_status() == "failed" ) :
        print( "Error : " + out.results_reason() + "\n" )
        sys.exit(2)
    
    input = NaElement("provisioning-policy-list-iter-next")
    input.child_add_string( "maximum", out.child_get_int("records") )
    input.child_add_string( "tag",     out.child_get_string("tag") )
    out = s.invoke_elem(input)
    
    if ( out.results_status() == "failed" ) :
        print( "Error : " + out.results_reason() + "\n" )
        sys.exit(2)
    
    pps = out.child_get("provisioning-policies")
    
    if ( pps == "" ) :
        print ("Error: No Provisioning Policies!\n")
        sys.exit(2)
    
    ppInfos = pps.child_get("provisioning-policy-info")
    pptype  = ppInfos.child_get_string("provisioning-policy-type")
    out = s.invoke( "dataset-edit-begin", "dataset-name-or-id", dsName )
    
    if ( out.results_status() == "failed" ) :
        print( "Error : " + out.results_reason() + "\n" )
        sys.exit(2)
    
    editLock = out.child_get_int("edit-lock-id")
    input = NaElement("dataset-provision-member")
    input.child_add_string( "edit-lock-id", editLock )
    pmri = NaElement("provision-member-request-info")
    pmri.child_add_string( "name", name )
    pmri.child_add_string( "size", size )

    if ( pptype == "san" ) :
        pmri.child_add_string( "maximum-snapshot-space", ssspace )
    
    input.child_add(pmri)
    print ("Provisioning storage...\n")
    out = s.invoke_elem(input)
    
    if ( out.results_status() == "failed" ) :
        print( "Error : " + out.results_reason() + "\n" )
        sys.exit(2)
    
    print ("Committing... \n")
    out = s.invoke( "dataset-edit-commit", "edit-lock-id", editLock )
    
    if ( out.results_status() == "failed" ) :
        print( "Error : " + out.results_reason() + "\n" )
        print("Attempting to roll-back...\n")
        out = s.invoke( "dataset-edit-rollback", "edit-lock-id", editLock )
        sys.exit(2)
    
    jobId = ( ( out.child_get("job-ids") ).child_get("job-info") ).child_get_string("job-id")
    print ("Job ID\t\t: " + jobId + " \n")
    jobStatus = "running"
    print ("Job Status\t: " + jobStatus)
    
    while ( jobStatus == "queued" or jobStatus == "running" ) :
        out = s.invoke( "dp-job-list-iter-start", "job-id", jobId )
        
        if ( out.results_status() == "failed" ) :
            print( "Error : " + out.results_reason() + "\n" )
            sys.exit(2)
        
        out = s.invoke("dp-job-list-iter-next", "maximum", out.child_get_string("records"), "tag", out.child_get_string("tag"))
        
        if ( out.results_status() == "failed" ) :
            print( "Error : " + out.results_reason() + "\n" )
            sys.exit(2)
        
        #print out.sprintf()
        dpJobs = out.child_get("jobs")
        dpJobInfo = dpJobs.child_get("dp-job-info")
        jobStatus = dpJobInfo.child_get_string("job-state")
        time.sleep (5)
        print (".")
        
        if ( jobStatus == "completed" or jobStatus == "aborted" ) :
            print ("\nOverall Status\t: " + dpJobInfo.child_get_string("job-overall-status") + "\n")
    
    out = s.invoke( "dp-job-progress-event-list-iter-start", "job-id", jobId )
    
    if ( out.results_status() == "failed" ) :
        print( "Error : " + out.results_reason() + "\n" )
        sys.exit(2)
    
    out = s.invoke("dp-job-progress-event-list-iter-next", "tag", out.child_get_string("tag"), "maximum", out.child_get_string("records"))

    if ( out.results_status() == "failed" ) :
        print( "Error : " + out.results_reason() + "\n" )
        sys.exit(2)
    
    progEvnts     = out.child_get("progress-events")
    progEvntsInfo = progEvnts.children_get()
    print ("\nProvision Details:\n")
    print ("=" * 19 + "\n")
    
    for evnt in progEvntsInfo :
    
        if ( evnt.child_get_string("event-type") != "" ) :
            print (evnt.child_get_string("event-type"))
        
        print ("\t: " + evnt.child_get_string("event-message") + "\n")

else :
    print ("Invalid Option...\n")
    print_usage()


