# 
#
# acfsremote.pl
# 
# Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved.
#
#    NAME
#      acfsremote.pl - <one-line expansion of the name>
#
#    DESCRIPTION
#      <short description of component this file declares/defines>
#
#    NOTES
#      <other useful comments, qualifications, etc.>
#
# 
#
#

use strict;
use Getopt::Long;
Getopt::Long::Configure(qw(posix_default auto_abbrev no_ignore_case));
use osds_acfsremote;
use acfslib;
use Data::Dumper;
use osds_acfslib;
require Exporter;
use Config;
my ($optc);
my $trace;
my $domain;
my $member;
my $nodeid;
my $help;
                                                                                
sub main                                                                        
{                                                                               
  my $rc;                                                                       
  my $operation = "";                                                           
  # user flags. See description above or usage message output                   
  my (%flags) = (                                                               
                  transport_config => {"rescan=s"  => \my $rescan,       
                                       "refresh=s" => \my $refresh,             
                                       "help"      => \$help,
                                       "trace=i"   => \$trace},                 
                                       
                  transport_list   => {"help"      => \$help,
                                       "trace=i"   => \$trace},

                  transport_change => {"add"       => \my $add,        
                                       "export"    => \my $export,             
                                       "remove"    => \my $remove,            
                                       "rmexport"  => \my $delete,            
                                       "dsf=s"     => \my $dsf,               
			 	                               "transid=s" => \$nodeid,
                                       "guid=s"    => \my $guid,
                                       "help"      => \$help,
                                       "trace=i"   => \$trace}, 
                  supported        => 
                                      {"domain"    => \$domain,
                                       "member"    => \$member,
                                       "help"      => \$help,
                                       "trace=i"   => \$trace},
                  loaded => 
                                      {"domain"    => \$domain,
                                       "member"    => \$member,
                                       "help"      => \$help,
                                       "trace=i"   => \$trace},
                  installed => 
                                      {"domain"    => \$domain,
                                       "member"    => \$member,
                                       "help"      => \$help,
                                       "trace=i"   => \$trace},
                );
  
  my $COMMAND;
  my $sub_command;

  $COMMAND = shift(@ARGV);     # aka 'acfsremote'
  $sub_command = shift(@ARGV); # aka one of %flags
  
  if ( $Config{osname} ne 'linux')
  {
      lib_inform_print(9999,"This command is not supported in this platform");
      acfsremote_exit(USM_FAIL);
  }

  if (!defined($sub_command))
  {
    usage("", 0);
    acfsremote_exit(USM_FAIL);
  }

  GetOptions(%{$flags{$sub_command}}) or usage($sub_command, 1);

  unless( exists $flags{$sub_command} )
  {
    usage("invalid", 0);
    acfsremote_exit(USM_FAIL);
  }

  if ($help)
  {
    usage($sub_command, 0);
    acfsremote_exit(USM_FAIL);
  }

  if(defined($trace))
  {
    $acfslib::_ORA_USM_TRACE_LEVEL = $trace;
    if($trace > 0)
    {
        $_ORA_USM_TRACE_ENABLED = 1;
    }
  }


  if(defined($domain) || defined($member))
  {

      my $cluster_class;
      if(defined($domain)) 
      {
          $cluster_class = "DOMAIN SERVICES";
      }
      elsif(defined($member))
      {
          $cluster_class = "MEMBER";
      }
      if($sub_command eq "supported")
      { 
         
        my $mode_query = "$ACFSUTIL cluster info";
        my $mode_match = 0;
        my $override = 0;

        # Check the acfs remote mode using acfsutil cluster info.
        # Check drivers are loaded instead of installed.
        # Drivers may be installed and not loaded during an incorrect
        # setup so we want to make sure we can execute acfsutil
        # There are 4 different scenarios to consider:
        # 1. The mode we are checking is the mode installed.
        # 2. The mode we are checking is not the mode installed.
        # 3. ACFS itself is not installed.
        # 4. ACFS Remote is OFF.   
        # For case 3, we can't even call acfsutil so check that first
        if (lib_check_drivers_loaded())
        {
            # Include & this way the command is executed properly on all shells
            my @output = qx{$mode_query 2>&1};

            for (@output)
            {
                if(/ACFS Remote mode: \[(.*)\]/)
                {
                    my $output_mode = $1;
                    if ($cluster_class =~ $output_mode)
                    {
                        # Case 1. The mode installed matches the mode being 
                        # checked. I guess it is kind of redundant to be 
                        # calling this command if it is already installed.  
                        $mode_match = 1;
                    }
                    elsif('OFF' =~ $output_mode)
                    {
                        # Case 4.
                        # If acfs remote is not setup, don't make this mode 
                        # match check factor in whether the specified mode 
                        # is supported.
                        $mode_match = 1;
                    }
                }
            }
        }
        else
        {
            # Drivers not installed, don't consider the installed mode to check
            # if the specified acfs remote mode is supported.
            $mode_match = 1;
        }

        # If at this point mode_match is still 0, that means we have case 2. 
        # That means we should not be reporting it is supported if there is
        # alredy a different acfs remote mode installed.     
        # $override to report supported if in single node mode. 
        if( $override == 1                        ||
            (lib_usm_supported()                  &&
             osds_acfsr_supported($cluster_class) &&
             $mode_match) ) 
        {
          lib_inform_print(9840,
                           "ACFS Remote supported: %s",
                           "True");
          lib_inform_print(9841,
                           "iSCSI supported: %s",
                           $acfslib::acfsr{'iSCSI'});
          #lib_inform_print(9842,
          #                 "xen blkfrnt/blkback supported: %s",
          #                 "True");
          $rc = USM_SUCCESS;
        }
        else
        {
          lib_inform_print(9840,
                           "ACFS Remote supported: %s",
                           "False");  
          $rc = USM_FAIL;
        }
      }
      elsif($sub_command eq "installed")
      { 
        if( lib_check_drivers_installed() &&
            osds_acfsr_installed($cluster_class) )           
        {
          lib_inform_print(9843,
                           "ACFS Remote installed: %s",
                           "True");
          lib_inform_print(9844,
                           "iSCSI installed: %s",
                           $acfslib::acfsr{'iSCSI'});
          $rc = USM_SUCCESS;
        }
        else
        {
          lib_inform_print(9843,
                           "ACFS Remote installed: %s",
                           "False");  
          $rc = USM_FAIL;
        }
      }
      elsif($sub_command eq "loaded")
      { 
        if ( lib_check_drivers_loaded() &&
             osds_acfsr_loaded($cluster_class) )
        {
          lib_inform_print(9846,
                           "ACFS Remote loaded: %s",
                           "True");
          lib_inform_print(9847,
                           "iSCSI loaded: %s",
                           $acfslib::acfsr{'iSCSI'});
          #lib_inform_print(9848,
          #                 "xen blkfrnt/blkback loaded: %s",
          #                 "True");
          $rc = USM_SUCCESS;
        }
        else
        {
          lib_inform_print(9846,
                           "ACFS Remote loaded: %s",
                           "False");  
          $rc = USM_FAIL;
        }
        
      }
      acfsremote_exit($rc);
  }

  # verify root access 
  if (!lib_am_root())
  {
    lib_error_print(9130, "Root access required");
    acfsremote_exit(USM_FAIL);
  }

  # transport config 
  if($sub_command eq 'transport_config')
  {

      my $transport;

      if(defined($rescan) && !defined($refresh))
      {
        $operation = "rescan";
        $transport = $rescan;
      }

      if(!defined($rescan) && defined($refresh))
      {
        $operation = "refresh";
        $transport = $refresh;
      }

      if($operation ne "")
      {
          $rc = osds_acfsr_transport_config($operation, $transport);
          acfsremote_exit($rc);
      }
      else
      {
        usage("transport_config", 0);
        acfsremote_exit(USM_FAIL);
      }
  }

  # transport list
  if($sub_command eq 'transport_list')
  {
      $rc = osds_acfsr_transport_list();
      acfsremote_exit($rc);
  }

  # transport change
  if($sub_command eq 'transport_change')
  {

      lib_trace(9999,"Enter transport_change");
      if(!defined($dsf))
      {
          usage("transport_change",0);
          acfsremote_exit(USM_FAIL);
      }
      lib_trace(9999,"checking option combinations");

      # Only one can be defined
      if( !( defined($add)     xor
             defined($export)  xor
             defined($remove)  xor
             defined($delete)))
      {
          usage("transport_change",0);
          acfsremote_exit(USM_FAIL);
      }

      # Add the node ID to the ACL of the specified DSF target
      if( defined($add) && $nodeid ne '' ) 
      {
        $rc = osds_acfsr_transport_change_add_acl($dsf,$nodeid,$guid);
      }
      # Remove the ACL from the specified DSF target  
      elsif( defined($remove) && $nodeid ne '' ) 
      {
        $rc = osds_acfsr_transport_change_remove_acl($dsf,$nodeid,$guid);
      }
      # Setup the specified DSF in the transport    
      elsif( defined($export) && defined($guid) ) 
      {
        $rc = osds_acfsr_transport_change_setup_target($guid,$dsf,$nodeid);
      }
      # Delete the target for the DSF  
      elsif( defined($delete) && defined($guid) )
      {
        $rc = osds_acfsr_transport_change_delete_target($guid,$dsf,$nodeid);
      }
      else
      {
          usage("transport_change",0);
          acfsremote_exit(USM_FAIL);
      }
      lib_trace(9999,"Exit transport_change");

      acfsremote_exit($rc);

  }

  # should never get here.
  usage("invalid", 0);
  acfsremote_exit(USM_FAIL);

}

sub acfsremote_exit
{
  my ($ret) = shift @_;
  lib_trace( 9176, "Entering '%s'", "acfsremote exit");
  lib_trace( 9178, "Return code = %s", "$ret");
  lib_trace( 9177, "Return from '%s'", "acfsremote exit");
  exit $ret;

}

sub usage
{
  my ($sub_command, $abort) = @_;
  
  lib_trace( 9176, "Entering '%s'", "usage");

  if ($sub_command eq 'supported' || 
      $sub_command eq 'installed' ||
      $sub_command eq 'loaded'
      )
  {
    if($sub_command eq 'supported')
    {
        lib_error_print_noalert(9867,"acfsremote supported: Check if ACFS Remote is supported.");
    }
    elsif($sub_command eq 'installed')
    {
        lib_error_print_noalert(9868,"acfsremote installed: Check if ACFS Remote is installed.");
    }
    elsif($sub_command eq 'loaded')
    {
        lib_error_print_noalert(9869,"acfsremote loaded: Check if ACFS Remote is loaded.");
    }
    lib_error_print_noalert(9837,"  --trace <level>: Trace level");
    lib_error_print_noalert(9838,"  --member: Member Cluster check");
    lib_error_print_noalert(9839,"  --domain: Domain Services Cluster check");
  }
  elsif ($sub_command eq 'transport_config')
  {
    lib_error_print_noalert(9850,"acfsremote transport_config: Configure ACFS Remote transport.");
    lib_error_print_noalert(9837,"  --trace <level>: Trace level");
    lib_error_print_noalert(9853,"  --rescan  <transport>: Rescan for new transports");
    lib_error_print_noalert(9854,"  --refresh <transport>: Refresh existing transports");

  }
  elsif ($sub_command eq 'transport_list')
  {
    lib_error_print_noalert(9851,"acfsremote transport_list: List ACFS Remote transports.");
    lib_error_print_noalert(9837,"  --trace <level>: Trace level");
  }
  elsif ($sub_command eq 'transport_change')
  {
    lib_error_print_noalert(9852,"acfsremote transport_change: Modify transport configuration.");
    lib_error_print_noalert(9837,"  --trace <level>: Trace level");
    lib_error_print_noalert(9855,"  --add      --dsf=<dsf> --transid=<id>: Add the node ID to the ACL of the specified DSF target");
    lib_error_print_noalert(9856,"  --remove   --dsf=<dsf> --transid=<id>: Remove the node ID from the ACL of the specified DSF target");
    lib_error_print_noalert(9857,"  --export   --dsf=<dsf> --guid=<guid>: Setup the specified DSF in the transport");
    lib_error_print_noalert(9858,"  --rmexport --dsf=<dsf> --guid=<guid>: Delete the target for the DSF");
    lib_error_print_noalert(9864,"    --dsf:     Path to Device Special File");
    lib_error_print_noalert(9865,"    --guid:    Cluster Global Unique Identifier (GUID)");
    lib_error_print_noalert(9866,"    --transid: Transport ID");
  }
  else
  {
    usage("supported","");
    usage("installed","");
    usage("loaded","");
    usage("transport_config","");
    usage("transport_list","");
    usage("transport_change","");
  }

}

main();
