#===============================================================#
#                                                               #
# $ID$                                                          #
#                                                               #
# dataset.pl                                                    #
#                                                               #
# Copyright (c) 2009 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.                 #
#===============================================================#
require 5.6.1;

use lib '../../../../../../../lib/perl/NetApp';
use NaServer;
use NaElement;
use strict;

# Variables declaration
my $cmd_args = $#ARGV + 1;
my $dfmserver = shift;
my $dfmuser   = shift;
my $dfmpw     = shift;
my $command   = shift;

sub print_usage() {
	print <<MSG;

 Usage:
 dataset.pl <dfmserver> <user> <passwd> list [<name>]
 dataset.pl <dfmserver> <user> <passwd> create <name> [<vfiler> <prov_pol> <prot-pol>]
 dataset.pl <dfmserver> <user> <passwd> destroy <name>
 dataset.pl <dfmserver> <user> <passwd> update <name> <prov_pol> <prot_pol> <pri_rp> <sec_rp> [<ter_rp>]
 dataset.pl <dfmserver> <user> <passwd> member list <name>
 dataset.pl <dfmserver> <user> <passwd> member add <name> <mem_add>
 dataset.pl <dfmserver> <user> <passwd> member del <name> <mem_del>
 dataset.pl <dfmserver> <user> <passwd> provision <name> <mem_prov_name> <size> [<snap-size>]

 <dfmserver>     -- Name/IP Address of the DFM server
 <user>          -- DFM server User name
 <passwd>        -- DFM server User Password
 <name>          -- Name of the dataset
 <vfiler>        -- Attach newly provisioned member to this vfiler
 <prov_pol>      -- name or id of an exisitng nas provisioning policy
 <prot-pol>      -- name or id of an exisitng protection policy
 <mem_prov_name> -- member name to be provisioned
 <size>          -- size of the new member to be provisioned in bytes
 <snap-size>     -- maximum size in bytes allocated to snapshots in SAN envs
 <mem_add>       -- member to be added
 <mem_del>       -- member to be removed
 <pri_rp>        -- Primary resource pool
 <sec_rp>        -- Secondary resource pool
 <ter_rp>        -- Tertiary resource pool
 
	If the protection policy is 'Mirror', specify only pri_rp and sec_rp.
	If protection policy is 'Back up, then Mirror', specify pri_rp, sec_rp and ter_rp
MSG
	exit 1;
}

# check for valid number of parameters
if ( $cmd_args < 4 ) {
	print_usage();
}

# Setup DFM server connection
my $s = NaServer->new( $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 eq "list" ) {
	if ( $cmd_args > 5 ) {
		print_usage();
		exit -1;
	}

	my $dsName = shift;
	my $out =
	  $s->invoke( "dataset-list-info-iter-start", "object-name-or-id",
		$dsName );
	if ( $out->results_status() eq "failed" ) {
		print( "Error : " . $out->results_reason() . "\n" );
		exit(-2);
	}

	my $in = NaElement->new("dataset-list-info-iter-next");
	$in->child_add_string( "maximum", $out->child_get_int("records") );
	$in->child_add_string( "tag",     $out->child_get_string("tag") );

	my $out = $s->invoke_elem($in);
	if ( $out->results_status() eq "failed" ) {
		print( "Error : " . $out->results_reason() . "\n" );
		exit(-2);
	}
	print "\nDATASETS:\n";
	print
	  "===================================================================\n";

	if ( $out->child_get_int("records") eq 0 ) {
		print "Error: No Datasets!\n";
		exit;
	}

	my $rps     = $out->child_get("datasets");
	my @rpInfos = $rps->children_get("datasets-info");
	foreach my $rpi (@rpInfos) {
		print "Dataset Name\t: " . $rpi->child_get_string("dataset-name");
		print "\n";
		my $dsstatus = $rpi->child_get("dataset-status");
		print "Overall Status\t: "
		  . $dsstatus->child_get_string("resource-status");
		print "\n";
		print "# of Members\t: " . $rpi->child_get_string("member-count");
		print "\n";
		my $value = "-Not Configured-";

		if ( $rpi->child_get_string("vfiler-name") ne "" ) {
			$value = $rpi->child_get_string("vfiler-name");
		}
		print "VFiler unit\t: " . $value . "\n";
		$value = "-Not Configured-";
		if ( $rpi->child_get_string("protection-policy-name") ne "" ) {
			$value = $rpi->child_get_string("protection-policy-name");
		}
		print "Prot. Policy\t: " . $value . "\n";
		$value = "-Not Configured-";
		if ( $rpi->child_get_string("provisioning-policy-name") ne "" ) {
			$value = $rpi->child_get_string("provisioning-policy-name");
		}
		print "Prov. Policy\t: " . $value . "\n";
		print "Res. pools(Pri)\t: ";
		my $rps = $rpi->child_get("resourcepools");
		if ( $rps eq "" ) {
			print "No attached Resourcepool!\n";
		} else {
			my @dsrpi = $rps->children_get("dataset-resourcepool-info");
			foreach my $rp (@dsrpi) {
				print $rp->child_get_string("resourcepool-name");
				print "; ";
			}
		}
		print "\n";
		print
"===================================================================\n";
	}
} elsif ( $command eq "create" ) {
	if ( $cmd_args < 5 ) {
		print_usage();
	}
	my $dsName = shift;
	my $vfiler = shift;
	my $prov   = shift;
	my $prot   = shift;

	my $in = NaElement->new("dataset-create");
	$in->child_add_string( "dataset-name", $dsName );
	if ( $prot ne "" ) {
		$in->child_add_string( "protection-policy-name-or-id", $prot );
	}
	if ( $prov ne "" ) {
		$in->child_add_string( "provisioning-policy-name-or-id", $prov );
	}
	if ( $vfiler ne "" ) {
		$in->child_add_string( "vfiler-name-or-id", $vfiler );
	}

	my $out = $s->invoke_elem($in);
	if ( $out->results_status() eq "failed" ) {
		print( "Error : " . $out->results_reason() . "\n" );
		exit(-2);
	}
	print "Dataset " 
	  . $dsName
	  . " created with ID "
	  . $out->child_get_int("dataset-id") . "!\n";
} elsif ( $command eq "destroy" ) {
	if ( $cmd_args < 5 ) {
		print
"Usage: dataset.pl <dfmserver> <user> <password> destroy <dataset_name>\n";
	}
	my $dsName = shift;
	my $out = $s->invoke( "dataset-destroy", "dataset-name-or-id", $dsName );
	if ( $out->results_status() eq "failed" ) {
		print( "Error : " . $out->results_reason() . "\n" );
		exit(-2);
	}
	print "Dataset " . $dsName . " destroyed!\n";
} elsif ( $command eq "update" ) {
	if ( $cmd_args < 9 ) {
		print_usage();
	}
	my $dsName = shift;
	my $provp  = shift;
	my $protp  = shift;
	my $priRp  = shift;
	my $secRp  = shift;
	my $terRp  = shift;

	my $out = $s->invoke( "dataset-edit-begin", "dataset-name-or-id", $dsName );
	if ( $out->results_status() eq "failed" ) {
		print( "Error : " . $out->results_reason() . "\n" );
		exit(-2);
	}
	my $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() eq "failed" ) {
		print( "Error : " . $out->results_reason() . "\n" );
		print("Attempting to roll-back...\n");
		$out = $s->invoke( "dataset-edit-rollback", "edit-lock-id", $editLock );
		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() eq "failed" ) {
		print( "Error : " . $out->results_reason() . "\n" );
		print("Attempting to roll-back...\n");
		$out = $s->invoke( "dataset-edit-rollback", "edit-lock-id", $editLock );
		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() eq "failed" ) {
		print( "Error : " . $out->results_reason() . "\n" );
		print("Attempting to roll-back...\n");
		$out = $s->invoke( "dataset-edit-rollback", "edit-lock-id", $editLock );
		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() eq "failed" ) {
		print( "Error : " . $out->results_reason() . "\n" );
		print("Attempting to roll-back...\n");
		$out = $s->invoke( "dataset-edit-rollback", "edit-lock-id", $editLock );
		exit(-2);
	}
	my $dps = $out->child_get("dp-policy-infos");
	if ( $dps eq "" ) {
		print "Error: No Provisioning Policies!\n";
		print("Attempting to roll-back...\n");
		$out = $s->invoke( "dataset-edit-rollback", "edit-lock-id", $editLock );
		exit;
	}

	my $dpInfo     = $dps->child_get("dp-policy-info");
	my $dpContent  = $dpInfo->child_get("dp-policy-content");
	my $dpNodes    = $dpContent->child_get("dp-policy-nodes");
	my @dpNodeInfo = $dpNodes->children_get("dp-policy-node-info");

	my $count = 1;
	my $rpool = $priRp;
	my $size  = scalar(@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 );
		exit;
	}
	foreach my $dpni (@dpNodeInfo) {
		if ( $count eq 2 ) { $rpool = $secRp; }
		if ( $count eq 3 ) { $rpool = $terRp; }

		my $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() eq "failed" ) {
			print( "Error : " . $out->results_reason() . "\n" );
			print("Attempting to roll-back...\n");
			$out =
			  $s->invoke( "dataset-edit-rollback", "edit-lock-id", $editLock );
			exit(-2);
		}
		$count = $count + 1;
	}

	print "Committing... \n";
	$out = $s->invoke( "dataset-edit-commit", "edit-lock-id", $editLock );
	if ( $out->results_status() eq "failed" ) {
		print( "Error : " . $out->results_reason() . "\n" );
		print("Attempting to roll-back...\n");
		$out = $s->invoke( "dataset-edit-rollback", "edit-lock-id", $editLock );
		exit(-2);
	}
} elsif ( $command eq "member" ) {
	my $subCommand = shift;
	if ( $subCommand eq "list" ) {
		if ( $cmd_args < 6 ) {
			print_usage();
		}
		my $dsName = shift;

		my $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() eq "failed" ) {
			print( "Error : " . $out->results_reason() . "\n" );
			exit(-2);
		}

		my $in = NaElement->new("dataset-member-list-info-iter-next");
		$in->child_add_string( "maximum", $out->child_get_int("records") );
		$in->child_add_string( "tag",     $out->child_get_string("tag") );

		my $out = $s->invoke_elem($in);
		if ( $out->results_status() eq "failed" ) {
			print( "Error : " . $out->results_reason() . "\n" );
			exit(-2);
		}
		print "\nDATASET : " . $dsName . "\n";
		print
"===================================================================\n";
		if ( $out->child_get_int("records") eq 0 ) {
			print "Error: No Members in this Dataset!\n";
			exit -2;
		}
		my $dms  = $out->child_get("dataset-members");
		my @dmis = $dms->children_get("dataset-member-info");
			foreach my $dmi (@dmis) {
			my $member_name = $dmi->child_get_string("member-name");

# Display all member details.  Avoid displaying the non-qtree member i.e members ending with "-"
			if ( substr( $member_name, -1, 1 ) ne "-" ) {
				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";
				my $mtype = $dmi->child_get_string("member-type");
				print "Member Type\t\t: " . $mtype;
				print "\n";

				if ( $mtype ne "qtree" ) {
					my $spinfo = $dmi->child_get("space-info");
					print "Space used\t\t: "
					  . ( $spinfo->child_get_int("used-space") ) . " ("
					  . (
						$spinfo->child_get_int("used-space") / ( 1024 * 1024 ) )
					  . "MB)\n";
					print "Space(Avail/Total)\t: "
					  . ( $spinfo->child_get_int("available-space") /
						  ( 1024 * 1024 ) )
					  . "MB / "
					  . (
						$spinfo->child_get_int("total-space") / ( 1024 * 1024 )
					  ) . "MB";
					print "\n";
				}
				print "\n";
				print
"===================================================================\n";
			}
		}
	} elsif ( $subCommand eq "add" ) {
		if ( $cmd_args < 7 ) {
			print_usage();
		}
		my $dsName = shift;
		my $mem    = shift;

		my $out =
		  $s->invoke( "dataset-edit-begin", "dataset-name-or-id", $dsName );
		if ( $out->results_status() eq "failed" ) {
			print( "Error : " . $out->results_reason() . "\n" );
			exit(-2);
		}
		my $editLock = $out->child_get_int("edit-lock-id");

		my $in = NaElement->new("dataset-add-member");
		$in->child_add_string( "edit-lock-id", $editLock );
		my $dmps = NaElement->new("dataset-member-parameters");
		while ( $mem ne "" ) {
			print "Adding member " . $mem . "...\n";
			my $dmp = NaElement->new("dataset-member-parameter");
			$dmp->child_add_string( "object-name-or-id", $mem );
			$mem = shift;
			$dmps->child_add($dmp);
		}

		$in->child_add($dmps);

		my $out = $s->invoke_elem($in);
		if ( $out->results_status() eq "failed" ) {
			print( "Error : " . $out->results_reason() . "\n" );
			print("Attempting to roll-back...\n");
			$out =
			  $s->invoke( "dataset-edit-rollback", "edit-lock-id", $editLock );
			exit(-2);
		}
		print "Committing... \n";
		$out = $s->invoke( "dataset-edit-commit", "edit-lock-id", $editLock );
		if ( $out->results_status() eq "failed" ) {
			print( "Error : " . $out->results_reason() . "\n" );
			print("Attempting to roll-back...\n");
			$out =
			  $s->invoke( "dataset-edit-rollback", "edit-lock-id", $editLock );
			exit(-2);
		}
		print "Addition of Members to Dataset " . $dsName . " Successful!\n";
	} elsif ( $subCommand eq "del" ) {
		if ( $cmd_args < 7 ) {
			print_usage();
		}
		my $dsName = shift;
		my $mem    = shift;

		my $out =
		  $s->invoke( "dataset-edit-begin", "dataset-name-or-id", $dsName );
		if ( $out->results_status() eq "failed" ) {
			print( "Error : " . $out->results_reason() . "\n" );
			exit(-2);
		}
		my $editLock = $out->child_get_int("edit-lock-id");

		my $in = NaElement->new("dataset-remove-member");
		$in->child_add_string( "edit-lock-id", $editLock );
		my $dmps = NaElement->new("dataset-member-parameters");
		while ( $mem ne "" ) {
			print "Removing member " . $mem . "...\n";
			my $dmp = NaElement->new("dataset-member-parameter");
			$dmp->child_add_string( "object-name-or-id", $mem );
			$mem = shift;
			$dmps->child_add($dmp);
		}
		$in->child_add($dmps);

		my $out = $s->invoke_elem($in);
		if ( $out->results_status() eq "failed" ) {
			print( "Error : " . $out->results_reason() . "\n" );
			print("Attempting to roll-back...\n");
			$out =
			  $s->invoke( "dataset-edit-rollback", "edit-lock-id", $editLock );
			exit(-2);
		}
		print "Committing... \n";
		$out = $s->invoke( "dataset-edit-commit", "edit-lock-id", $editLock );
		if ( $out->results_status() eq "failed" ) {
			print( "Error : " . $out->results_reason() . "\n" );
			print("Attempting to roll-back...\n");
			$out =
			  $s->invoke( "dataset-edit-rollback", "edit-lock-id", $editLock );
			exit(-2);
		}
		print "Removal of Members from Dataset " . $dsName . " Successful!\n";
	} else {
		print "Invalid Option selected...\n";
		print_usage();
	}
} elsif ( $command eq "provision" ) {
	if ( $cmd_args < 7 ) {
		print_usage();
	}

	my $dsName  = shift;
	my $name    = shift;
	my $size    = shift;
	my $ssspace = shift;

	#Determine the provisioning policy attached to the dataset
	my $out =
	  $s->invoke( "dataset-list-info-iter-start", "object-name-or-id",
		$dsName );
	if ( $out->results_status() eq "failed" ) {
		print( "Error : " . $out->results_reason() . "\n" );
		exit(-2);
	}

	my $in = NaElement->new("dataset-list-info-iter-next");
	$in->child_add_string( "maximum", $out->child_get_int("records") );
	$in->child_add_string( "tag",     $out->child_get_string("tag") );

	my $out = $s->invoke_elem($in);
	if ( $out->results_status() eq "failed" ) {
		print( "Error : " . $out->results_reason() . "\n" );
		exit(-2);
	}

	my $rps = $out->child_get("datasets");

	if ( $rps eq "" ) {
		print "Error: No Datasets!\n";
		exit;
	}

	my $rpInfos = $rps->child_get("dataset-info");
	my $provpId = $rpInfos->child_get_string("provisioning-policy-id");
	print "Prov Policy\t: "
	  . $rpInfos->child_get_string("provisioning-policy-name") . "\n";

	my $in = NaElement->new("provisioning-policy-list-iter-start");
	$in->child_add_string( "provisioning-policy-name-or-id", $provpId );
	my $out = $s->invoke_elem($in);
	if ( $out->results_status() eq "failed" ) {
		print( "Error : " . $out->results_reason() . "\n" );
		exit(-2);
	}

	my $in = NaElement->new("provisioning-policy-list-iter-next");
	$in->child_add_string( "maximum", $out->child_get_int("records") );
	$in->child_add_string( "tag",     $out->child_get_string("tag") );

	my $out = $s->invoke_elem($in);
	if ( $out->results_status() eq "failed" ) {
		print( "Error : " . $out->results_reason() . "\n" );
		exit(-2);
	}

	my $pps = $out->child_get("provisioning-policies");
	if ( $pps eq "" ) {
		print "Error: No Provisioning Policies!\n";
		exit;
	}
	my $ppInfos = $pps->child_get("provisioning-policy-info");
	my $pptype  = $ppInfos->child_get_string("provisioning-policy-type");

	my $out = $s->invoke( "dataset-edit-begin", "dataset-name-or-id", $dsName );
	if ( $out->results_status() eq "failed" ) {
		print( "Error : " . $out->results_reason() . "\n" );
		exit(-2);
	}
	my $editLock = $out->child_get_int("edit-lock-id");

	my $in = NaElement->new("dataset-provision-member");
	$in->child_add_string( "edit-lock-id", $editLock );
	my $pmri = NaElement->new("provision-member-request-info");
	$pmri->child_add_string( "name", $name );
	$pmri->child_add_string( "size", $size );

	if ( $pptype eq "san" ) {
		$pmri->child_add_string( "maximum-snapshot-space", $ssspace );
	}
	$in->child_add($pmri);
	print "Provisioning storage...\n";
	my $out = $s->invoke_elem($in);
	if ( $out->results_status() eq "failed" ) {
		print( "Error : " . $out->results_reason() . "\n" );
		exit(-2);
	}

	print "Committing... \n";
	$out = $s->invoke( "dataset-edit-commit", "edit-lock-id", $editLock );
	if ( $out->results_status() eq "failed" ) {
		print( "Error : " . $out->results_reason() . "\n" );
		print("Attempting to roll-back...\n");
		$out = $s->invoke( "dataset-edit-rollback", "edit-lock-id", $editLock );
		exit(-2);
	}

	my $jobId =
	  ( ( $out->child_get("job-ids") )->child_get("job-info") )
	  ->child_get_string("job-id");
	print "Job ID\t\t: " . $jobId . " \n";
	my $jobStatus = "running";
	print "Job Status\t: " . $jobStatus;
	while ( $jobStatus eq "queued" || $jobStatus eq "running" ) {
		my $out = $s->invoke( "dp-job-list-iter-start", "job-id", $jobId );
		if ( $out->results_status() eq "failed" ) {
			print( "Error : " . $out->results_reason() . "\n" );
			exit(-2);
		}
		my $out = $s->invoke(
			"dp-job-list-iter-next",           "maximum",
			$out->child_get_string("records"), "tag",
			$out->child_get_string("tag")
		);
		if ( $out->results_status() eq "failed" ) {
			print( "Error : " . $out->results_reason() . "\n" );
			exit(-2);
		}

		#print $out->sprintf();
		my $dpJobs = $out->child_get("jobs");
		our $dpJobInfo = $dpJobs->child_get("dp-job-info");
		$jobStatus = $dpJobInfo->child_get_string("job-state");
		sleep 5;
		print ".";
		if ( $jobStatus eq "completed" || $jobStatus eq "aborted" ) {
			print "\nOverall Status\t: "
			  . $dpJobInfo->child_get_string("job-overall-status") . "\n";
		}
	}

	my $out =
	  $s->invoke( "dp-job-progress-event-list-iter-start", "job-id", $jobId );
	if ( $out->results_status() eq "failed" ) {
		print( "Error : " . $out->results_reason() . "\n" );
		exit(-2);
	}
	my $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() eq "failed" ) {
		print( "Error : " . $out->results_reason() . "\n" );
		exit(-2);
	}
	my $progEvnts     = $out->child_get("progress-events");
	my @progEvntsInfo = $progEvnts->children_get("dp-job-progress-event-info");
	print "\nProvision Details:\n";
	print "=" x 19 . "\n";
	foreach my $evnt (@progEvntsInfo) {
		if ( $evnt->child_get_string("event-type") ne "" ) {
			print $evnt->child_get_string("event-type");
		}
		print "\t: " . $evnt->child_get_string("event-message") . "\n";
	}
} else {
	print "Invalid Option...\n";
	print_usage();
}
