#!/usr/bin/env perl
# $Copyright: Copyright (c) 2022 Veritas Technologies LLC. All rights reserved $
#
# This is a perl script that allows users to manipulate the json file that
# NetBackup creates and uses for restore of Individual disks of a virtual
# machine. 
#
# Main parameter: -req_type
#
# Operations include:
# 1. modify       :- Replace the value for any field specified by the user
#                    Required parameters: -file_path, -field, -value.(see example usage 1)      
#                    Valid fields are:
#                        1. ClientName
#                        2. StartDate
#                        3. EndDate
#                        4. BackupId
#                        5. vCenterServer
#                        6. VMwareRecoveryHost
#                        7. DefaultDiskProvisioning
#                        8. TransportMode
#                        9. VMName
#                        10. Datacenter
#                        11. ESX
#                        12. Folder
#                        13. ResourcePool/Vapp
#                        14. VmxDatastore
#                        15. AttachDisksToExistingVM
#                        16. PowerOn
#                        17. DeleteRestoredVMOnError
#                        18. VMShutdownWaitSeconds
#                        19. Datastore
#                        20. Path
#                        21. Provisioning
#                        22. OverwriteExistingDisk
#                   
#                    Note : if modifying these fields :-(Datastore|Path|Provisioning
#                           |OverwriteExistingDisk) -controller parameter is required as well.
#                           (see example usage 2)
#                                           
# 2. keep_disks   :- Keep the disks specified by the user and delete all the other disks
#                    from the json file.
#                    Required parameters: -file_path, -controller (comma separated list)
#                    See example usage 3
# 3. delete_disks :- Delete the disks specified by the user and keep all the other disks
#                    in the json file. 
#                    Required parameters: -file_path, -controller (comma separated list)
#                    See example usage 4    
# 
# Example Usage:
#   1. restore_spec_utility.pl -req_type modify -file_path /usr/example.json -field VMName -value new_name 
#   2. restore_spec_utility.pl -req_type modify -file_path C:\example.json -field Datastore -value new_datastore -controller scsi0-4
#   3. restore_spec_utility.pl -req_type delete_disks -file_path /usr/example.json -controller scsi0-0,scsi0-1
#   4. restore_spec_utility.pl -req_type keep_disks -file_path C:\example.json -controller scsi0-2,scsi0-3  
#  
# ----------------------------------------------------------------------------
# Pragmas
use strict;
use warnings;
use JSON;
use Path::Tiny qw(path );
use Getopt::Long qw(GetOptions);
use Encode;

my ($req_type, $file_path, $field, $value, @all_controllers, $controller, @controllers_in_json);

my @all_fields = qw(ClientName StartDate EndDate BackupId vCenterServer
                       VMwareRecoveryHost DefaultDiskProvisioning TransportMode VMName 
                       Datacenter ESX Folder ResourcePool/Vapp VmxDatastore 
                       AttachDisksToExistingVM PowerOn DeleteRestoredVMOnError 
                       VMShutdownWaitSeconds Datastore Path Provisioning OverwriteExistingDisk);
							  
if ($^O =~ m/linux/){
   umask 022;
}

GetOptions('req_type=s'	 => \$req_type,
			'file_path=s'	 => \$file_path,
			'field:s'	    => \$field,
			'value:s'	    => \$value,
			'controller:s'  => \@all_controllers);
                
@all_controllers = split(/,/,join(',',@all_controllers));
	
if ((not defined $req_type) || (not defined $file_path)){
   die "Need both the required parameters -req_type and -file_path\n";
}

if (($req_type ne "modify") && ($req_type ne "delete_disks") && ($req_type ne "keep_disks")){
   die "Invalid request type. Valid options are :- modify, delete_disks, keep_disks\n";
}
 	
if (($req_type =~ "modify") && (!$field || !$value || !$file_path)){
   die "-field, -value and -file_path parameteres are required for the request type [$req_type]\n";
}

if((($req_type =~ "modify" && $field =~ /^(Datastore|Path|Provisioning|OverwriteExistingDisk)$/ ) && !@all_controllers))	{
   die "-controller parameter is required for the request type [$req_type]\n";
}
  
if(($req_type =~ "delete_disks") && (!$file_path || !@all_controllers)) {
   die "-file_path and -controller parameters are both required for the request type [$req_type]\n";
}

if(($req_type =~ "keep_disks") && (!$file_path || !@all_controllers)) {
   die "-file_path and -controller parameters are both required for the request type [$req_type]\n";
} 
 
my $data  = path($file_path)->slurp_utf8;  #read the file in slurp mode
my $data_decoded = decode_json(encode("utf8", $data)); #convert the json file into perl datastructures

# Read all the controllers from the json file to validate the controller input from the user
my $virtual_disk_destination =  $data_decoded->{VMwareRestoreParameters}->{VMwareVirtualDiskDestination};

if (ref($virtual_disk_destination) eq "ARRAY") {       
   my @array = @{$virtual_disk_destination};
   my $length = scalar @array;         
   my $i;
   for($i = 0; $i < $length; $i++){            
      push (@controllers_in_json, $data_decoded->{VMwareRestoreParameters}->{VMwareVirtualDiskDestination}[$i]{Controller} );                   
   }
}

# Validate the controller is present in the json file
foreach my $controller (@all_controllers){
   if (!grep(/^$controller$/, @controllers_in_json)){
      die "[$controller] is not present in the json file. Enter a valid controller.\n";
   }
}
  
if ($req_type =~ "modify") {
   
   print "Request type is modify\n";
   
   # Verify the field specified is valid
   if (not grep( /^$field$/, @all_fields)){
           
      die "Field parameter [$field] is not recognized as "
         . "a valid parameter. (valid fields are: ClientName|StartDate|EndDate"
         . "|BackupId|vCenterServer|VMwareRecoveryHost|DefaultDiskProvisioning|TransportMode|VMName"
         . "|Datacenter|ESX|Folder|ResourcePool/Vapp|VmxDatastore|AttachDisksToExistingVM|PowerOn"
         . "|DeleteRestoredVMOnError|VMShutdownWaitSeconds|Datastore|Path|Provisioning"
         . "|OverwriteExistingDisk)\n";
   }
    
   print "The field to modify is [$field]\n";
   print "The value to be replaced with is [$value]\n";
       
   if ($field =~ /^(ClientName)$/){
      $data_decoded->{$field} = $value ; # replace the value
   }
    
   if ($field =~ /^(StartDate|EndDate|BackupId)$/){
      $data_decoded->{BackupImageSelection}->{$field} = $value; # replace the value    
   }
    
   if ($field =~ /^(vCenterServer|VMwareRecoveryHost|DefaultDiskProvisioning|TransportMode)$/){
      $data_decoded->{VMwareRestoreParameters}->{$field} = $value; # replace the value    
   }
          
   if ($field =~ /^(VMName|Datacenter|ESX|Folder|(ResourcePool\/Vapp)|VmxDatastore|PowerOn)$/){
      $data_decoded->{VMwareRestoreParameters}->{VMwareVirtualMachineDestination}->{$field} = $value;
   }
   
   if ($field =~ /^(AttachDisksToExistingVM)$/){
      $data_decoded->{VMwareRestoreParameters}->{VMwareVirtualMachineDestination}->{$field} = $value;
      if ($value =~ /^(DeleteAllDisksAndReplace)$/){
         delete $data_decoded->{VMwareRestoreParameters}->{VMwareVirtualDiskDestination};
         print "Deleted all the disks from the json file\n";
      }
   }

   if ($field =~ /^(DeleteRestoredVMOnError|VMShutdownWaitSeconds)$/){
      $data_decoded->{VMwareRestoreParameters}->{VMwareAdvancedRestoreOptions}->{$field } = $value;
   }   
    
   if ($field =~ /^(Datastore|Path|Provisioning|OverwriteExistingDisk)$/){
   
      if (scalar @all_controllers != 1){
         die "Specify exactly one controller whose [$field] needs to be modified.\n"
      }
      else {
         $controller = $all_controllers[0];
      }
      print "The controller whose disk is to be modified is [$controller]\n";
      my $virtual_disk_destination =  $data_decoded->{VMwareRestoreParameters}->{VMwareVirtualDiskDestination};
      
      if (ref($virtual_disk_destination) eq "ARRAY") {       
         my @array = @{$virtual_disk_destination};
         my $length = scalar @array;         
         my $i;
         for($i = 0; $i < $length; $i++){
            
            if ($data_decoded->{VMwareRestoreParameters}->{VMwareVirtualDiskDestination}[$i]{Controller} eq $controller ) {
                  # replace the value                                      
               $data_decoded->{VMwareRestoreParameters}->{VMwareVirtualDiskDestination}[$i]{$field} = $value; 
               last;
            }
         }
      }
   }       
   print "The field [$field] has been modified with value [$value]\n";
 
} elsif ($req_type =~ "delete_disks") {  # Handle delete_disks request type
     
   print "Request type is delete disks\n";               
 
   #get the array reference of hashes of disks
   my $virtual_disk_destination =  $data_decoded->{VMwareRestoreParameters}->{VMwareVirtualDiskDestination}; 
     
   if (ref($virtual_disk_destination) eq "ARRAY") {       
      my @array = @{$virtual_disk_destination}; # convert array ref into an array
      my $length = scalar @array;  #get the length of the array         
      my $i;
      my @array_new;
      for($i = 0; $i < $length; $i++){
         my $delete = 0;
         foreach my $controller (@all_controllers){
            if ($data_decoded->{VMwareRestoreParameters}->{VMwareVirtualDiskDestination}[$i]{Controller} eq $controller ) {
               $delete = 1; 
               print "The disk associated with controller [$controller] is deleted from the json file\n"; 
               next; #exit the loop when the disk matches                              
            }           
         }         
         #create a new array of hash of only the disks that aren't to be deleted       
         push (@array_new, $data_decoded->{VMwareRestoreParameters}->{VMwareVirtualDiskDestination}[$i]) if !$delete;
      }       
      $virtual_disk_destination = \@array_new; 
      $data_decoded->{VMwareRestoreParameters}->{VMwareVirtualDiskDestination} = $virtual_disk_destination;              
   }	 
   
} elsif ($req_type =~ "keep_disks") {  # Handle keep_disks request type
     
   print "Request type is keep disks\n";               
         
   #get the array reference of hashes of disks
   my $virtual_disk_destination =  $data_decoded->{VMwareRestoreParameters}->{VMwareVirtualDiskDestination}; 
     
   if (ref($virtual_disk_destination) eq "ARRAY") {       
      my @array = @{$virtual_disk_destination}; # convert array ref into an array
      my $length = scalar @array;  #get the length of the array         
      my $i;
      my @array_new;
      for($i = 0; $i < $length; $i++){
         my $keep = 0;
         foreach my $controller (@all_controllers){
            if ($data_decoded->{VMwareRestoreParameters}->{VMwareVirtualDiskDestination}[$i]{Controller} eq $controller ) {
               $keep = 1;
               print "The disk associated with controller [$controller] is kept in the json file\n";
               next; #exit the loop when the disk matches                               
            }           
         }         
         #create a new array of hash of only the disks that are to be kept       
         push (@array_new, $data_decoded->{VMwareRestoreParameters}->{VMwareVirtualDiskDestination}[$i]) if $keep;
      }       
      $virtual_disk_destination = \@array_new; 
      $data_decoded->{VMwareRestoreParameters}->{VMwareVirtualDiskDestination} = $virtual_disk_destination;              
   }	  		  
}
   
my $data_encoded = JSON->new->pretty->encode($data_decoded); # convert the hash into a json format
path($file_path) -> spew_utf8($data_encoded); #write the content to the file
print "The operation [$req_type] was completed successfully\n";

exit 0;  


