#===============================================================#
#                                                               #
# $ID$                                                          #
#                                                               #
# dataset_metrics.pl                                            #
#                                                               #
# Copyright (c) 2009 NetApp, Inc. All rights reserved.          #
# Specifications subject to change without notice.              #
#                                                               #
# Sample code to demonstrate how to get the storage information #
# of each dataset. This provides total space, used space and    #
# available space nformation of each node in a dataset. It also #
# provides space breakout and dedupe space savings information  #
# of each dataset member.                                       #
#                                                               #
# 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;

##### MAIN SECTION
# checking for valid number of parameters for the respective operations
usage() if ( $args < 4 );

# Create the server context and set appropriate attributes
$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 );

if ( $opt eq "-n" ) {
	calc_ds_space_info("Node");
} elsif ( $args > 4 && $opt eq "-m" ) {
	calc_ds_space_info( "Storage", $ARGV[4] );
} elsif ( $args > 4 && $opt eq "-s" ) {
	calc_ds_detail_space_info( "Space breakout", $ARGV[4] );
} elsif ( $args > 4 && $opt eq "-d" ) {
	calc_ds_detail_space_info( "Dedupe", $ARGV[4] );
} else {
	usage();
}
##### SUBROUTINE SECTION
sub get_units {
	my $bytes  = shift;
	my $format = 0;
	my $units;

	if ( $bytes < 1048576 ) {
		$format = sprintf( "%.2f", $bytes / (1024.00) );
		$units = $format . " (KB)";
	} elsif ( $bytes >= 1048576 && $bytes < 1073741824 ) {
		$format = sprintf( "%.2f", $bytes / ( 1024.00 * 1024.00 ) );
		$units = $format . " (MB)";
	} else {
		$format = sprintf( "%.2f", $bytes / ( 1024.00 * 1024.00 * 1024.00 ) );
		$units = $format . " (GB)";
	}
	return $units;
}

sub calc_ds_detail_space_info {
	my $operation    = shift;
	my $dataset_name = shift;

	# create the input API request to get the dataset details
	my $input = NaElement->new("dataset-list-info-iter-start");
	$input->child_add_string( "object-name-or-id", $dataset_name )
	  if ($dataset_name);

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

	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");
	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 dataset info
	my $dataset_info =
	  $output->child_get("datasets")->child_get("dataset-info");

	if ($dataset_info) {
		my $dataset_name = $dataset_info->child_get_string("dataset-name");
		my $dataset_id   = $dataset_info->child_get_string("dataset-id");
		my $prov_policy_id =
		  $dataset_info->child_get_string("provisioning-policy-id");
		my $storage_type = "nas";

		# get the policy type to identify for SAN or NAS storage type
		my $prov_policy_type = get_prov_policy_type($prov_policy_id);
		my $member_input =
		  NaElement->new("dataset-member-list-info-iter-start");
		$member_input->child_add_string( "dataset-name-or-id", $dataset_id );
		$member_input->child_add_string( "include-indirect",   "true" );
		$member_input->child_add_string( "include-space-info", "true" );
		$member_input->child_add_string( "suppress-status-refresh", "true" );
		$member_input->child_add_string( "include-exports-info",    "true" );

		my $member_output = $server->invoke_elem($member_input);
		print( "Error : " . $member_output->results_reason() . "\n" )
		  and exit(-2)
		  if ( $output->results_status() eq "failed" );
		my $member_records = $member_output->child_get_string("records");
		my $member_tag     = $member_output->child_get_string("tag");
		$member_output = $server->invoke( "dataset-member-list-info-iter-next",
			"maximum", $member_records, "tag", $member_tag );
		print( "Error : " . $member_output->results_reason() . "\n" )
		  and exit(-2)
		  if ( $member_output->results_status() eq "failed" );
		my @members;

		if ( $member_output->child_get_int("records") > 0 ) {
			@members =
			  $member_output->child_get("dataset-members")->children_get();
		}

		print "\n";
		print "-" x 75;
		print("\nDataset               : $dataset_name\n");
		print "-" x 75;
		print("\n");

		# iterate through each dataset member
		foreach my $member (@members) {
			my $space_info   = $member->child_get("space-info");
			my $space_status = "unknown";
			my $node_name;
			my $member_name = $member->child_get_string("member-name");
			my $member_type = $member->child_get_string("member-type");
			$node_name = $member->child_get_string("dp-node-name");
			my $snap_used       = 0;
			my $snap_total      = 0;
			my $snap_free       = 0;
			my $snap_overflow   = 0;
			my $data_used       = 0;
			my $data_free       = 0;
			my $data_total      = 0;
			my $overwrite_used  = 0;
			my $overwrite_total = 0;
			my $overwrite_free  = 0;
			my $hole_reserve    = 0;

			if ( !$node_name ) {
				$node_name = "Primary data";
			}

			# ignore special dataset member
			if ( !$space_info ) {
				$space_status = "Not Available";
			}
			if ( substr( $member_name, -1, 1 ) eq "-" ) {
				$space_status = undef;
			} elsif ( 
				# get the space information for non-volume member type
				$member_type ne "volume" && $space_info ) {
				$space_status = $space_info->child_get_string("space-status");
				$data_free    = $space_info->child_get_int("available-space");
				$data_total   = $space_info->child_get_int("total-space");
				$data_used    = $space_info->child_get_int("used-space");
			} elsif ( $member_type eq "volume" && $space_info ) {
				# get the space information for volume member type
				$space_status = $space_info->child_get_int("space-status");
				my $volume_output =
				  $server->invoke( "volume-list-info-iter-start",
					"object-name-or-id",
					$member->child_get_string("member-id") );
				print( "Error : " . $volume_output->results_reason() . "\n" )
				  and exit(-2)
				  if ( $volume_output->results_status() eq "failed" );

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

				if ( $volume_output->child_get_int("records") > 0 ) {
					$volume_info =
					  $volume_output->child_get("volumes")
					  ->child_get("volume-info");
				}

				#get the volume space information
				if ($volume_info) {
					my $volume_size = $volume_info->child_get("volume-size");
					$snap_used =
					  $volume_size->child_get_int("snapshot-reserve-used");
					$snap_total =
					  $volume_size->child_get_int("snapshot-reserve-total");
					$snap_free =
					  $volume_size->child_get_int("snapshot-reserve-avail");
					$data_used  = $volume_size->child_get_int("afs-used");
					$data_free  = $volume_size->child_get_int("afs-avail");
					$data_total = $volume_size->child_get_int("afs-total");
					$overwrite_used =
					  $volume_size->child_get_int("overwrite-reserve-used");
					$overwrite_total =
					  $volume_size->child_get_int("overwrite-reserve-total");
					$overwrite_free =
					  $volume_size->child_get_int("overwrite-reserve-avail");
					$hole_reserve = $volume_size->child_get_int("hole-reserve");
					my $dedupe_error;
					my $dedupe_info;
					my $dedupe_progress;
					my $dedupe_space_savings;
					my $dedupe_space_savings_percent;
					my $dedupe_status;

					# For SAN, we have hole reserve
					if ( $hole_reserve > 0 || $prov_policy_type eq "san" ) {
						$storage_type = "san";
					}
					if ( $operation eq "Dedupe" ) {
						$dedupe_info =
						  $volume_info->child_get("volume-dedupe-info");
						if ( !$dedupe_info ) {
							$dedupe_error = "Dedupe information not available";
						} else {
							$dedupe_status =
							  $dedupe_info->child_get_string("dedupe-status");
							$dedupe_progress =
							  $dedupe_info->child_get_string("dedupe-progress");
							$dedupe_space_savings =
							  $dedupe_info->child_get_string(
								"dedupe-space-savings");
							$dedupe_space_savings_percent =
							  $dedupe_info->child_get_string(
								"dedupe-space-savings-percentage");
						}
						calc_dedupe_space_info(
							$member_name,
							$member_type,
							$space_status,
							$storage_type,
							$data_used,
							$data_total,
							$data_free,
							$snap_used,
							$snap_total,
							$snap_free,
							$overwrite_used,
							$overwrite_total,
							$overwrite_free,
							$hole_reserve,
							$dedupe_space_savings,
							$dedupe_space_savings_percent,
							$dedupe_status,
							$dedupe_progress,
							$dedupe_error
						);
					}
				}
				$volume_output =
				  $server->invoke( "volume-list-info-iter-end", "tag",
					$volume_tag );
				print( "Error : " . $volume_output->results_reason() . "\n" )
				  and exit(-2)
				  if ( $volume_output->results_status() eq "failed" );
			}
			if ( $operation eq "Space breakout" && $space_status ) {
				calc_space_breakout_info(
					$member_name,    $member_type,    $space_status,
					$storage_type,   $data_used,      $data_total,
					$data_free,      $snap_used,      $snap_total,
					$snap_free,      $overwrite_used, $overwrite_total,
					$overwrite_free, $hole_reserve
				);
			}
		}
		if ( $member_records == 0 ) {
			print("\nNo data members available \n");
		}
		print "-" x 75;
		print "\n";
		$member_output = $server->invoke( "dataset-member-list-info-iter-end",
			"tag", $member_tag );
		print( "Error : " . $member_output->results_reason() . "\n" )
		  and exit(-2)
		  if ( $member_output->results_status() eq "failed" );
	}

	# invoking 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" );
}

# This function will retrieve the node level and member level space information.
# First parameter contains the type of space information that needs to
# be retrieved and second parameter contains the optional dataset name
sub calc_ds_space_info {
	my $option  = shift;
	my $ds_name = shift;
	my $total   = 0;
	my $used    = 0;
	my $avail   = 0;
	my $count   = 0;
	my $flag    = 0;

	# create the input API request
	my $input = NaElement->new("dataset-list-info-iter-start");
	$input->child_add_string( "object-name-or-id", $ds_name ) if ($ds_name);

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

	print( "Error : " . $output->results_reason() . "\n" ) and exit(-2)
	  if ( $output->results_status() eq "failed" );

	# Extracr the record and tag values
	my $records = $output->child_get_string("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 are contained under datasets element
	my $dataset_info = $output->child_get("datasets");
	my @result       = $dataset_info->children_get();

	# print the header for listing member level storage details
	if ( $option eq "Storage" ) {
		print("\n Dataset name: $ds_name \n\n");
		print("\n Member level details:\n");
		print "|";
		print "-" x 75;
		print "|\n";
		print
"| Storage                                                                   |\n";
		print
"|           | Node     | status  | Total space  | Used space  | Avail space |\n";
		print "|";
		print "-" x 75;
		print "|\n";
	}

	# Iterate through each dataset
	foreach my $dataset (@result) {

		# hash table declarations for node level storage details
		my %space;

		my $dataset_name = $dataset->child_get_string("dataset-name");
		my $dataset_id   = $dataset->child_get_string("dataset-id");

# create the dataset-member-list-info-iter-start API request to iterate through each
# dataset member
		my $member_input =
		  NaElement->new("dataset-member-list-info-iter-start");
		$member_input->child_add_string( "dataset-name-or-id", $dataset_id );
		$member_input->child_add_string( "include-indirect",   "true" );
		$member_input->child_add_string( "include-space-info", "true" );
		$member_input->child_add_string( "suppress-status-refresh", "true" );
		$member_input->child_add_string( "include-exports-info",    "true" );

		# invoke the dataset-member-list-info-iter-start API
		my $member_output = $server->invoke_elem($member_input);

		print( "Error : " . $member_output->results_reason() . "\n" )
		  and exit(-2)
		  if ( $member_output->results_status() eq "failed" );

		my $member_records = $member_output->child_get_string("records");
		my $member_tag     = $member_output->child_get_string("tag");

		# invoke the dataset-member-list-info-iter-next API
		$member_output = $server->invoke( "dataset-member-list-info-iter-next",
			"maximum", $member_records, "tag", $member_tag );

		print( "Error : " . $member_output->results_reason() . "\n" )
		  and exit(-2)
		  if ( $member_output->results_status() eq "failed" );
		my @members;

		if ( $member_output->child_get_int("records") > 0 ) {
			@members =
			  $member_output->child_get("dataset-members")->children_get();
		}

		#print $member_output->sprintf("");
		# iterate through each dataset member
		foreach my $member (@members) {
			my $space_info   = $member->child_get("space-info");
			my $avail_space  = 0;
			my $space_status = "unknown";
			my $total_space  = 0;
			my $used_space   = 0;
			my $node_name;
			my $member_name = $member->child_get_string("member-name");
			my $member_type = $member->child_get_string("member-type");
			$node_name = $member->child_get_string("dp-node-name");

			if ( $node_name eq "Primary data" || !$node_name ) {
				$node_name = "Primary";
			}

			# ignore special member
			if ( substr( $member_name, -1, 1 ) eq "-" ) {
				$member_name = undef;
			} 
			# Get the space information for non volume member type i.e for
			# qtrees and luns. For volume member type, get space info from
			# volme-list-info API
			elsif ( $member_type ne "volume" && $space_info ) {
				$avail_space  = $space_info->child_get_int("available-space");
				$total_space  = $space_info->child_get_int("total-space");
				$used_space   = $space_info->child_get_int("used-space");
				$space_status = $space_info->child_get_string("space-status");
			} elsif ( $member_type eq "volume" && $space_info ) {
				my $volume_output =
				  $server->invoke( "volume-list-info-iter-start",
					"object-name-or-id",
					$member->child_get_string("member-id") );
				print( "Error : " . $volume_output->results_reason() . "\n" )
				  and exit(-2)
				  if ( $volume_output->results_status() eq "failed" );
				my $volume_records =
				  $volume_output->child_get_string("records");
				my $volume_tag = $volume_output->child_get_string("tag");
				$volume_output =
				  $server->invoke( "volume-list-info-iter-next", "maximum",
					$volume_records, "tag", $volume_tag );

				print( "Error : " . $volume_output->results_reason() . "\n" )
				  and exit(-2)
				  if ( $volume_output->results_status() eq "failed" );

				my $volume = $volume_output->child_get("volumes");
				if ($volume) {
					$space_status =
					  $space_info->child_get_string("space-status");
					my $volume_info = $volume->child_get("volume-info");
					my $volume_size = $volume_info->child_get("volume-size");
					$used_space  = $volume_size->child_get_int("afs-used");
					$avail_space = $volume_size->child_get_int("afs-avail");
					$total_space = $volume_size->child_get_int("afs-total");
				}
				$volume_output =
				  $server->invoke( "volume-list-info-iter-end", "tag",
					$volume_tag );
				print( "Error : " . $volume_output->results_reason() . "\n" )
				  and exit(-2)
				  if ( $volume_output->results_status() eq "failed" );
			}
			if ($member_name) {
				my @val = (
					$member_type, $space_status, $total_space,
					$used_space,  $avail_space
				);
				if ( $node_name eq "Primary" ) {
					$space{primary}{$member_name}[$_] = $val[$_]
					  for ( 0 .. scalar(@val) - 1 );
				}
				if ( $node_name eq "Backup" ) {
					$space{backup}{$member_name}[$_] = $val[$_]
					  for ( 0 .. scalar(@val) - 1 );
				}
				if ( $node_name eq "Mirror" ) {
					$space{mirror}{$member_name}[$_] = $val[$_]
					  for ( 0 .. scalar(@val) - 1 );
				}
				if ( $node_name eq "First Mirror" ) {
					$space{first_mirror}{$member_name}[$_] = $val[$_]
					  for ( 0 .. scalar(@val) - 1 );
				}
				if ( $node_name eq "Second Mirror" ) {
					$space{second_mirror}{$member_name}[$_] = $val[$_]
					  for ( 0 .. scalar(@val) - 1 );
				}
				if ( $node_name eq "DR Mirror" ) {
					$space{dr_mirror}{$member_name}[$_] = $val[$_]
					  for ( 0 .. scalar(@val) - 1 );
				}
				if ( $node_name eq "DR Backup" ) {
					$space{dr_backup}{$member_name}[$_] = $val[$_]
					  for ( 0 .. scalar(@val) - 1 );
				}
			}
			if ( $option eq "Storage" && $member_name ) {
				my $tspace = "";
				my $uspace = "";
				my $aspace = "";
				$tspace = get_units($total_space);
				$uspace = get_units($used_space);
				$aspace = get_units($avail_space);
				my $string =
				  sprintf
				  "|%-75s|\n| %-10s| %-9s| %-8s| %-13s| %-12s| %-12s|\n",
				  (
					$member_name, "", $node_name, $space_status, $tspace,
					$uspace, $aspace
				  );
				print $string;
				print "|";
				print "-" x 75;
				print "|\n";

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

		if ( $option eq "Storage" ) {
			print("\n Node level details:\n");
		} else {
			print("\n Dataset name: $dataset_name\n");
		}
		calc_node_space_info( \%space );

		print "|";
		print "-" x 75;
		print "|\n\n";
	}

	# invoking 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" );
}

sub calc_node_space_info {
	my $space = shift;
	my %space = %{$space};
	print "|";
	print "-" x 75;
	print "|\n";
	print(
"| Node type     |  Total space     |  Used space     |  Avail space         |\n"
	);
	print "|";
	print "-" x 75;
	print "|\n";

	foreach my $node ( keys %space ) {
		my $total  = 0;
		my $used   = 0;
		my $avail  = 0;
		my $flag   = 0;
		my %member = %{ $space{$node} };
		foreach my $member ( keys %member ) {
			my @size = @{ $space{$node}{$member} };
			my $member =~ /(.*)\//;
			my $vol = $1;

			if ( $size[0] eq "volume" or $space{$vol} ) {
				$total += $size[2];
				$used  += $size[3];
				$avail += $size[4];
			} else { $flag = 1; }
		}
		my $string = sprintf "| %-14s| %-17s| %-16s| %-21s|\n",
		  ( $node, get_units($total), get_units($used), get_units($avail) );
		print "$string";
		print "|" and print "-" x 75 and print "|\n" if ( not $flag );
	}
}

sub get_prov_policy_type {
	my $prov_policy_id = shift;
	my $prov_policy_type;
	my $prov_policy_output = $server->invoke(
		"provisioning-policy-list-iter-start",
		"provisioning-policy-name-or-id",
		$prov_policy_id
	);
	print( "Error : " . $prov_policy_output->results_reason() . "\n" )
	  and exit(-2)
	  if ( $prov_policy_output->results_status() eq "failed" );
	my $prov_member_records = $prov_policy_output->child_get_string("records");
	my $prov_member_tag     = $prov_policy_output->child_get_string("tag");
	$prov_policy_output = $server->invoke( "provisioning-policy-list-iter-next",
		"maximum", $prov_member_records, "tag", $prov_member_tag );
	print( "Error : " . $prov_policy_output->results_reason() . "\n" )
	  and exit(-2)
	  if ( $prov_policy_output->results_status() eq "failed" );
	my $prov_policy_type;

	if ( $prov_policy_output->child_get_int("records") > 0 ) {
		$prov_policy_type =
		  $prov_policy_output->child_get("provisioning-policies")
		  ->child_get("provisioning-policy-info")
		  ->child_get_string("provisioning-policy-type");
	}
	$prov_policy_output = $server->invoke( "provisioning-policy-list-iter-end",
		"tag", $prov_member_tag );
	print( "Error : " . $prov_policy_output->results_reason() . "\n" )
	  and exit(-2)
	  if ( $prov_policy_output->results_status() eq "failed" );

	return $prov_policy_type;
}

sub calc_dedupe_space_info {
	my $member_name = shift, my $member_type = shift;
	my $space_status                 = shift;
	my $storage_type                 = shift;
	my $data_used                    = shift;
	my $data_total                   = shift;
	my $data_free                    = shift;
	my $snap_used                    = shift;
	my $snap_total                   = shift;
	my $snap_free                    = shift;
	my $overwrite_used               = shift;
	my $overwrite_total              = shift;
	my $overwrite_free               = shift;
	my $hole_reserve                 = shift;
	my $dedupe_space_savings         = shift;
	my $dedupe_space_savings_percent = shift;
	my $dedupe_status                = shift;
	my $dedupe_progress              = shift;
	my $dedupe_error                 = shift;

	my $snap_overflow            = 0;
	my $data_used_without_dedupe = 0;
	my $data_used_without_dedupe = 0;

	print("Storage               : $member_name \n");
	print("Storage type          : $member_type \n");
	print("Space status          : $space_status \n");

	if ($dedupe_error) {
		print( $dedupe_error . "\n\n" );
		return;
	}
	print("\nDedupe status                      : $dedupe_status \n");
	print("Current Dedupe progress            : $dedupe_progress \n\n");
	if ( $storage_type eq "nas" ) {
		my $snap_overflow;
		if ( $snap_used > $snap_total ) {
			$snap_overflow = $snap_used - $snap_total;
			$data_used -= $snap_overflow;
		}
		my $used_space_without_dedupe = $data_used + $dedupe_space_savings;
		my $used_space_with_dedupe    = $data_used;
		print("Dedupe space saving information  :\n");
		print(  "Dedupe space saved                 : "
			  . get_units($dedupe_space_savings)
			  . "\n" );
		print(  "Dedupe space savings(%)            : "
			  . $dedupe_space_savings_percent
			  . " (%)\n\n" );
		print(  "Used space without dedupe          : "
			  . get_units($used_space_without_dedupe)
			  . "\n" );
		print(  "Used space with dedupe             : "
			  . get_units($used_space_with_dedupe)
			  . "\n\n\n" );

	} else {
		my $lun_used_space;
		my $lun_free_space = $hole_reserve;
		if ( $overwrite_used > 0 ) {
			$data_used -= ( $overwrite_used + $overwrite_free );
		} elsif ( $overwrite_used == 0 ) {
			$data_used -= $overwrite_total;
		}
		$lun_used_space = $data_used - $lun_free_space;
		if ( $snap_used > $snap_total ) {
			$snap_overflow = $snap_used - $snap_total;
			$lun_used_space -= $snap_overflow;
		}
		my $lun_used_space_without_dedupe =
		  $lun_used_space + $dedupe_space_savings;
		print("Dedupe space savings :\n");
		print(  "Dedupe space saved                 : "
			  . get_units($dedupe_space_savings)
			  . "\n" );
		print(  "Dedupe space savings(%)            : "
			  . $dedupe_space_savings_percent
			  . " (%)\n\n" );
		print(  "LUN used space without dedupe      : "
			  . get_units($lun_used_space_without_dedupe)
			  . "\n" );
		print(  "LUN used space with dedupe         : "
			  . get_units($lun_used_space)
			  . "\n\n\n" );
	}
}

sub calc_space_breakout_info {
	my $member_name     = shift;
	my $member_type     = shift;
	my $space_status    = shift;
	my $storage_type    = shift;
	my $data_used       = shift;
	my $data_total      = shift;
	my $data_free       = shift;
	my $snap_used       = shift;
	my $snap_total      = shift;
	my $snap_free       = shift;
	my $overwrite_used  = shift;
	my $overwrite_total = shift;
	my $overwrite_free  = shift;
	my $hole_reserve    = shift;
	my $snap_overflow;
	my $total_vol_size  = 0;
	my $total_data_size = 0;

	print("Storage               : $member_name \n");
	print("Storage type          : $member_type \n");
	print("Space status          : $space_status \n");

	if ( $space_status eq "Not Available" ) {
		print("\n");
		return;
	}
	if ( $member_type ne "volume" ) {
		if ( $member_type eq "qtree" ) {
			print( "Qtree used space      : " . get_units($data_used) . "\n" );
			print(  "Qtree free space      : "
				  . get_units($data_free)
				  . " approx \n\n" );
		} else {
			print(
				"LUN total space       : " . get_units($data_total) . "\n\n" );
		}
		return;
	}
	if ( $storage_type eq "nas" ) {
		if ( $snap_used > $snap_total ) {
			$snap_overflow = $snap_used - $snap_total;
			$data_used -= $snap_overflow;
			$snap_used = $snap_total;
		}
		print("\nData     :\n");
		print(  "Used space                         : "
			  . get_units($data_used)
			  . "\n" );
		print(  "Free space                         : "
			  . get_units($data_free)
			  . "\n" );
		print(  "Snap overflow                      : "
			  . get_units($snap_overflow)
			  . "\n" );
		$total_data_size = $data_used + $data_free + $snap_overflow;
		$total_vol_size += $total_data_size;
		print("                                   --------------------\n");
		print(  "Total data space                   : "
			  . get_units($total_data_size)
			  . "\n\n" );
		print("Snapshot :\n");
		print(  "Used snapshot space                : "
			  . get_units($snap_used)
			  . "\n" );
		print(  "Free snapshot space                : "
			  . get_units($snap_free)
			  . "\n" );
		print("                                   --------------------\n");
		print(  "Snapshot reserve                   : "
			  . get_units($snap_total)
			  . "\n\n" );
		print("\nVolume   :\n");
		$total_vol_size += $snap_total;
		print(  "Total volume size                  : "
			  . get_units($total_vol_size)
			  . "\n\n\n" );
	} else {
		my $lun_used_space         = $data_used;
		my $lun_free_space         = $hole_reserve;
		my $total_data_size        = 0;
		my $data_overwrite_reserve = 0;
		my $volume_used            = 0;
		if ( $overwrite_used > 0 ) {
			$data_used -= ( $overwrite_used + $overwrite_free );
		} elsif ( $overwrite_used == 0 ) {
			$data_used -= $overwrite_total;
		}
		$lun_used_space = $data_used - $lun_free_space;
		if ( $snap_used > $snap_total ) {
			$snap_overflow = $snap_used - $snap_total;
			$lun_used_space -= $snap_overflow;
		}
		print("\nData     :\n");
		print(  "LUN used space                     : "
			  . get_units($lun_used_space)
			  . "\n" );
		print(  "LUN free space                     : "
			  . get_units($lun_free_space)
			  . "\n" );
		print(  "Data in overwrite reserve          : "
			  . et_units($overwrite_used)
			  . "\n" )
		  if ( $overwrite_used > 0 );
		$total_data_size = $lun_used_space + $lun_free_space + $overwrite_used;
		print("                                   --------------------\n");
		print(  "Total data space                   : "
			  . get_units($total_data_size)
			  . "\n" );
		print("\nVolume   :\n");
		print(  "Used snapshot space                : "
			  . get_units($snap_used)
			  . "\n" );
		$data_overwrite_reserve =
		  $lun_used_space + $lun_free_space + $overwrite_total;
		print(  "Data and overwrite reserve size    : "
			  . get_units($data_overwrite_reserve)
			  . "\n" );
		print("                                   --------------------\n");
		$volume_used = $snap_used + $data_overwrite_reserve;
		print(  "Volume used                        : "
			  . get_units($volume_used)
			  . "\n\n" );
		print(  "Volume available                   : "
			  . get_units($data_free)
			  . "\n" );
		$total_vol_size = $volume_used + $data_free;
		print(  "Total volume size                  : "
			  . get_units($total_vol_size)
			  . "\n\n\n" );
	}
}

sub usage() {
	print <<MSG;

Usage:
perl dataset_metrics.pl <dfm-server> <user> <password>  -n | <-m <dataset-name> > | <-s <dataset-name>> | <-d <dataset-name>>

	   <dfm-server>      -- Name/IP Address of the DFM server
	   <user>            -- DFM Server User name
	   <password>        -- DFM Server Password
	   <dataset-name>    -- dataset name
	   -n                -- list node level space information of all the datasets
	   -m                -- list member and node level space information of the dataset
	   -s                -- list space breakout information of the dataset
	   -d                -- list dedupe space saving information of the dataset

MSG
	exit 1;
}
