MINI MINI MANI MO

Path : /opt/oracle/product/18c/dbhomeXE/lib/
File Upload :
Current File : //opt/oracle/product/18c/dbhomeXE/lib/osds_acfsremote.pm

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

use strict;
package osds_acfsremote;
use English;
use File::Copy;
use acfslib;
use osds_acfslib;
use osds_unix_linux_acfslib;
use osds_acfsroot;
use POSIX qw(strftime);
use Socket;

our @ISA = qw(Exporter);
our @EXPORT = qw(
                 osds_acfsr_transport_config
                 osds_acfsr_transport_list
                 osds_acfsr_transport_change_add_acl
                 osds_acfsr_transport_change_remove_acl
                 osds_acfsr_transport_change_setup_target
                 osds_acfsr_transport_change_delete_target
                 osds_acfsr_supported
                 osds_acfsr_installed
                 osds_acfsr_loaded
                );

my $d = 3;
my $log_path = "/usr/tmp/acfsr_domain.log";
my $log_fh;
my $log_valid = 0;
my $iqn_pre="iqn.2015-12.com.oracle\\:acfsr";
my ($UNAME_R) = `uname -r`;
my ($ORACLE_HOME) = $ENV{ORACLE_HOME};
chomp ($UNAME_R);


#   Transport rescan
#
#        1. Description
#
#        This functionality will sync up the logged in sessions with the
#        available targets from the DC node. 
#
#        2. Workflow
#
#        Get existing logged in acfs remote iSCSI targets using
# 
#           'iscsiadm -m session'
#
#        Get available targets using iSCSI scan command.
#
#        FOR EACH logged in session
#            Scan available target list
#            if NOT FOUND
#                Logout of session
#
#        For EACH available target
#            Scan session targets
#            if NOT FOUND
#                Login to target using iSCSI login command 
sub osds_acfsr_transport_rescan
{
    # Will look like nshga2601.iSCSI.0
    my $current_transport = shift;
    # Strip away the hostname
    my $symlink_device = "";
    
    if($current_transport =~ /\w+\.(\w+\.\w+)/)
    {
        $symlink_device = $1;
    }

    # Will be an IP address
    my $DSC_IP = shift;
    my ($rc,@out);

    lib_trace(9999,"Enter transport_config --rescan");

    my ($logged_rc,@logged_entries);
    my $logged_entry;

    my ($discovered_rc,@discovered_entries);
    my $discovered_entry;
    
    lib_trace(9999,"Retrieve logged in sessions");
    ($logged_rc,@logged_entries) = 
        execute_command('/sbin/iscsiadm -m session');

    if($logged_rc > 0)
    {
        lib_trace(9999,"Error retrieving logged in sessions");
    }
# Logged entries will look like
# tcp: [4] 10.137.12.155:3260,1 iqn.2015-12.com.oracle:acfsr:000000:1:vol-000
# (non-flash)
    foreach (@logged_entries) 
    {
        lib_trace(9999,$_);
    }

    lib_trace(9999,"Retrieve discovered devices in Domain Services " . 
                   "Cluster @ $DSC_IP");
    
    ($discovered_rc,@discovered_entries) = 
        execute_command("/sbin/iscsiadm -m discovery " . 
                        "-t st -p $DSC_IP ");
# Discovered entries will look like
# 10.137.12.155:3260,1 iqn.2015-12.com.oracle:acfsr:000000:1:vol-000
    if($discovered_rc > 0)
    {
        lib_trace(9999,"Error retrieving available transports in $DSC_IP");
        $rc = 1;
        goto done;
    }

    foreach (@discovered_entries) 
    {
        lib_trace(9999,$_);
    }

    if(-e "/dev/acfsr/$symlink_device")
    {
    
        my @devices;
        # translate $current_transport into an entry format to find a match
        # Use find to get the devices under a given transport (in
        # $symlink_device). Readlink is then used to resolve the symlink 
        # we expect to find to the actual device under /dev/sd*.
        ($rc, @devices) = execute_command ("/bin/find -P /dev/acfsr/$symlink_device -maxdepth 1 -type l -exec /bin/readlink -f {} \\;");
        
        # @devices should contain entries like:
        #  /dev/sda 
        foreach (@devices)
        {
            my $current_device = $_;
            # Strip the /dev/ part
            $current_device =~ s/\/dev\///;
            chomp($current_device);

            ($rc,@out) = execute_command("ls /sys/block/$current_device/device/scsi_device/");
            # the output should be a single bus entry
            # 9:0:0:0 
            if($rc > 0)
            {
                lib_trace(9999,"Failed to list the bus entry for " .
                          "$current_device");
                goto done;
            }

            # Use acfsr_member to get device iqn.
            chomp($out[0]);
            ($rc, @out) = execute_command("/usr/lib/udev/acfsr_member " . 
                                          "$out[0] -I");
            my $iqn = $out[0];
            # $out[0] should contain something like
            #  iqn.2015-12.com.oracle:acfsr:000000:1:vol-000
            # Log out any entries that may have become stale if they are not
            # found in the DSC portal discovered entries. 
            for $logged_entry (@logged_entries)
            {
                my $logged_entry_short;
                my @logged_entry_split = split ' ',$logged_entry;
                $logged_entry_short = $logged_entry_split[2] . " " .
                                      $logged_entry_split[3];
                # A logged entry should match a discovered entry
                # $logged_entry should look like:
                # tcp: [4] 10.137.12.155:3260,1 iqn.2015-12.com.oracle:acfsr:000000:1:vol-000 (non-flash)
                # $logged_entry_short should look like 
                # 10.137.12.155:3260,1 iqn.2015-12.com.oracle:acfsr:000000:1:vol-000
                # which will then match the format of discovered_entries
                unless (grep { /$logged_entry_short/ } @discovered_entries)
                {
                    my $target;
                    if($logged_entry =~ /.*($iqn).*/)
                    {
                        $target = $1;
                        # log out
                        lib_trace(9999,"Log out $target");
                        ($rc,@out) = execute_command("iscsiadm -m node -T " . 
                                                     "$iqn -u");
                    }
                }
                        
            }
        }
        ($discovered_rc,@discovered_entries) = 
        execute_command("/sbin/iscsiadm -m discovery " . 
                        "-t st -p $DSC_IP ");
        for $discovered_entry (@discovered_entries)
        {
            lib_trace(9999,"Looking for $discovered_entry in " .
                      "@logged_entries");
            unless(grep { /$discovered_entry/ } @logged_entries)
            {
                my $target;
                if($discovered_entry =~ / ($iqn_pre\S+)/)
                {
                    $target = $1;
                    lib_trace(9999,"Log in $target");
                    ($rc, @out) = execute_command("iscsiadm -m node " . 
                                                  "-p $DSC_IP " . 
                                                  " -T $target -l");
                    lib_trace(9999,"rc = $rc");
                    lib_trace(9999,"Stop $target from auto login on boot");
                    ($rc, @out) = execute_command("iscsiadm -m node " . 
                                                  " -T $target " . 
                                                  " -o update " .
                                                  " -n node.startup " .
                                                  " -v manual" ); 
                    lib_trace(9999,"rc = $rc");
                }
            }
        }
        $rc = 0;
    }
    else
    {
        lib_trace(9999,"No sessions to operate on.");
        $rc = 0;
    }

done:

    lib_trace(9999,"Exit transport_config --rescan");

    return $rc;

}

#    Transport refresh
#
#        1. Description
#
#        This functionality will logout of any existing ACFS remote sessions
#        and attempt to login to an available iSCSI targets.  One area where
#        this will used is if a MC node needs to reconnect to the VIP because
#        of DC node failover. This is a reinitilization of the entire Transport
#        Session - every underlying ISCSI session associated with it will be
#        paused and affected.
#
#        2. Workflow
#
#        For each acfs remote session
#        
#            logout 
#
#        Run iSCSI scan command
#
#        For each acfs remote session target returned by iscsiadm -m node
#            
#            login with iSCSI login command
sub osds_acfsr_transport_refresh
{
    lib_trace(9999,"Enter transport_config --refresh"); 

    my $current_transport = shift;
    # Strip away the hostname
    my $symlink_device = "";
    
    if($current_transport =~ /\w+\.(\w+\.\w+)/)
    {
        $symlink_device = $1;
    }
    my $DSC_IP = shift;
    my ($rc,@out);
    my $output_entry;
    my ($logged_rc,@logged_entries);
    my $logged_entry;
    my @devices;

    my ($discovered_rc,@discovered_entries);
    my $discovered_entry;
    
    lib_trace(9999,"Retrieve logged in sessions");
    ($logged_rc,@logged_entries) = 
        execute_command('/sbin/iscsiadm -m session');

# Logged entries will look like
# tcp: [4] 10.137.12.155:3260,1 iqn.2015-12.com.oracle:acfsr:000000:1:vol-000
# (non-flash)
    foreach (@logged_entries)
    {
        lib_trace(9999,$_);
    }
    if($logged_rc > 0)
    {
        lib_trace(9999,"Error retrieving logged in sessions");
    }

    lib_trace(9999,"Retrieve discovered devices in Domain Services " . 
                   "Cluster @ $DSC_IP");
    
    ($discovered_rc,@discovered_entries) = 
        execute_command("/sbin/iscsiadm -m discovery -t st -p $DSC_IP");
# Discovered entries will look like
# 10.137.12.155:3260,1 iqn.2015-12.com.oracle:acfsr:000000:1:vol-000
    if($discovered_rc > 0)
    {
        lib_trace(9999,"Error retrieving available transports in $DSC_IP");
        $rc = 1;
        goto done;
    }

    foreach (@discovered_entries)
    {
        lib_trace(9999,$_);
    }

    # translate $current_transport into an entry format to find a match
    if(-e "/dev/acfsr/$symlink_device")
    {
        ($rc, @devices) = execute_command ("/bin/find -P /dev/acfsr/$symlink_device  -maxdepth 1 -type l -exec /bin/readlink -f {} \\;");
        
        # @devices should contain entries like:
        #  /dev/sda 
        foreach (@devices)
        {
            my $current_device = $_;
            # Strip the /dev/ part
            $current_device =~ s/\/dev\///;
            chomp($current_device);

            ($rc,@out) = execute_command("ls /sys/block/$current_device/device/scsi_device/");
            # the output should be a single bus entry
            # 9:0:0:0 
            if($rc > 0)
            {
                lib_trace(9999,"Failed to list the bus entry for " .
                          "$current_device");
                goto done;
            }

            # Use acfsr_member to get device iqn.
            chomp($out[0]);
            ($rc, @out) = execute_command("/usr/lib/udev/acfsr_member $out[0] -I");
            # $out[0] should contain something like
            #  iqn.2015-12.com.oracle:acfsr:000000:1:vol-000
            my $iqn = $out[0];
            # Log out any entries that may have become stale if they are not found
            # in the DSC portal discovered entries. 


            for $output_entry (@logged_entries)
            {
                my $target;

                if($output_entry =~ /.*($iqn).*/)
                {
                    $target = $1;
                    # log out
                    lib_trace(9999,"Log out $target");
                    ($rc,@out) = execute_command("iscsiadm -m node -T " . 
                                                    "$target -u");
                    lib_trace(9999,"rc = $rc");
                }
            }

            lib_trace(9862,"Scanning iSCSI devices in Domain Services " . 
                      "Cluster %s ","$DSC_IP");

            for $output_entry (@discovered_entries)
            {
                my $target;
                if($output_entry =~ /.*($iqn).*/)
                {
                    $target = $1;
                    lib_trace(9863,"Logging in %s", $target);
                    ($rc,@out) = execute_command("iscsiadm -m node -p " . 
                                                 "$DSC_IP -T $iqn -l");
                    lib_trace(9999,"rc = $rc");
                    lib_trace(9999,"Stop $iqn from auto login on boot");
                    ($rc, @out) = execute_command("iscsiadm -m node " . 
                                                  " -T $iqn " . 
                                                  " -o update " .
                                                  " -n node.startup " .
                                                  " -v manual" ); 
                    lib_trace(9999,"rc = $rc");
                }
                    
            }
        }

    }
    else
    {
        # No active sessions. Just login.
        lib_trace(9999,"No active sessions found, login discovered entries");
        for $output_entry (@discovered_entries)
        {
            lib_trace(9999, $output_entry);
            my $iqn;
            if($output_entry =~ /.*(iqn.*)/)
            {
                $iqn = $1;
                lib_trace(9863,"Logging in %s", $iqn);
                my $command = "iscsiadm -m node -p $DSC_IP -T $iqn -l ";
                lib_trace(9999, "Executing '$command'");
                ($rc,@out) = execute_command($command);
                lib_trace(9999,"return code = $rc");
                lib_trace(9999,"Stop $iqn from auto login on boot");
                ($rc, @out) = execute_command("iscsiadm -m node " . 
                                              " -T $iqn " . 
                                              " -o update " .
                                              " -n node.startup " .
                                              " -v manual" ); 
                lib_trace(9999,"rc = $rc");
            }
        }
           
    }

done:
    
    lib_trace(9999,"Exit transport_config --refresh");
    return $rc;
}
#    This function will perform the transport configuration for acfs remote.
#    This function gets called by 'acfsroot transport_config'.
#    This function expects 2 arguments:
#        1) 'refresh' or 'rescan'
#        2) comma-separated list of transports to which apply the operation
#    
#    iSCSI scan
#
#        On Linux, the following command should be used to do the scan.
#
#            iscsiadm -m discovery -t st -p <vip address for DC>
#
#        The above command will populate the database of available targets on
#        the node. It will also return the available targets in the output.
#
#    iSCSI login
#
#        On Linux, the following command should be used to do the login for
#        each target.
#
#            iscsiadm -m node -p <vip address for DC> -T <target name> -l
#
#        The iscsi initiator allows multiple connections per target, so it is
#        important NOT to login to the same target more than once.  If so,
#        multiple SCSI devices will show up that point to the same target on
#        the DCN.
#
#    iSCSI logout
#        
#        On Linux, the following command should be used to logout a target.
#
#            iscsiadm -m node -T <target from session output> -u
#
#    iSCSI session
#
#        On Linux, the following command should be used to retrieve the list
#        of active iSCSI sessions.
#
#            iscsiadm -m session
#
#        We need to further filter/verify this so we only operate on iSCSI 
#        sessions related to ACFS Remote
#
sub osds_acfsr_transport_config
{

    my $operation        = shift;
    my $transport_string = shift; # this is a comma separated transport list
                                  # nshga2601.iSCSI.0,nshga2601.iSCSI.2
    my @transport_list;
    my @ip_list;
    my $seqnum           = 0;
    my $transport_type   = "iSCSI"; # Default to iSCSI
    my $ignore_entry     = 1;
    my $match_found      = 0;
    my $name             = "";
    my $op_applied       = 0;
    my $ret              = USM_FAIL; # Assume fail if the corresponding 
                                     # transport operation is not even reached. 

    lib_trace( 9176, "Entering '%s'", "osds_acfsr_transport_config");
    # split into an array
    my @transports_to_config = split /,/,$transport_string; 
    my $rc;
    my @advmutil_output;

    foreach (@transports_to_config)
    {
      my $current_transport = $_;
      if($current_transport !~ /\w+\.BLK|[iI]SCSI\.\d+/)
      {
        lib_trace(9999,"Invalid transport format: $current_transport");
        return 1;
      }
      ($transport_type, $seqnum) = $current_transport =~ /\w+\.(\w+)\.(\d+)/;
      # We want verbose to get the xml data output, which will (hopefully) 
      # contain the DSC's address.
      ($rc,@advmutil_output) = execute_command("$ADVMUTIL session list -v " .
                                               "-i $transport_type.$seqnum ");

#   Sample output:   
#[root@nshgc1712 ~]# /sbin/advmutil session list -v -i ISCSI.0
#nodeName     transportType sequenceNum state    
#    (XML data)
#---------------------------------------------------------
#nshgc1712    iSCSI         0           ONLINE   
#    <TRANSPORT>
#      <TYPE> ISCSI </TYPE>
#        <ENDPOINTS>
#          <ENDPOINT>
#            <ID> cluster99_ISCSI_0 </ID>
#            <BINDNAME> nshgc171112-havip1 </BINDNAME>
#            <NETNUM> 1 </NETNUM>
#          </ENDPOINT>
#        </ENDPOINTS>
#    </TRANSPORT>

#   BEGIN XML PARSE
#   This XML parsing is not being done via a library, it should be. 
#   Allan Graves did tell me to use the library once this was on code review
#   but I'm not going to do it in this transaction due to time constraints.
    
		  foreach my $line (@advmutil_output)
  		{
        # Look for
        # <nodename>   <transport type>   <seqnum>   <state>  
      	if ($line =~ /(\w+)\W+(BLK|[iI]SCSI)\W+(\d+).*/)
      	{
          # Match none or some non-word
          # then a word (iSCSI or BLK)
          # then at least one non word
          # then one or more digit (seqnum)
          # and then put it all together 
          my $session_list_element = uc ("$1.$2.$3");
          # Now, loop the passed input to find matches

          if( uc($current_transport) =~ $session_list_element)
          {
            # Match found! 
            $match_found = 1;
            lib_trace(9999,"Applying '$operation' to " . 
                           "'$current_transport'"); 
            $op_applied = 0; # We haven't done anything to this match
          }
          # We looped all transports input, did we find a match?
          if($match_found == 1)
          {
            # Set this so the XML data following won't be ignored
            $ignore_entry = 0;
            # Reset for next transport in the list    
            $match_found = 0;
          }
          else
          {
            # No need to process the XML data 
            $ignore_entry = 1;
          }
        }
        elsif($ignore_entry == 0 && $line !~ /^-+/)
        {
          my $in_IP_tag = 0;
          my $xml_DSC_IP = "";
          $line =~ s/\s//g;
          # If the line is not the separator being printed, we should be
          # at the XML data. Now, look for an IP address. 
          if(($line =~ /.*<BINDADDR>.*/ || $line =~ /.*<BINDNAME>.*/)
             || $in_IP_tag == 1 
             && $op_applied == 0)
          {
            $in_IP_tag = 1;
            # For a DNS name I would look for word[.word]* format
            my $name_regex = ">(.+)<";
            # Maybe the ip/name is in the same line, so the tags may
            # be present in $line 
            if($line =~ /\D*(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}).*/) 
            {
              # Look for zero or more non-digits, then 
              # look for one to three digits followed by a period
              # 3 times and one last set of 1-3 digits.
              # OR
              # Look for the xml tags and brackets and grab the name
              # OR
              # Look for a name  
              $xml_DSC_IP = $1;
              # The problem with this is if a transport has more than
              # one IP address (as seen in the sample provided by Allan)
              # I would use the last one. He told me not to worry about
              # it.            
            }
            elsif ($line =~ /$name_regex/)
            {
              $name = $1;
              my @addresses = gethostbyname($name);
              # filter out other data returned by gethostbyname
              @addresses    = map { inet_ntoa($_) } @addresses[4 .. $#addresses];
              $xml_DSC_IP   = shift @addresses; 
            }
            # At this point we should have an IP address 
            if($op_applied == 0 && $xml_DSC_IP ne '')
            {
              lib_trace(9999, "Using '$xml_DSC_IP' for transport " . 
                              "'$current_transport'");
              if ($operation eq 'rescan')
              {
                $ret = osds_acfsr_transport_rescan($current_transport,
                                                   $xml_DSC_IP);
              }
              elsif ($operation eq 'refresh')
              {
                $ret = osds_acfsr_transport_refresh($current_transport,
                                                    $xml_DSC_IP);
              }
              else
              {
                # Unknown operation, should never get here
                $ret = USM_FAIL;
              }
              $op_applied = 1;
            }
            else
            {
              lib_trace(9999,"Invalid transport configuration, no host" .
                             " IP address or hostname is present. Verify ". 
                             "configuration for $current_transport");
            }
          }
          if(($line =~ /.*<BINDADDR>.*/ || $line =~ /.*<BINDNAME>.*/) &&
             $in_IP_tag == 0 && $op_applied == 0)
          {
              $in_IP_tag = 1;
          }

          # Check if we're leaving the <BIND*> xml tag
          if($in_IP_tag == 1 && 
             ($line =~ /.*<\/BINDADDR>.*/ || $line =~ /.*<\/BINDNAME>.*/))
          {
              $in_IP_tag = 0;
          }
        }
      }
    }
#   END XML PARSE
#   This XML parsing is not being done via a library, it should be. 
#   Allan Graves did tell me to use the library once this was on code review
#   but I'm not going to do it in this transaction due to time constraints.
    lib_trace(9999,"Exit osds_acfsr_transport_config"); 
    return $ret;
}


#    This function will list the available transports and their status.
#    This function gets called by 'acfsroot transport_list'.
#    The output gets parsed by the crs acfs agent.
#
#    V1 - List the transport IDs found in 
#         iscsi: /etc/iscsi/initiatorname.iscsi 
#         blk:   /sys/hypervisor/uuid
#         Mark BLK as not in use.

sub osds_acfsr_transport_list
{
 
    my $iscsi_path       = "/etc/iscsi/initiatorname.iscsi";
    my $blk_path         = "/sys/hypervisor/uuid";
    my $iscsi_identifier = "";
    my $blk_identifier   = "";
    my $line;

    open(FH,"<$iscsi_path") 
        || die "cannot open $iscsi_path for read: $!";

    while ($line = <FH>)
    {
        if($iscsi_identifier eq "")
        {
            $iscsi_identifier = $line;
            chomp($iscsi_identifier);
            $iscsi_identifier =~ s/InitiatorName=//g; 
        }
        else
        {
            return USM_FAIL;
            # This means there are multiple lines in the identifier file.
            # Shouldn't happen.   
        }
    }

    close FH;
    #Output of the acfsroot transport_list looks like:
    #  $ acfsroot transport_list
    #  ISCSI: INUSE : IQN:foo.123
    #  BLK: NOTINUSE : 1111-XXXX
    # 
           
    print "ISCSI: " ;
    print $iscsi_identifier ne "" ? "INUSE":"NOTINUSE"; 
    print $iscsi_identifier ne "" ? " : $iscsi_identifier \n" : "\n";
    print "BLK: " ;
    print $blk_identifier ne "" ? "INUSE":"NOTINUSE"; 
    print $blk_identifier ne "" ? " : $blk_identifier \n" : ":\n";

    return USM_SUCCESS;
}

sub execute_command
{
    my $command = join ' ', @_;
    my @output;
    my $output_string;
    my $rc;
    my $timestamp = strftime "%F %T", localtime;

    lib_trace(9999, "Executing - $command");
    @output = qx{$command 2>>$log_path};
    $rc = $? >> 8;
    chomp(@output);
    $output_string = join("\n",@output);
    lib_trace(9999, $output_string);
    if(open(my $fh, '>>', $log_path))
    {
        print $fh "[$$] [$timestamp] ";
        print $fh "Executing - $command\n";
        print $fh "$output_string";
        close $fh;
    }
    else
    {
        lib_trace(9999, "unable to write to $log_path");
    }

    return ($rc, @output);
}

# This function will modify the values of the acfslib::acfsr hash.
# The first value is 'True' when ACFS Remote is supported, 'False' otherwise.
# In order to determine if it is supported we
#   - Retrieve the passed argument. It will either be "DOMAIN SERVICES"
#     or "MEMBER". 
#   - Depending on cluster class, determine if the current OS is supported.
#   If it is supported, other values will be pushed into the array
# Is ISCSI supported? 'True' or 'False'.
sub osds_acfsr_supported
{
    my $cluster_class = shift;
    my $rc = 0;

    if($cluster_class eq 'MEMBER')
    {
        $acfslib::acfsr{'ACFS Remote'} = 'True';
        $rc = 1;
    }
    elsif($cluster_class eq 'DOMAIN SERVICES')
    {
        my $kernel_name = `uname -s`;
        my $kernel_release = $UNAME_R;
        chomp($kernel_name);
        if($kernel_name eq 'Linux')
        {
            $kernel_release =~ s/(\d+\.\d+)\..*/$1/g;

            if($kernel_release ge '3.8')
            {
                $acfslib::acfsr{'ACFS Remote'} = 'True';
                $rc = 1;
            }
        }
    }
    else
    {
        $acfslib::acfsr{'ACFS Remote'} = 'False';
    }
    # Check ISCSI support
    # Allan said this is always supported.
    $acfslib::acfsr{'iSCSI'} = 'True'; 

#    # Check if this is an ODA 
#    if(acfslib::isODA())
#    {
#        # Xen Blkfrnt/blkback support 
#        $acfslib::acfsr{'xen blkfrnt/blkback'} = 'True';
#    }
    return $rc; 
}

# This function will modify the values of the acfslib::acfsr hash.
# The first value is 'True' when ACFS Remote is installed, 'False' otherwise.
# In order to determine if it is installed we need to look for 
# /etc/modprobe.d/oracleadvm.conf (Linux location, this may vary in other OS)
#   If found, read it and look for asm_acfsr_mode option
#       As of 2/3/16 modes are:
#           DOMAINSERVICES = 1
#           MEMBER         = 2
#           SHMI           = 3 (ADE-only)
#       The list can be found in acfsroot.pl. 
#       Perhaps I should move that list somewhere else?
#   Any of those modes mean 'installed'. Any other value (or a lack of one)
#   means not installed.
#   If it is supported, other values will be pushed into the array
# Is ISCSI setup? 'True' or 'False'.
sub osds_acfsr_installed
{

    my $cluster_class = shift;
    my $mode = '';
    my $fh;
    my $line;
    my @clusterinfo;
    my $rc;

    ($rc, @clusterinfo) = execute_command("$ACFSUTIL cluster info");

    for (@clusterinfo)
    {
        if(/ACFS Remote mode: \[(.*)\]/)
        {
            $mode = $1;
        }
    }

    if($cluster_class eq $mode)
    {
        $acfslib::acfsr{'ACFS Remote'} = 'True';
        $rc = 1;
    }
    else
    {
        $acfslib::acfsr{'ACFS Remote'} = 'False';
        $rc = 0;
    }

    # Determine is iscsi is installed.
    my $iscsi_inst = `/sbin/service iscsid status`;
    # Could be running or stopped, so check if it's not there.
    # This check might break if we run in a shell in another language
    if($iscsi_inst =~ /unrecognized service/)
    {
        $acfslib::acfsr{'iSCSI'} = 'False';
    }
    else
    {
        $acfslib::acfsr{'iSCSI'} = 'True';
    }
#    # We would check for xen blkfrnt/blkback support via isODA.
#    # For now, we aren't going to check/show that. 
#    if(acfslib::isODA())
#    {
#        # Xen Blkfrnt/blkback support 
#        $acfslib::acfsr{''} = 'True';
#    }
#    else
#    {
#        $acfslib::acfsr{''} = 'True';
#    }
    return $rc;
}

# This function will modify the values of the acfslib::acfsr hash.
# The first value is 'True' when ACFS Remote is loaded, 'False' otherwise.
# Is ISCSI setup and running? 'True' or 'False'.
sub osds_acfsr_loaded
{
    my $cluster_class = shift;
    my $mode = '';
    my $fh;
    my $line;
    my @clusterinfo;
    my $rc;

    ($rc, @clusterinfo) = execute_command("$ACFSUTIL cluster info");

    for (@clusterinfo)
    {
        if(/ACFS Remote mode: \[(.*)\]/)
        {
            $mode = $1;
        }
    }

    if($cluster_class eq $mode)
    {
        $acfslib::acfsr{'ACFS Remote'} = 'True';
        $rc = 1;
    }
    else
    {
        $acfslib::acfsr{'ACFS Remote'} = 'False';
        $rc = 0;
    }

    
    # Determine is iscsi is installed.
    my $iscsi_inst = `/sbin/service iscsid status`;
    # This check might break if we run in a shell in another language
    if($iscsi_inst =~ /running/)
    {
        $acfslib::acfsr{'iSCSI'} = 'True';
    }
    else
    {
        $acfslib::acfsr{'iSCSI'} = 'False';
    }
#    if(acfslib::isODA())
#    {
#        # Xen Blkfrnt/blkback support 
#        $acfslib::acfsr{''} = 'True';
#    }
#    else
#    {
#        $acfslib::acfsr{''} = 'True';
#    }

    return $rc;
}

# This function adds the specified nodeid to a DSF's ACL. 
# It also sets the cmdsn_depth value for the target and the nr_requests for the
# virtual block device. 
sub osds_acfsr_transport_change_add_acl
{
    my $dsf         = shift;
    my $nodeid      = shift;
    my $return_code = USM_SUCCESS;
    my ($rc, @output);
    my $seqnum      = 0;
    my $iqn;
    my $block_name;
    my $path;
    my $attr;
    my $mcid;
    my $whoami      = (caller(0))[3];

    lib_trace(9999, "Enter $whoami");

    $iqn = iscsi_get_iqn($seqnum,$dsf);

    $path        = "/iscsi/$iqn/tpg1/acls/";
    $return_code = run_targetcli($path, "create $nodeid");
    if($return_code != USM_SUCCESS)
    {
      goto done; 
    }

    $attr        = "/sys/kernel/config/target/iscsi/$iqn/tpgt_1/" . 
                   "acls/$nodeid/cmdsn_depth";

    ($rc,@output) = execute_command("echo 512 > $attr");

    # Since this is all iscsi (for now), always use 01 as the transport type.
    $iqn =~ /.*vol-(\d\d\d)/;
    my $vol_number = $1;
    if(! defined $vol_number) 
    {
        return USM_FAIL;
    }
    ($mcid) = $dsf =~ /\/dev\/acfsr\/(\d\d\d\d\d\d)-\d\d-\d\d/;
    my $nr_path = "/sys/devices/virtual/block/acfsr\\!$mcid-01-$vol_number" . 
                  "/queue/nr_requests";
    ($rc,@output) = execute_command ("echo 1024 > $nr_path");
    
done:

    lib_trace(9999,"Exit $whoami with rc = $return_code");
    return $return_code;
}

sub osds_acfsr_transport_change_remove_acl
{
    my $dsf         = shift;
    my $nodeid 	    = shift;
    my $return_code = USM_SUCCESS;

    my $seqnum      = 0;
    my $iqn;
    my $path;
    my $whoami 	    = (caller(0))[3];

    lib_trace(9999, "Enter $whoami");

    $iqn = iscsi_get_iqn($seqnum,$dsf);

    if (! -d "/sys/kernel/config/target/iscsi/$iqn/tpgt_1/acls/$nodeid")
    {
        lib_trace(9999,"$nodeid does not exist for $iqn");
        $return_code = USM_FAIL;
    }
    else
    {
        $path = "/iscsi/$iqn/tpg1/acls/";
        $return_code = run_targetcli($path, "delete $nodeid");
    }
    return $return_code;
}

# 
#   This function creates a new iscsi target using the specified DSF for the 
#	specified remote cluster. 
#   Input:
#       - $guid = GUID of the cluster for which the storage is going to be 
#                 exported. 
#       - $dsf  = Path to device special file being exported.
#	Output:
#		Returns a counter of how many targetcli commands failed. 0 means 
#		everything executed successfully.
#

sub osds_acfsr_transport_change_setup_target
{
    my $guid        = shift;
    my $dsf         = shift;
    my $return_code = USM_SUCCESS;
    my @output;
    my $rc;
    my $block_name;
    my $seqnum      = 0;
    my $whoami      = (caller(0))[3];
    my $path;
    my $iqn;
    my $hostname;

    lib_trace(9999, "Enter $whoami");
    
    ($rc,@output) = execute_command("$acfslib::ADVMUTIL transport list " . 
                                    " -g $guid");
# Expected output for 'advmutil transport list -g $guid'
#	clusterName: NSHGA2603    (clusterGUID: b9ba5d73a27bff74bf8db9073edc7d7a)
#  	 transportType sequenceNum
#	-----------------------------------------------------------
#    	iSCSI         0  

    for (@output)
    {
        my $line = $_;
        if($line =~ /\s*BLK|[iI]SCSI\s+(\d+)/)
        {
            $seqnum = $1;

            $iqn = iscsi_get_iqn($seqnum,$dsf);

            $block_name = iscsi_get_block_name($seqnum,$dsf);

            if ( -d "/sys/kernel/config/target/iscsi/$iqn" )
            {
                lib_trace(9999, "Removing $iqn export");
                osds_acfsr_transport_change_delete_target($guid,$dsf);
            }

            $path = "/backstores/block/";
            $return_code += run_targetcli($path, 
                                         "create $block_name $dsf");
            $path = "/iscsi/";
            $return_code += run_targetcli($path, 
                                         "create $iqn");
            $path .= "$iqn/tpg1/";
            $return_code += run_targetcli($path, 
                                         "set attribute authentication=0");
            $path .= "luns/";
            $return_code += run_targetcli($path, 
                                       "create /backstores/block/$block_name");
        }
    }
   


    return $return_code > 0 ? USM_FAIL : USM_SUCCESS;
}

# 
#   This function deletes the iscsi target that matches the specified DSF for 
#   the specified remote cluster. 
#   Input:
#       - $guid = GUID of the cluster for which the storage is going to be 
#                 unexported. 
#       - $dsf  = Path to device special file being unexported.
#	Output:
#		Returns a counter of how many targetcli commands failed. 0 means 
#		everything executed successfully.
#
sub osds_acfsr_transport_change_delete_target
{
    my $guid        = shift;
    my $dsf         = shift;
    my $return_code = USM_SUCCESS;
    my $whoami      = (caller(0))[3];
    my $seqnum      = 0;
    my $iqn;
    my $block_name;
    my $path;
    my $attr;
    
    lib_trace(9999, "Enter $whoami");

    my ($rc,@output) = execute_command("$acfslib::ADVMUTIL transport list " . 
                                    " -g $guid");
# Expected output for 'advmutil transport list -g $guid'
#	clusterName: NSHGA2603    (clusterGUID: b9ba5d73a27bff74bf8db9073edc7d7a)
#  	 transportType sequenceNum
#	-----------------------------------------------------------
#    	iSCSI         0  

   
    for (@output)
    {
        my $line = $_;
        if($line =~ /\s*BLK|[iI]SCSI\s+(\d+)/)
        {
            $seqnum = $1;

            $iqn = iscsi_get_iqn($seqnum,$dsf);

            $block_name = iscsi_get_block_name($seqnum,$dsf);

            $path = "/iscsi/";
            $return_code += run_targetcli($path, "delete $iqn");

            $path = "/backstores/block/";
            $return_code += run_targetcli($path, "delete $block_name");
        }
    }

    lib_trace(9999,"Exit $whoami with rc = $return_code");
    return $return_code > 0 ? USM_FAIL : USM_SUCCESS;
}

sub run_targetcli
{
    my $path = shift;
    my @args = @_;
    my $cmd = "/bin/targetcli";

    my ($rc, @out) = execute_command("export HOME=/tmp; " .
                                     "$cmd set global " . 
                                     "auto_save_on_exit=false 2>&1");

    $cmd .= " $path";

    for (@args)
    {
        $cmd .= " $_";
    }

    ($rc,@out) = execute_command("export HOME=/tmp; " . $cmd);

    return $rc;
}

sub iscsi_get_block_name
{
    my $seqnum = shift;
    my $dsf = shift;
    my $block_name = "acfsr";
    my $mc = 0;
    my $mnr = 0;

    if($dsf =~ /.*\/(.*)-(.*)-(.*)/)
    {
        $mc = $1;
        $mnr = $3;
    }

    $block_name .= "-$mc-vol-$mnr";
    lib_trace(9999,"Generated block name: $block_name");

    return $block_name;
}

sub iscsi_get_iqn
{
    my $seqnum = shift;
    my $dsf = shift;
    my $iqn = $iqn_pre;
    my $mc = 0;
    my $mnr = 0;

    if($dsf =~ /.*\/(.*)-(.*)-(.*)/)
    {
        $mc = $1;
        $mnr = $3;
    }

    $iqn .= "\\:$mc\\:$seqnum\\:vol-$mnr";

    lib_trace(9999,"Generated IQN: $iqn");
    return $iqn;
}

1;

OHA YOOOO