#===============================================================#
#                                                               #
# ID                                                            #
#                                                               #
# dataset_health.py                                             #
#                                                               #
# Copyright (c) 2011 NetApp, Inc. All rights reserved.          #
# Specifications subject to change without notice.              #
#                                                               #
# Sample code for providing health status of all the datasets   #
# in the system. This is provided using a dashboard which       #
# provides information about total protected and                #
# unprotected datasets, dataset protection status,              #
# space status, conformance status, resource status,  etc.      #
#                                                               #
# 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 *

##### SUBROUTINE SECTION
def usage() :
    print ("Usage:\n")
    print ("python dataset_health.py <server> <user> <password> -d | -p | -D | <-l [<dataset-name>]>\n")
    print ("<dfm-server>    --  Name/IP Address of the DFM Server\n")
    print ("<user>          --  DFM Server User name\n")
    print ("<password>      --  DFM Server Password\n")
    print ("<dataset-name>  --  Name of the dataset\n")
    print ("-d              --  Display the dataset health dashboard\n")
    print ("-l              --  List all datasets and its status information\n")
    print ("-p              --  List protected and Unprotected datasets\n")
    print ("-D              --  List DR configured datasets\n")
    sys.exit(1)


def convert_seconds(secs):
    parts = time.gmtime(secs)
    output = ("%4d Days %2d Hr %2d Min %2d Sec"  %(parts[7],parts[2],parts[1],parts[0]))
    return output
     
     
def list_datasets(option, dataset) :
    # create the input API request
    input = NaElement("dataset-list-info-iter-start")
    
    if (dataset) :
        input.child_add_string( "object-name-or-id", dataset )
    
    # check for the given options
    # For listing DR Configured datasets, add is-dr-capable option to TRUE
    if ( option == "DRConfigured" ) :
        input.child_add_string( "is-dr-capable", "True" )
    
    # invoke the API request and capture the output
    output = server.invoke_elem(input)

    # check the API status
    if(output.results_status() == "failed" ):
        print( "Error : " + output.results_reason() + "\n" ) 
        sys.exit(2)
        
    #Extract the record and tag values
    records = output.child_get_string("records")
    ds_total = records
    tag = output.child_get_string("tag")

    # now invoke the dataset-list-info-iter-next to return list of datasets
    output = server.invoke( "dataset-list-info-iter-next", "maximum", records, "tag", tag )

    # check for the API status
    if(output.results_status() == "failed" ):
        print( "Error : " + output.results_reason() + "\n" )
        sys.exit(2)
    
    # get the list of datasets which is contained under datasets element
    dataset_info = output.child_get("datasets")
    result = dataset_info.children_get()
    prot_status     = None
    conf_status     = None
    resource_status = None
    is_dr_capable   = None
    is_protected    = None
    dr_state        = None

    print ("\n")
    print ("-" * 70)
    print ("\n")

    # Iterate through each dataset record
    for dataset in result:
        dataset_name = dataset.child_get_string("dataset-name")
        
        # printing detials if only one dataset is selected for listing
        status = dataset.child_get("dataset-status")

        # check for the input option and retrieve appropriate information
        if ( option == "ProtectedUnprotected" ) :
            print("Dataset name               : " + dataset_name)
            is_protected = dataset.child_get_string("is-protected")
            
            if(re.match(r'True',is_protected,re.I)):
                print ("Protected                  : Yes \n")
            
            else :
                print ("Protected                  : No \n")
            
            print ("-" * 70)
            print ("\n")
        
        elif ( option == "DRconfigured" ) :
            status = dataset.child_get("dataset-status")
            is_dr_capable = dataset.child_get_string("is-dr-capable")
            
            if(re.match(r'True',is_dr_capable,re.I)):
                dr_state = dataset.child_get_string("dr-state")
                dr_prot_status = status.child_get_string("dr-status")
                print ("Dataset name               : " + dataset_name + "\n")
                print ("DR State-Status            : " + dr_state + " - " + dr_prot_status + "\n")
                print ("-" * 70)
                print ("\n")
        
        elif ( option == "All" ) :
            print ("Dataset name               : " + dataset_name + "\n")
            is_protected = dataset.child_get_string("is-protected")
            
            if(re.match(r'True',is_protected,re.I)):
                status = dataset.child_get("dataset-status")
                prot_status = status.child_get_string("protection-status")
                
                if (prot_status) :
                    print ("Protection status          : " + prot_status + "\n")
                
                else :
                    print ("Protection status          : unknown \n")
                    
            else :
                print ("Protection status          : No data protection policy applied.\n")
            
            conf_status = status.child_get_string("conformance-status")
            
            if (conf_status) :
                print ("Conformance status         : " + conf_status + "\n")
            
            else :
                print ("Conformance status         : Unknown \n")
            
            resource_status = status.child_get_string("resource-status")

            if (resource_status) :
                print ("Resource status            : " + resource_status + "\n")
            
            else :
                print ("Resource    status         : Unknown \n")
            
            space_status = status.child_get_string("space-status")
            
            if (space_status) :
                print ("Space status               : " + space_status + "\n")
            
            else :
                print ("Space status               : Unknown \n")
            
            is_dr_capable = dataset.child_get_string("is-dr-capable")
            
            if(re.match(r'True',is_dr_capable,re.I)):
                dr_state = dataset.child_get_string("dr-state")
                dr_prot_status = status.child_get_string("dr-status")
                print ("DR State-Status            : " + dr_state + " - " + dr_prot_status + "\n")
            
            else:
                print ("DR State-Status            : No data protection policy applied.\n")
            
            print ("\n")
            print ("-" * 70)
            print ("\n")

    #finally invoke the dataset-list-info-iter-end API
    end = server.invoke( "dataset-list-info-iter-end", "tag", tag )
    
    if(end.results_status() == "failed" ):
        print( "Error : " + end.results_reason() + "\n" )
        sys.exit(2)
    

def list_dashboard():
    # create the input dataset-list-info-iter-start API request for listing
    # through all datasets
    global ps_uninitialized,ds_conformant,rs_normal,ss_normal,ss_unknown,ps_baseline_failure
    global ps_lag_error,ps_lag_warning,ps_suspended,ps_protected,ds_non_conformant,rs_emergency
    global rs_critical,rs_error,rs_warning,ss_error,ss_warning
    input = NaElement("dataset-list-info-iter-start")

    # invoke the api and capture the ouput
    output = server.invoke_elem(input)

    # check the status of the API request
    if(output.results_status() == "failed" ):
        print( "Error : " + output.results_reason() + "\n" )
        sys.exit(2)
    
    # Extract the record and tag values
    records = output.child_get_string("records")
    ds_total = records
    tag = output.child_get_string("tag")

    output = server.invoke( "dataset-list-info-iter-next", "maximum", records, "tag", tag )
    
    if(output.results_status() == "failed" ):
        print( "Error : " + output.results_reason() + "\n" )
        sys.exit(2)
        
    # get the list of datasets which is contained under datasets element
    dataset_info = output.child_get("datasets")
    result = dataset_info.children_get()
    prot_status     = None
    conf_status     = None
    resource_status = None

    # Iterate through each dataset record
    for dataset in result:
        dataset_name = dataset.child_get_string("dataset-name")
        status = dataset.child_get("dataset-status")

        # get the protection status information
        prot_status = status.child_get_string("protection-status")
        
        if ( prot_status == "protected" ) :
            ps_protected = ps_protected + 1
        
        elif ( prot_status == "uninitialized" ) :
            conf_status = status.child_get_string("conformance-status")
            
            if ( conf_status != "conforming" ) :
                ps_uninitialized = ps_uninitialized + 1
            
        elif ( prot_status == "protection_suspended" ) :
            ps_suspended = ps_suspended + 1
            
        elif ( prot_status == "lag_warning" ) :
            ps_lag_warning = ps_lag_warning + 1
            
        elif ( prot_status == "lag_error" ) :
            ps_lag_error = ps_lag_error + 1
        
        elif ( prot_status == "baseline_failure" ) :
            ps_baseline_failure = ps_baseline_failure + 1
        
        # get the conformance status information
        conf_status = status.child_get_string("conformance-status")
        
        if ( conf_status == "conformant" ) :
            ds_conformant = ds_conformant + 1
        
        elif ( conf_status == "nonconformant" ) :
            ds_non_conformant = ds_non_conformant + 1
        
        # get the resource status information
        resource_status = status.child_get_string("resource-status")
        
        if ( resource_status == "emergency" ) :
            rs_emergency = rs_emergency + 1
        
        elif ( resource_status == "critical" ) :
            rs_critical = rs_critical + 1
            
        elif ( resource_status == "error" ) :
            rs_error = rs_error + 1
            
        elif ( resource_status == "warning" ) :
            rs_warning = rs_warning + 1
            
        elif ( resource_status == "normal" ) :
            rs_normal = rs_normal + 1
            
        # get the space status information
        space_status = status.child_get_string("space-status")
        if ( space_status == "error" ) :
            ss_error = ss_error + 1
        
        elif ( space_status == "warning" ) :
            ss_warning = ss_warning + 1
        
        elif ( space_status == "ok" ) :
            ss_normal = ss_normal + 1
            
        elif ( space_status == "unknown" ) :
            ss_unknown = ss_unknown + 1
        
    # invoke the iter-end zapi
    end = server.invoke( "dataset-list-info-iter-end", "tag", tag )
    
    if(end.results_status() == "failed" ):
        print( "Error : " + end.results_reason() + "\n" )
        sys.exit(2)
    
    # invoke the dp-dashboard-get-protected-data-counts API request to get
    # the protected and unprotected dataset counts
    output = server.invoke("dp-dashboard-get-protected-data-counts")
    
    if(output.results_status() == "failed" ):
        print( "Error : " + output.results_reason() + "\n" )
        sys.exit(2)
    
    ds_total_protected = output.child_get_string("protected-dataset-count")
    ds_total_unprotected = output.child_get_string("unprotected-dataset-count")

    # invoke the dp-dashboard-get-dr-dataset-counts API request to get
    # the DR configured datasets and its state and status information
    output = server.invoke("dp-dashboard-get-dr-dataset-counts")
    
    if(output.results_status() == "failed" ):
        print( "Error : " + output.results_reason() + "\n" )
        sys.exit(2)
    
    dr_state_status_counts = output.child_get("dr-state-status-counts")
    dr_counts_results      = dr_state_status_counts.children_get()
    dr_count               = 0
    dr_state               = ""
    state_status           = ""
    dr_status              = ""
    dr_hash                = {}    
    # iterate through each DR dataset
    for dr_state_status_count in dr_counts_results:
        count = dr_state_status_count.child_get_string("count")
        dr_state  = dr_state_status_count.child_get_string("dr-state")
        dr_status = dr_state_status_count.child_get_string("dr-status")
        
        if ( dr_status == "warning" ) :
            dr_status = "warnings"
        
        state_status = dr_state + " - " + dr_status
        dr_hash[state_status] = count
        dr_count = dr_count + count
    
    print ("\n\n  Datasets")
    print (" |" + "-" * 60 + "-|")
    print (" | %-60s|" %(("Protected            : ") + str(ds_total_protected)))
    print (" | %-60s|" %(("Unprotected          : ") + str(ds_total_unprotected)))
    print (" |" + "-" * 60 + "-|\n")
    print ("              Total datasets : " + ds_total + "\n\n\n")
    print ("  Dataset protection status \n")
    print (" |" + "-" * 60 + "-|")
    print (" | %-60s|" %(("Baseline Failure          : ") + str(ps_baseline_failure)))
    print (" | %-60s|" %(("Lag Error                 : ") + str(ps_lag_error))) 
    print (" | %-60s|" %(("Lag Warning               : ") + str(ps_lag_warning)))
    print (" | %-60s|" %(("Protection Suspended      : ") + str(ps_suspended))) 
    print (" | %-60s|" %(("Uninitialized             : ") + str(ps_uninitialized)))
    print (" | %-60s|" %(("Protected                 : ") + str(ps_protected))) 
    print (" |" + "-" * 60 + "-|")
    print ("\n\n\n")
    print ("  Dataset Lags ")
    print (" |" + "-" * 60 + "-|")
    output2 = server.invoke("dp-dashboard-get-lagged-datasets")
    
    if ( output.results_status() == "failed" ):
        print( "Error : " + output.results_reason() + "\n" )
        sys.exit(2)

    dataset_lags = output2.child_get("dp-datasets").children_get()
    count        = 0
    
    for dataset_lag in dataset_lags :
        name      = dataset_lag.child_get_string("dataset-name")
        worst_lag = dataset_lag.child_get_string("worst-lag")
        time      = convert_seconds(worst_lag)
        print(" | %-20s  %-38s|" %( name, time ) )
        
        count = count + 1
        if ( count >= 5 ) :
            break
        
    if ( count == 0 ) :
        print (" | %-60s|" %( "No data available" ) )
    
    print (" |" + "-" * 60 + "-|")
    print ("\n\n\n")
    print ("  Failover readiness \n")
    print (" |" + "-" * 60 + "-|")

    if ( dr_count != 0 ) :
    
        for key in dr_hash.keys() :
            value = dr_hash[key]
            print (" | %-60s|" %((key)+("            : ")+value))
    
    else :
        print(" | %-60s|" %("Normal" ) )
    
    print (" |" + "-" * 60 + "-|")
    print ("   Total DR enabled datasets : " + str(dr_count) + "\n\n\n")
    
    print ("  Dataset conformance status \n")
    print (" |" + "-" * 60 + "-|")
    print (" | %-60s|" %(("Conformant                : ") + str(ds_conformant)))
    print (" | %-60s|" %(("Non Conformant            : ") + str(ds_non_conformant)))
    print (" |" + "-" * 60 + "-|")
    print ("\n\n\n")
    print ("  Dataset resource status \n")
    print (" |" + "-" * 60 + "-|")
    print (" | %-60s|" %(("Emergency                 : ") + str(rs_emergency))) 
    print (" | %-60s|" %(("Critical                  : ") + str(rs_critical)))
    print (" | %-60s|" %(("Error                     : ") + str(rs_error )))
    print (" | %-60s|" %(("Warning                   : ") + str(rs_warning))) 
    print (" | %-60s|" %(("Normal                    : ") + str(rs_normal))) 
    print (" |" + "-" * 60 + "-|")
    print ("\n\n\n")
    print ("  Dataset space status \n")
    print (" |" + "-" * 60 + "-|")
    print (" | %-60s|" %(("Error                     : ") + str(ss_error))) 
    print (" | %-60s|" %(("Warning                   : ") + str(ss_warning))) 
    print (" | %-60s|" %(("Nowmal                    : ") + str(ss_normal))) 
    print (" | %-60s|" %(("Unknown                   : ") + str(ss_unknown))) 
    print (" |" + "-" * 60 + "-|")
    print ("\n\n\n")


##### VARIABLES SECTION
args = len(sys.argv) - 1

# checke for valid number of arguments
if ( args < 4 ):
        usage()

args = len(sys.argv) - 1
dfmserver = sys.argv[1]
dfmuser = sys.argv[2]
dfmpw = sys.argv[3]
opt = sys.argv[4]

if(args > 4):
    ds_name = sys.argv[5]
else :
    ds_name = None 

server = ""

# Dataset protection status parameters
ps_baseline_failure = 0
ps_lag_error        = 0
ps_lag_warning      = 0
ps_suspended        = 0
ps_uninitialized    = 0
ds_total_protected   = 0
ds_total_unprotected = 0
ds_total             = 0

# Dataset conformance status counters
ds_conformant     = 0
ds_non_conformant = 0

# Dataset protected/unprotected counters
ps_protected   = 0
ps_unprotected = 0

# DR configured datasets
dr_count           = 0
ds_resource_status = 0

# Dataset resource status variables
rs_emergency = 0
rs_critical  = 0
rs_error     = 0
rs_warning   = 0
rs_normal    = 0

# Dataset space status variables
space_status = 0
ss_error     = 0
ss_warning   = 0
ss_normal    = 0
ss_unknown   = 0

# Create the server context and set appropriate attributes for DFMServer
server = NaServer( dfmserver, 1, 0 )
server.set_style("LOGIN")
server.set_transport_type("HTTP")
server.set_server_type("DFM")
server.set_port(8088)
server.set_admin_user(dfmuser, dfmpw )

# parse the input options and call appropriate function
if ( opt == "-d" ) :
    list_dashboard()

elif ( opt == "-p" ) :
    list_datasets("ProtectedUnprotected", None)

elif ( opt == "-D" ) :
    list_datasets("DRconfigured", None)

elif ( opt == "-l" ) :
    list_datasets( "All", ds_name )

else :
    usage()
