#===============================================================#
#                                                               #
# $ID$                                                          #
#                                                               #
# dataset_health.pl                                             #
#                                                               #
# Copyright (c) 2009 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.                 #
#===============================================================#
require 5.6.1;
use lib '../../../../../../../lib/perl/NetApp';
use NaServer;
use NaElement;
use strict;

##### VARIABLES SECTION
my $args = $#ARGV + 1;
my ( $dfmserver, $dfmuser, $dfmpw, $opt ) = @ARGV;
my $server;

# Dataset protection status parameters
my $ps_baseline_failure = 0;
my $ps_lag_error        = 0;
my $ps_lag_warning      = 0;
my $ps_suspended        = 0;
my $ps_uninitialized    = 0;

my $ds_total_protected   = 0;
my $ds_total_unprotected = 0;
my $ds_total             = 0;

# Dataset conformance status counters
my $ds_conformant     = 0;
my $ds_non_conformant = 0;

# Dataset protected/unprotected counters
my $ps_protected   = 0;
my $ps_unprotected = 0;

# DR configured datasets
my $dr_count           = 0;
my $ds_resource_status = 0;

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

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

##### MAIN SECTION
# checke for valid number of arguments
usage() if ( $args < 3 );

# Create the server context and set appropriate attributes for DFMServer
$server = NaServer->new( $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 eq "-d" ) {
	list_dashboard();
} elsif ( $opt eq "-p" ) {
	list_datasets("ProtectedUnprotected");
} elsif ( $opt eq "-D" ) {
	list_datasets("DRconfigured");
} elsif ( $opt eq "-l" ) {
	list_datasets( "All", $ARGV[4] );
} else {
	usage();
}
##### SUBROUTINE SECTION

sub list_datasets {
	my $option  = shift;
	my $dataset = shift;
	my $input;
	my $output;

	# create the input API request
	$input = NaElement->new("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 eq "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
	print( "Error : " . $output->results_reason() . "\n" ) and exit(-2)
	  if ( $output->results_status() eq "failed" );

	# Extract the record and tag values
	my $records = $output->child_get_string("records");
	$ds_total = $records;
	my $tag = $output->child_get_string("tag");

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

	# check for the API status
	print( "Error : " . $output->results_reason() . "\n" ) and exit(-2)
	  if ( $output->results_status() eq "failed" );

	# get the list of datasets which is contained under datasets element
	my $dataset_info = $output->child_get("datasets");
	my @result       = $dataset_info->children_get();

	my $prot_status     = undef;
	my $conf_status     = undef;
	my $resource_status = undef;
	my $is_dr_capable   = undef;
	my $is_protected    = undef;
	my $dr_state        = undef;

	print "\n";
	print "-" x 70;
	print "\n";

	# Iterate through each dataset record
	foreach my $dataset (@result) {
		my $dataset_name = $dataset->child_get_string("dataset-name");

		# printing detials if only one dataset is selected for listing
		my $status = $dataset->child_get("dataset-status");

		# check for the input option and retrieve appropriate information
		if ( $option eq "ProtectedUnprotected" ) {
			print "Dataset name               : $dataset_name \n";
			$is_protected = $dataset->child_get_string("is-protected");
			if ( $is_protected =~ /True/i ) {
				print "Protected                  : Yes \n";
			} else {
				print "Protected                  : No \n";
			}
			print "-" x 70;
			print "\n";
		} elsif ( $option eq "DRconfigured" ) {
			my $status = $dataset->child_get("dataset-status");
			$is_dr_capable = $dataset->child_get_string("is-dr-capable");
			if ( $is_dr_capable =~ /True/i ) {
				my $dr_state       = $dataset->child_get_string("dr-state");
				my $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 "-" x 70;
				print "\n";
			}
		} elsif ( $option eq "All" ) {
			print "Dataset name               : $dataset_name \n";
			$is_protected = $dataset->child_get_string("is-protected");
			if ( $is_protected =~ /True/i ) {
				my $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 ( $is_dr_capable =~ /True/i ) {
				my $dr_state       = $dataset->child_get_string("dr-state");
				my $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 "-" x 70;
			print "\n";

		}

	}

	# finally invoke the dataset-list-info-iter-end API
	my $end = $server->invoke( "dataset-list-info-iter-end", "tag", $tag );
	print( "Error : " . $end->results_reason() . "\n" ) and exit(-2)
	  if ( $end->results_status() eq "failed" );
}

sub list_dashboard {

	# create the input dataset-list-info-iter-start API request for listing
	# through all datasets
	my $input = NaElement->new("dataset-list-info-iter-start");

	# invoke the api and capture the ouput
	my $output = $server->invoke_elem($input);

	# check the status of the API request
	print( "Error : " . $output->results_reason() . "\n" ) and exit(-2)
	  if ( $output->results_status() eq "failed" );

	# Extract the record and tag values
	my $records = $output->child_get_string("records");
	$ds_total = $records;

	my $tag = $output->child_get_string("tag");

	my $output =
	  $server->invoke( "dataset-list-info-iter-next", "maximum", $records,
		"tag", $tag );
	print( "Error : " . $output->results_reason() . "\n" ) and exit(-2)
	  if ( $output->results_status() eq "failed" );

	# get the list of datasets which is contained under datasets element
	my $dataset_info = $output->child_get("datasets");
	my @result       = $dataset_info->children_get();
	my $prot_status     = undef;
	my $conf_status     = undef;
	my $resource_status = undef;

	# Iterate through each dataset record
	foreach my $dataset (@result) {
		my $dataset_name = $dataset->child_get_string("dataset-name");
		my $status = $dataset->child_get("dataset-status");

		# get the protection status information
		$prot_status = $status->child_get_string("protection-status");
		if ( $prot_status eq "protected" ) {
			$ps_protected++;
		} elsif ( $prot_status eq "uninitialized" ) {
			$conf_status = $status->child_get_string("conformance-status");
			if ( $conf_status ne "conforming" ) {
				$ps_uninitialized++;
			}
		} elsif ( $prot_status eq "protection_suspended" ) {
			$ps_suspended++;
		} elsif ( $prot_status eq "lag_warning" ) {
			$ps_lag_warning++;
		} elsif ( $prot_status eq "lag_error" ) {
			$ps_lag_error++;
		} elsif ( $prot_status eq "baseline_failure" ) {
			$ps_baseline_failure++;
		}

		# get the conformance status information
		$conf_status = $status->child_get_string("conformance-status");
		if ( $conf_status eq "conformant" ) {
			$ds_conformant++;
		} elsif ( $conf_status eq "nonconformant" ) {
			$ds_non_conformant++;
		}

		# get the resource status information
		$resource_status = $status->child_get_string("resource-status");
		if ( $resource_status eq "emergency" ) {
			$rs_emergency++;
		} elsif ( $resource_status eq "critical" ) {
			$rs_critical++;
		} elsif ( $resource_status eq "error" ) {
			$rs_error++;
		} elsif ( $resource_status eq "warning" ) {
			$rs_warning++;
		} elsif ( $resource_status eq "normal" ) {
			$rs_normal++;
		}

		# get the space status information
		$space_status = $status->child_get_string("space-status");
		if ( $space_status eq "error" ) {
			$ss_error++;
		} elsif ( $space_status eq "warning" ) {
			$ss_warning++;
		} elsif ( $space_status eq "ok" ) {
			$ss_normal++;
		} elsif ( $space_status eq "unknown" ) {
			$ss_unknown++;
		} 
	}

	# invoke the iter-end zapi
	my $end = $server->invoke( "dataset-list-info-iter-end", "tag", $tag );
	print( "Error : " . $end->results_reason() . "\n" ) and exit(-2)
	  if ( $end->results_status() eq "failed" );

	# 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");
	print( "Error : " . $output->results_reason() . "\n" ) and exit(-2)
	  if ( $output->results_status() eq "failed" );

	$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");
	print( "Error : " . $output->results_reason() . "\n" ) and exit(-2)
	  if ( $output->results_status() eq "failed" );

	my $dr_state_status_counts = $output->child_get("dr-state-status-counts");
	my @dr_counts_results      = $dr_state_status_counts->children_get();
	my $dr_count               = 0;
	my $dr_state               = "";
	my $state_status           = "";
	my $dr_status               = "";
	my %dr_hash                = ();

	# iterate through each DR dataset
	foreach my $dr_state_status_count (@dr_counts_results) {
		my $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 eq "warning" ) {
			$dr_status = "warnings";
		}
		$state_status = $dr_state . " - " . $dr_status;
		$dr_hash{$state_status} = $count;
		$dr_count += $count;
	}

	print "\n\n  Datasets\n";
	print " |";
	print "-" x 60;
	print("-|\n");
	print(
		sprintf( " | %-60s|\n",
			"Protected                 : $ds_total_protected" )
	);
	print(
		sprintf( " | %-60s|\n",
			"Unprotected               : $ds_total_unprotected" )
	);
	print " |";
	print "-" x 60;
	print("-|\n");
	print "              Total datasets : " . $ds_total . "\n\n\n";

	print "  Dataset protection status \n";
	print " |";
	print "-" x 60;
	print("-|\n");
	print(
		sprintf( " | %-60s|\n",
			"Baseline Failure          : $ps_baseline_failure" )
	);
	print(
		sprintf( " | %-60s|\n", "Lag Error                 : $ps_lag_error" ) );
	print(
		sprintf( " | %-60s|\n", "Lag Warning               : $ps_lag_warning" )
	);
	print(
		sprintf( " | %-60s|\n", "Protection Suspended      : $ps_suspended" ) );
	print(
		sprintf( " | %-60s|\n",
			"Uninitialized             : $ps_uninitialized" )
	);
	print(
		sprintf( " | %-60s|\n", "Protected                 : $ps_protected" ) );
	print " |";
	print "-" x 60;
	print("-|\n\n\n");

	print "  Dataset Lags \n";
	print " |";
	print "-" x 60;
	print("-|\n");
	my $output2 = $server->invoke("dp-dashboard-get-lagged-datasets");
	print( "Error : " . $output->results_reason() . "\n" ) and exit(-2)
	  if ( $output->results_status() eq "failed" );

	my @dataset_lags = $output2->child_get("dp-datasets")->children_get();
	my $count        = 0;
	foreach my $dataset_lag (@dataset_lags) {
		my $name      = $dataset_lag->child_get_string("dataset-name");
		my $worst_lag = $dataset_lag->child_get_string("worst-lag");
		my $time      = convert_seconds($worst_lag);
		print( sprintf( " | %-20s  %-38s|\n", $name, $time ) );
		if ( ++$count >= 5 ) {
			last;
		}
	}
	if ( $count == 0 ) {
		print( sprintf( " | %-60s|\n", "No data available" ) );
	}
	print " |";
	print "-" x 60;
	print("-|\n\n\n");

	print "  Failover readiness \n";
	print " |";
	print "-" x 60;
	print("-|\n");

	if ( $dr_count != 0 ) {
		foreach my $key ( keys %dr_hash ) {
			my $value = $dr_hash{$key};
			print( sprintf( " | %-60s|\n", "$key            : $value" ) );
		}
	} else {
		print( sprintf( " | %-60s|\n", "Normal" ) );
	}
	print " |";
	print "-" x 60;
	print("-|\n");
	print "   Total DR enabled datasets : $dr_count \n\n\n";

	print "  Dataset conformance status \n";
	print " |";
	print "-" x 60;
	print("-|\n");
	print(
		sprintf( " | %-60s|\n", "Conformant                : $ds_conformant" )
	);
	print(
		sprintf( " | %-60s|\n",
			"Non Conformant            : $ds_non_conformant" )
	);
	print " |";
	print "-" x 60;
	print("-|\n\n\n");

	print "  Dataset resource status \n";
	print " |";
	print "-" x 60;
	print("-|\n");
	print(
		sprintf( " | %-60s|\n", "Emergency                 : $rs_emergency" ) );
	print(
		sprintf( " | %-60s|\n", "Critical                  : $rs_critical" ) );
	print( sprintf( " | %-60s|\n", "Error                     : $rs_error" ) );
	print(
		sprintf( " | %-60s|\n", "Warning                   : $rs_warning" ) );
	print( sprintf( " | %-60s|\n", "Normal                    : $rs_normal" ) );
	print " |";
	print "-" x 60;
	print("-|\n\n\n");

	print "  Dataset space status \n";
	print " |";
	print "-" x 60;
	print("-|\n");
	print( sprintf( " | %-60s|\n", "Error                     : $ss_error" ) );
	print(
		sprintf( " | %-60s|\n", "Warning                   : $ss_warning" ) );
	print( sprintf( " | %-60s|\n", "Nowmal                    : $ss_normal" ) );
	print(
		sprintf( " | %-60s|\n", "Unknown                   : $ss_unknown" ) );
	print " |";
	print "-" x 60;
	print("-|\n\n\n");
}

sub convert_seconds {
	my $secs = shift;
	my $days;
	my $hours;
	my $minutes;
	my $seconds;
	my $output;

	my @parts = gmtime($secs);
	$output =
	  sprintf( "   %4d Days %2d Hr %2d Min %2d Sec", @parts[ 7, 2, 1, 0 ] );
	return $output;
}

sub usage() {
	print <<MSG;

Usage:
	perl dataset_health.pl <server> <user> <password> -d | -p | -D | <-l [<dataset-name>]>
		<dfm-server>    --  Name/IP Address of the DFM Server
		<user>          --  DFM Server User name
		<password>      --  DFM Server Password
		<dataset-name>  --  Name of the dataset
		-d              --  Display the dataset health dashboard
		-l              --  List all datasets and its status information
		-p              --  List protected and Unprotected datasets
		-D              --  List DR configured datasets
MSG
	exit 1;
}
