#!/usr/local/bin/perl
#
# Name:     convert-imports.pl
# Author:   Stefan Evans, CNE 4/5/6 & MCP  (stefan.evans@gulfstream.com)
# Date:     2004-01-28
# Version:  1.0
#
# Tested platform:	- Server Console: NetWare 5.1 SP6+ / NetWare 6.0 SP3+
#                   - PERL Interpreter: Built-in to NetWare OS already
#                   - Original 'imports' file from NFS 3.0 SP5+
#
#                   Or,
#
#                   - Workstation: Windows 2000 Professional SP4
#                   - PERL Interpreter: ActiveState Perl 5.8.2+
#                   - Original 'imports' file from NFS 3.0 SP5+
#
#
# Purpose: To convert the legacy NFS Gateway (Novell NFS 3.0 SP5+) "sys:/etc/imports" configuration file to the new
#          format used by NFS Gateway 4.0 for NetWare 6.x. This script is especially useful if you have a large number
#          of Unix-to-PC Gateway volumes (we have 90+) and you do not wish to re-create all of that configuration by
#          hand.
#
#          By default, if this script is run from your current NFS 3.0 Gateway server console prompt, it will
#          read in "sys:/etc/imports" and output to a new "sys:/gy4-volumes.ncf" file. You can then copy the contents
#          of this output and paste it into "sys:/system/gystart.ncf" at the very bottom of the file, or simply load
#          this new gy4-volumes.ncf batch file separately in the AUTOEXEC.NCF (after the load line for gystart.ncf
#          itself). The "sys:/system/gystart.ncf" is created by the NFS Gateway 4.0 installation routines, and it is
#          what starts-up the NFS Gateway services and sets up global parameters.
#
#          Of special note, Novell now recommends referring to Unix hosts by their IP Address to avoid a performance
#          setback. So, this script will attempt to dynamically lookup any DNS hostnames you may have used in the past.
#          Existing use of IP addresses or unknown DNS names will be preserved.
#
#          Also, certain fields used in the NFS 3.0 Gateway "imports" configuration file are no longer relevant in NFS
#          Gateway 4.0. Specifically "nfsPacketXferSize" and "Mount Volume On Startup" fields have no meaning in an NFS
#          4.0 volume mount (GYMOUNT) syntax. For nfsPacketXferSize (a.k.a. NFS Packet Transfer Size), that is now
#          handled globally by the "Largest UDP Packet Size" console SET parameter. And as far as the "Mount Volume On
#          Startup" field is concerned, since GYMOUNT commands are all dumped individually into an NCF batch file,
#          there's no point to even including a particular volume command if you don't intend to mount it upon NFS
#          Gateway start-up.
#
#          Additionally, some fields that continue to exist in NFS Gateway 4.0 have new system defaults -- RPC Inter
#          Packet Timeout and RPC Retry Count. The two, mutually exclusive command line options for this script
#          account for this reality by giving you the option 1) to blindly recreate your NFS 3.0 "imports" file into
#          the new GYMOUNT syntax and effectively keeping the old timeout/retry values (-o), or 2) throw away the old
#          system defaults used by NFS 3.0 in favor of the new system default values (-n) while preserving other
#          settings found.
#
#          Either route you chose, you should always review the new output to ensure precisely the type of connectivity
#          that you'd prefer.
#
#
# Instructions for the non-PERL programmer:
#
# 1a) To run this script from a NetWare server console, no PERL interpreter installation is necessary. NetWare 5.1,
#     6.0, and 6.5 OS's already include the needed software. However, special considerations have to be realized if
#     this software is going to be run from a NetWare server console. See the "SPECIAL NOTES FOR NETWARE-BASED USE"
#     section below.
#
# 1b) To run from a Windows PC instead of your NetWare server, download a current PERL interpreter by going to
#     http://activestate.com/Products/ActivePerl/. Click on the "Download" link in the upper-left corner of the
#     screen. Install ActivePerl once your download completes.
#
#
#  2) Copy this script to an easily accessible location. For example, on NetWare copy it to the root of the SYS:
#     volume, or on Windows copy it to the root of the C:\ drive.
#
#
#  3) Customize specific sections of the script below to match your environment using a basic text editor like
#     Notepad (with "Word Wrapping" disabled) that will not introduce any other formatting information into the code
#     (particularly avoid MS Word):
#
#       - Section 1: CUSTOMIZE HERE -- Input and Output File Paths
#
#       - Section 2: CUSTOMIZE HERE -- Hardcoded DNS-to-IP Entries (Optional)
#
#
#  4) Run the script:
#
#     From NetWare, at the server console type: perl sys:convert-imports.pl -h
#     Or from Windows, open a Command Prompt and type: perl c:\convert-imports.pl -h
#
#     In both cases, read the help screen and select one of the two conversion options.
#
#
# SPECIAL NOTES FOR NETWARE-BASED USE:
#
#   1) If running from a NetWare console prompt, for the script's dynamic DNS lookup and translations to work for you,
#      make sure your server's sys:/etc/resolve.cfg file contains at least one valid DNS server's IP address for your
#      environment.
#
#
#   2) Warning: the NetWare OS console doesn't do multi-DNS-domain suffix searches, so if your NFS 3.0 "imports" file
#      contains any unqualified Unix hostnames, they won't successfully query unless that hostname is in the same
#      DNS domain as the NetWare server itself is configured for in the "sys:/etc/resolv.cfg" file. So, if myUnixHost
#      is specified without a DNS suffix, and the NetWare server is configured in myDNSzone1.com but the host record is
#      only defined in DNS for the Unix machine in myDNSzone2.com, then a DNS lookup from the NetWare console will fail
#      and the hostname will be unavoidably preserved in the new NFS 4.0 NCF output. The same is also true for Windows,
#      unless you configure multiple DNS suffixes to be searched from in Control Panel | Network and Dial-up
#      Connections | Local Area Connection | Properties | Internet Protocol (TCP/IP) | Advanced | DNS tab | "Append
#      these DNS suffixes (in order)".
#
#      A suffix search can pose it's own problems, such as when a host record is defined in multiple zones but
#      pointing to different IP addresses -- perhaps one of the entries is outdated but was not properly cleaned-up
#      by the DNS Administrator. So, depending on your workstations search order, you might get the "stale" IP
#      Address and not the current. Admittedly, this is a pretty rare scenario.
#
#      So, the moral of the story here is to ALWAYS review the output file (or the output screen of the script
#      immediately after processing) to ensure overall accuracy and that the proper IP Addresses were found
#      and used.
#
######################################################################################################################

use Getopt::Std;
use strict;

# Get the script name without the final extension or parent path
my @program_name = split (/\\/, $0);
my ($script, undef) = split(/\.[\w ]*$/, pop(@program_name), 2);

my %hostname_to_ip; # Hash for storing DNS to IP Address mappings

#=================================================================================================
# Section 1: CUSTOMIZE HERE -- Input and Output File Paths
#
# For use if I/O paths are from a Windows PC's perspective; tailor this to your needs.
#my $input_file = "c:\\imports";
#my $output_file = "c:\\gy4-volumes.ncf";
#
# For use if I/O paths are from a NetWare console's perspective; tailor this to your needs.
my $input_file = "sys:/etc/imports";
my $output_file = "sys:/gy4-volumes.ncf";
#=================================================================================================

#=================================================================================================
# Section 2: CUSTOMIZE HERE -- Hardcoded DNS-to-IP Entries (Optional)
#
# If for some reason DNS look-ups are not functional for your environment or is otherwise not
# working correctly in every case, you can create as many static entries below as to your liking,
# mirroring the sample syntax on the '#'-commented lines. Any unix-host entries that are used in
# your 'imports' file but not specified here will 1) be dynamically looked-up via a DNS server
# query, or 2) if not found in a DNS query, then the original hostname or IP value used in the
# 'imports' file will be preserved.
#
# NOTE: Novell recommends using IP Addresses for defining Unix hosts in lieu of DNS hostnames in
#       NFS Gateway 4.0 to avoid performance degradation.
#
#$hostname_to_ip{"unix-host.mydomain.com"} = "155.12.17.135";
#$hostname_to_ip{"unix2.other-zone.com"} = "130.16.18.1";
#
#=================================================================================================


# Get and parse command line options
die("\nOne or more command-line options were invalid!\n\n") unless &getopts('hno');

if ($main::opt_h)
 {
    &usage(); # Displays help & terminates script. Checking for 'h' first so if other options called, still go to help.
 }
elsif ($main::opt_n && $main::opt_o)
 {
    &usage(); # These two options are mutually exclusive, so terminate with help screen displayed.
 }
elsif (!($main::opt_n) && !($main::opt_o))
 {
    &usage(); # ONE of these options MUST be specified, so terminate with help screen displayed.
 }

# Pre-initialize variables used through-out the script
my ($new_entry, $num_of_entries, $skipped_excessive, $skipped_missing, $successful_entries) = (undef, 0, 0, 0, 0);

open(INFILE, $input_file) or die "\nCan't open input file ($input_file): $!\n\nEdit the script source and customize the input path.\n\n";
open(OUTFILE, ">$output_file") or die "\nCan't create output file ($output_file): $!\n\nEdit the script source and customize the output path.\n\n";

#system(cls);
print("\nConversion in progress (preferring new NFS 4.0 defaults)... \n\n") if ($main::opt_n);
print("\nConversion in progress (preserving old NFS 3.0 defaults)... \n\n") if ($main::opt_o);

while ($new_entry = <INFILE>)
 {
    chomp($new_entry); # Remove any trailing newline characters, which will cause empty lines to be discarded

    if ($new_entry ne undef)
     {
        $num_of_entries++;

        $new_entry =~ s/^[ \t]+//; # Remove leading spaces/tabs at the start of the input line
        $new_entry =~ s/[ \t]+$//; # Remove trailing spaces/tabs at the end of the input line

        # Break-down the original format into manageable pieces
        my @params = split(/[ \t]+/, $new_entry);

        if (@params < 12)
         {
            $skipped_missing++;

            print("\nEntry #$num_of_entries: Discarded, bad format detected (too few items in this row):\n\t$new_entry\n");
         }
        elsif (@params > 12)
         {
            $skipped_excessive++;

            print("\nEntry #$num_of_entries: Discarded, bad format detected (too many items in this row):\n\t$new_entry\n");
         }
        else
         {
            $successful_entries++;

            # Turn the DNS name into an IP Address, as advised for NFS Gateway 4.0 for NetWare 6.x
            # Hostname should be fully qualified DNS name, i.e. unix-server.your-domain.com)

            if (! exists($hostname_to_ip{$params[0]})) # If entry isn't already part of the Hash, then create new key pair with (host => ip_address)
             {
                if (unpack('C4', gethostbyname($params[0])) eq undef)
                 {
                    # Preserve the original DNS hostname
                    $hostname_to_ip{$params[0]} = $params[0];
                 }
                else
                 {
                    # Store the IP Address that was found via lookup
                    $hostname_to_ip{$params[0]} = join(".", unpack('C4', gethostbyname($params[0])));
                 }
             }

            my $ro_rw = undef;
            
            if ($params[9] == 0)
             {
                $ro_rw = ' -readWrite';
             }
            else
             {
                $ro_rw = ' -readOnly';
             }

            if ($main::opt_n)
             {
                # Build the new format, discarding old NFS 3.0 defaults in favor of NFS 4.0 system defaults
                print OUTFILE ('gymount '.
                                $params[2].
                                ' '.
                                $hostname_to_ip{$params[0]}.
                                ' '.
                                $params[1].
                                ' -mountUID '.
                                $params[3].
                                ' -mountGID '.
                                $params[4].
                                $ro_rw.
                                "\n\n"
                              );
             }
            else
             {
                # Build the new format, preserving old NFS 3.0 SP1+ settings defined in original 'imports' file
                #
                # NOTE: "nfsPacketXferSize" and "Mount Volume On Startup" fields have no meaning in NFS 4.0 volume
                # mount GYMOUNT command. For nfsPacketXferSize (a.k.a. NFS Packet Transfer Size), that is now
                # handled globally by the "Largest UDP Packet Size" console SET parameter. And as far as the
                # "Mount Volume On Startup" field is concerned, since GYMOUNT commands are all dumped individually
                # into an NCF batch file, there's no point to even including a particular volume command if you don't intend
                # to mount it upon NFS Gateway start-up.
                print OUTFILE ('gymount '.
                                $params[2].
                                ' '.
                                $hostname_to_ip{$params[0]}.
                                ' '.
                                $params[1].
                                ' -nfsVersion '.
                                $params[11].
                                ' -mountUID '.
                                $params[3].
                                ' -mountGID '.
                                $params[4].
                                ' -rpcTimeOut '.
                                $params[6].
                                ' -rpcRetries '.
                                $params[5].
                                $ro_rw.
                                "\n\n"
                              );
             }
         }
     }
 }

print("\nUnique Hostnames:\n\n");
foreach my $tempkey (sort keys(%hostname_to_ip))
 {
    print("$tempkey: ".$hostname_to_ip{$tempkey}."\n");
 }

print("\n\n");
print("Total NFS Gateway Entries:     $num_of_entries\n");
print(" - Skipped, Missing Values:    $skipped_missing\n");
print(" - Skipped, Excessive Values:  $skipped_excessive\n");
print(" - Successfully Processed:     $successful_entries\n\n");

close (INFILE);
close (OUTFILE);


# Subroutines

sub usage {
    system(cls);
    print(" Usage: perl $script.pl [-n | -o]\n\n");
    print("  -h         : Displays this help message.\n\n");
    print("  -n         : Discards old NFS 3.0 default settings included in your \"imports\"\n");
    print("               file in favor of relying on the new NFS 4.0 defaults now\n");
    print("               suggested by Novell (for such settings as \"RPC Inter Packet\n");
    print("               Timeout\" and \"RPC Retry Count\").  Also presumes NFS v.3 protocol.\n");
    print("               [Recommended]\n\n");
    print("  -o         : Preserves all of your original NFS 3.0 Gateway volume settings\n");
    print("               that still apply in NFS 4.0, simply converting the \"imports\" file\n");
    print("               into the new GYMOUNT statement syntax.\n\n");
    print("  Example: perl $script.pl -n\n\n\n");
    print("** Options \'-n\' and \'-o\' are mutually exclusive; choose only one or the other.\n\n");
    
    exit;
} # End of &usage
