#!/usr/bin/perl -w
#
#  (C) 2001 Clemson University and The University of Chicago
#
#  See COPYING in top-level directory.
#
# generate a global pvfs2 configuration file based on user input
#
use Term::ReadLine;
use Getopt::Long;

# ugly global variables for option parsing
my $opt_protocol = '';
my $opt_port = '';
my $opt_tcpport = '';
my $opt_gmport = '';
my $opt_ibport = '';
my @opt_ioservers = ();
my @opt_metaservers = ();
my $opt_logfile = '';
my $opt_storage = '';
my $opt_trovesync = '1';
my $opt_quiet = '';
my $opt_logging = '';
my $opt_logstamp = '';
my $opt_server_job_timeout = '';
my $opt_client_job_timeout = '';
my $opt_first_handle = '';
my $opt_last_handle = '';
my $opt_root_handle = '';
my $opt_fsid = '';
my $opt_default_num_dfiles = '';

my $opt_security = '0';
my $opt_trusted_port = '';
my $opt_trusted_network = '';
my $opt_trusted_netmask = '';

my @opt_ioports = ();
my @opt_metaports = ();

my @io_hosts = ();
my @meta_hosts = ();

# sometimes people use ip addresses instead of hostnames.  perl's default sort
# will sort the ip addresses lexically, not numerically, so we need a slightly
# smarter sorter 

sub host_sort
{
    if ($a =~ /(\d+)\.(\d+)\.(\d+)\.(\d+)/) {
        my @addr1 = $a =~ /(\d+)\.(\d+)\.(\d+)\.(\d+)/ ;
        my @addr2 = $b =~ /(\d+)\.(\d+)\.(\d+)\.(\d+)/ ;

        $addr1[0] <=> $addr2[0] ||
        $addr1[1] <=> $addr2[1] ||
        $addr1[2] <=> $addr2[2] ||
        $addr1[3] <=> $addr2[3] 
    } else {
        $a cmp $b;
    }
}

sub get_user_input
{
    my($term,$OUT,$prompt,$res);

    $term = new Term::ReadLine 'pvfs2-genconfig';
    $OUT = $term->OUT || STDOUT;
    print "\n";
    $prompt = "* $_[0]";

    $_ = $term->readline($prompt);
    print "\n";
    return $_;
}

sub valid_number
{
    my($num, $len, $i, $digit);

    $num = $_[0];
    $len = length($num);

    for($i = 0; $i < $len; $i++)
    {
        $digit = substr($num,$i,1);
        if (($digit < 0) || ($digit > 9))
        {
            return 0;
        }
    }
    return (($len > 0) ? 1 : 0);
}

sub prompt_num
{
    my($prompt,$num,$default);
    $prompt = $_[0];
    $default = $_[1];
    do
    {
        $num = get_user_input($prompt);
        if (length($num) == 0)
        {
            return $default;
        }
    } while(!valid_number($num));
    return $num;
}

sub prompt_word
{
    my($prompt,$default,$val);
    $prompt = $_[0];
    $default = $_[1];
    
    $val = get_user_input($prompt);
    if (length($val) == 0)
    {
        $val = $default;
    }
    return $val;
}

sub parse_hostlist
{
    my($inputline, $port, $i, $foo, @parse, $arrayline);
    my($prefix, $count, @temparray, @inodes, $numinodes);
    $inputline = $_[0];
    $port = $_[1];

    # store input line in default variable 
    $_ = $inputline;
    # initialize array index counter
    $i = 0;
    # while there is still text left
    while($_)
    {
    # strip out the first matching word (delimited by comma or white sp)
    s/^[\s,]*[^\s,]+//;
    # save the matching string that was stripped out
    $foo = "$&";
    # pull leading and trailing white sp off
    $foo =~ s/[\s,]+//g;
    # stick it in the array
    $parse[$i] = $foo;
    # increment array index
    $i++;
    # loop around and continue on next word
    }

    $numinodes = 0;
    for($i=0; defined($parse[$i]) && $parse[$i] !~ /^$/;$i++) {
    # expand parsed input that contains brackets
    if ($parse[$i] =~ /{/) {
        @arrayline = split(/[{}, ]+/, $parse[$i]);
        $prefix = $arrayline[0];
        $count = @arrayline - 1;
        for ($j = 1; $j <= $count; $j++) { 
        if ($arrayline[$j] =~ /-/) {
            @temparray = split(/-/, $arrayline[$j]);
            for ($k = $temparray[0]; $k <= $temparray[1]; $k++) {
            $inodes[$numinodes] = $arrayline[0].$k;
            $numinodes++;
            }    
        } else { 
            $inodes[$numinodes] = $arrayline[0].$arrayline[$j];
            $numinodes++;
        }    
        }
    }
    else {
        $inodes[$numinodes] = $parse[$i];
        $numinodes++;
    }
    }
    return @inodes;
}

sub emit_defaults
{
    my($target,$num_unexp,$bmi_module,$handle_purg);
    $target = $_[0];
    $num_unexp = $_[1];
    $bmi_module = $_[2];
    $handle_purg = $_[3];

    # remove quotes from logfile if any
    if ($logfile =~ /\".*\"/)
    {
        $logfile =~ s/\"//g;
    }

    print $target "<Defaults>\n";
    print $target "\tUnexpectedRequests $num_unexp\n";
    print $target "\tLogFile $logfile\n";
    print $target "\tEventLogging $logging\n";
    print $target "\tLogStamp $logstamp\n";
    print $target "\tBMIModules $bmi_module\n";
    print $target "\tFlowModules flowproto_multiqueue\n";
    print $target "\tPerfUpdateInterval 1000\n";
    print $target "\tServerJobBMITimeoutSecs $server_job_timeout\n";
    print $target "\tServerJobFlowTimeoutSecs $server_job_timeout\n";
    print $target "\tClientJobBMITimeoutSecs $client_job_timeout\n";
    print $target "\tClientJobFlowTimeoutSecs $client_job_timeout\n";
    print $target "\tClientRetryLimit 5\n";
    print $target "\tClientRetryDelayMilliSecs 2000\n";

    print $target "</Defaults>\n";
}

sub emit_security
{
    my($portlist,$network,$netmask);
    $target = $_[0];
    $portlist = $_[1];
    $network = $_[2];
    $netmask = $_[3];

    print $target "<Security>\n";
    print $target "\tTrustedPorts $portlist\n";
    print $target "\tTrustedNetwork $network $netmask\n";
    print $target "</Security>\n";
}

sub emit_aliases
{
    my($target);
    $target = $_[0];

    print $target "\n<Aliases>\n";
    for($i = 0; $i <= $#union_hosts; $i++)
    {
        print $target "\tAlias $union_hosts[$i] $union_bmi[$i]\n";
    }
    print $target "</Aliases>\n";
}

sub emit_filesystem
{
    my($target, $name, $fs_id, $root_handle);
    $target = $_[0];
    $name = $_[1];
    $fs_id = $_[2];
    $root_handle = $_[3];

    # divide handle range space equally among servers ((2^32)-1 for now)
    my($total_num_handles_available, $start, $end, $i, $step, $num_ranges);
    $num_ranges = $#meta_bmi + $#io_bmi + 2;
    $total_num_handles_available = $last_handle - $first_handle + 1;

    # since meta and data handle ranges must be split, increment
    # num nodes for calculation purposes below
    $step = sprintf("%lu",($total_num_handles_available / $num_ranges));

    print $target "\n<Filesystem>\n";
    print $target "\tName $name\n";
    print $target "\tID $fs_id\n";
    print $target "\tRootHandle $root_handle\n";
    if($default_num_dfiles > 0)
    {
        print $target "\tDefaultNumDFiles $default_num_dfiles\n";
    }
    print $target "\t<MetaHandleRanges>\n";

    $start = $end = $first_handle - 1;

    foreach(@meta_aliases)
    {
        $start = $end + 1;
    $end += $step;
    print $target "\t\tRange $_ $start-$end\n";
    }

    print $target "\t</MetaHandleRanges>\n";
    print $target "\t<DataHandleRanges>\n";

    foreach(@io_aliases)
    {
        $start = $end + 1;
        $end += $step;
        print $target "\t\tRange $_ $start-$end\n";
    }
    print $target "\t</DataHandleRanges>\n";

    print $target "\t<StorageHints>\n";
#    print $target "\t\tHandleRecycleTimeoutSecs $handle_recycle_timeout_seconds\n";
    # only in special cases would someone want to sync data (failover comes to
    # mind)  The default thus should be to sync metadata but not sync data.  
    if ($opt_trovesync == 1) {
        print $target "\t\tTroveSyncMeta yes\n";
        print $target "\t\tTroveSyncData no\n";
    } else {
        print $target "\t\tTroveSyncMeta no\n";
        print $target "\t\tTroveSyncData no\n";
    }

    print $target "\t\tAttrCacheKeywords datafile_handles,metafile_dist\n";
    print $target "\t\tAttrCacheKeywords dir_ent, symlink_target\n";
    print $target "\t\tAttrCacheSize 4093\n";
    print $target "\t\tAttrCacheMaxNumElems 32768\n";
    print $target "\t</StorageHints>\n";

    print $target "</Filesystem>\n";
}

sub emit_server_conf
{
    my($target, $node, $storage, $logfile);
    $target = $_[0];
    $node = $_[1];
    $storage = $_[2];
    $logfile = $_[3];

    print $target "StorageSpace $storage\n";
    print $target "HostID \"$node\"\n";
    print $target "LogFile $logfile\n";
}

sub confirm
{
    my($prompt, $char, $valid_char);
    $prompt = $_[0];
    $valid_char = 0;
    do
    {
        $char = prompt_word($prompt,"-");
        if (($char eq 'y') || ($char eq 'n'))
        {
            $valid_char = 1;
        }
    } while($valid_char == 0);

    return (($char eq 'y') ? 1 : 0);
}

sub usage
{
    
# dump usage with a single HERE document rather than seperate print
# statements
print <<"THIS";    
Usage: pvfs2-genconfig [OPTIONS] <fs.conf> <server.conf>

  The pvfs2-genconfig utility creates configuration files for the
  PVFS2 file system.  The <fs.conf> and <server.conf> arguments are
  manditory and specify the names of the configuration files that will
  be written.  This utility will create one fs.conf file.  It will also
  create a seperate server.conf file for each server in the file system.
  Each server.conf file will be appended with the host name of the server
  to which it belongs.

  EXAMPLE: 'pvfs2-genconfig /tmp/fs.conf /tmp/server.conf' will
  generate a file called /tmp/fs.conf and server specific files
  called /tmp/server.conf-host1, /tmp/server.conf-host2, etc.  
  NOTE: If pvfs2-genconfig is executed with a single argument of "-", 
  then all output is directed to stdout and no files are written.

  All other arguments are optional.  If run without any optional
  arguments, then pvfs2-genconfig will prompt interactively for required
  parameters.

  pvfs2-genconfig can also be executed non-interactively by providing,
  at a minimum, all of the following arguments:

     --protocol    <PROTO>[,<PROTO>..] protocol(s) to use (tcp,gm,ib)
     --port        <NUM>               port to use (any single protocol)
     --ioservers   <STRING>            hostnames of data servers
     --metaservers <STRING>            hostnames of meta servers
     --logfile     <STRING>            logfile location
     --storage     <STRING>            storage space location
     --quiet                           run silently

  The following arguments are entirely optional, whether your intention is
  to run pvfs2-genconfig in interactive or non-interactive mode:

     --tcpport     <NUM>               TCP port to use
     --gmport      <NUM>               GM port to use
     --ibport      <NUM>               IB port to use
     --ioports     <STRING>            list of ports to use in single host case
     --metaports   <STRING>            list of ports to use in single host case
     --logging     <STRING>            debugging mask for log messages
     --logstamp    <STRING>            timestamp type for log messages 
                                       ('none','usec', or 'datetime' are valid)
     --notrovesync                     sync metadata only upon request
     --server-job-timeout <NUM>        server job timeout value (seconds)
     --client-job-timeout <NUM>        server job timeout value (seconds)
     --first-handle <NUM>              first handle value to reserve
     --last-handle  <NUM>              last handle value to reserve
     --root-handle  <NUM>              handle value to reserve for root object
     --fsid         <NUM>              fs identifier value
     --default-num-dfiles <NUM>        number of datafiles to use per file
                                       (defaults to number of I/O servers)
     --trusted      <0|1>              indicate whether trusted connection options need to be emitted
THIS

}

sub print_welcome
{
    if (!$opt_quiet) {
        print "**********************************************************************\n";
        print "\tWelcome to the PVFS2 Configuration Generator:\n\n";
        print "This interactive script will generate configuration files suitable\n";
        print "for use with a new PVFS2 file system.  Please see the PVFS2 quickstart\n";
        print "guide for details.\n\n";
        print "**********************************************************************\n";
    }
}

sub get_portlist
{
    my $type;

    if (!$opt_quiet) {
        print "\n";
        print "You must enter the trusted port ranges that your file system will accept\n";
        print "This must be of the form <port1 - port2>\n";
    }
    $type = prompt_word("Enter port list [Default is 0-65535]: ","0-65535");
    return $type;
}

sub get_network
{
    my $type;

    if (!$opt_quiet) {
        print "\n";
        print "You must enter the network address and network mask to identify list of trusted hosts\n";
        print "This must be of the form <network>, <netmask>\n";
    }
    $type = prompt_word("Enter network address, network mask [Default is 0.0.0.0, 0.0.0.0]: ", "0.0.0.0, 0.0.0.0");
    return $type;
}

sub get_protocol
{
    my $type;

    if (!$opt_quiet) {
        # get network type
        print "\n";
        print "You must first select the network protocol that your file system will use.\n";
        print "The only currently supported options are \"tcp\", \"gm\", and \"ib\".\n";
        print "(For multi-homed configurations, use e.g. \"ib,tcp\".)\n";
    }
    if ($opt_protocol) {
        $type = $opt_protocol;
    } else {
        $type = prompt_word("Enter protocol type [Default is tcp]: ","tcp");
    }
    return $type;
}

sub get_logging
{
    if ($opt_logging) {
        $logging = $opt_logging;
    } else {
        $logging = "none";
    }
    return $logging;
}

sub get_logstamp
{
    if ($opt_logstamp) {
        $logstamp = $opt_logstamp;
    } else {
        $logstamp = "usec";
    }
    return $logstamp;
}

sub get_root_handle
{
    if ($opt_root_handle) {
        $root_handle = $opt_root_handle;
    } else {
        $root_handle = 1048576;
    }
    return $root_handle;
}

sub get_fsid
{
    if ($opt_fsid) {
        $fsid = $opt_fsid;
    } else {
        # compute a psuedo-random 32 bit file system ID
        $fsid = int((rand() * 2147483647));
    }
    return $fsid;
}

sub get_last_handle
{
    if ($opt_last_handle) {
        $last_handle = $opt_last_handle;
    } else {
        $last_handle = 4294967297;
    }
    return $last_handle;
}

sub get_default_num_dfiles
{
    if ($opt_default_num_dfiles) {
        $default_num_dfiles = $opt_default_num_dfiles;
    } else {
        $default_num_dfiles = -1;
    }
    return $default_num_dfiles;
}

sub get_first_handle
{
    if ($opt_first_handle) {
        $first_handle = $opt_first_handle;
    } else {
        $first_handle = 4;
    }
    return $first_handle;
}

sub get_server_job_timeout
{
    if ($opt_server_job_timeout) {
        $server_job_timeout = $opt_server_job_timeout;
    } else {
        $server_job_timeout = 30;
    }
    return $server_job_timeout;
}

sub get_client_job_timeout
{
    if ($opt_client_job_timeout) {
        $client_job_timeout = $opt_client_job_timeout;
    } else {
        $client_job_timeout = 300;
    }
    return $client_job_timeout;
}

sub get_logfile
{
    if (!$opt_quiet) {
        print "Choose a file for each server to write log messages to.\n";
    }
    if ($opt_logfile) {
        $logfile = $opt_logfile;
    } else {
        $logfile = prompt_word("Enter log file location [Default is /tmp/pvfs2-server.log]: ","/tmp/pvfs2-server.log");
    }
    return $logfile;
}

sub get_storage
{
    if (!$opt_quiet) {
        print "Choose a directory for each server to store data in.\n";
    }
    if ($opt_storage) {
        $storage = $opt_storage;
    } else {
        $storage = prompt_word("Enter directory name: [Default is /pvfs2-storage-space]: ","/pvfs2-storage-space");
    }
    return $storage;
}
        


# get host port
sub tcp_get_port
{
    my $port;
    if (!$opt_quiet) { 
        print "Choose a TCP/IP port for the servers to listen on.  Note that this\n";
        print "script assumes that all servers will use the same port number.\n";
    }
    if ($opt_tcpport) {
        $port = $opt_tcpport;
    } elsif ($opt_port) {
        $port = $opt_port;
        $opt_port = '';
    } elsif (!@opt_ioports) {
        $port = prompt_num("Enter port number [Default is 3334]: ","3334");
    }
    return  $port;
}
sub gm_get_port
{
    my $port;
    if (!$opt_quiet) { 
        print "Choose a GM port (in the range of 0 to 7) for the servers to listen on. \n";
        print "This script assumes that all servers will use the same port number.\n";
    }
    if ($opt_gmport) {
        $port = $opt_gmport;
    } elsif ($opt_port) {
        $port = $opt_port;
        $opt_port = '';
    } else {
        $port = prompt_num("Enter port number [Default is 6]: ","6");
    }
    # every myrinet card i've seen has 8 ports.  If myricom makes a card
    # with more than that, we'll have to adapt
    ($port < 8) or die "GM ports must be in the range 0 to 7";

    return  $port;
}
sub ib_get_port
{
    my $port;
    if (!$opt_quiet) { 
        print "Choose a TCP/IP port for the servers to listen on for IB communications.  Note that this\n";
        print "script assumes that all servers will use the same port number.\n";
    }
    if ($opt_ibport) {
        $port = $opt_ibport;
    } elsif ($opt_port) {
        $port = $opt_port;
        $opt_port = '';
    } else {
        $port = prompt_num("Enter port number [Default is 3335]: ","3335");
    }
    return  $port;
}

sub expand_ports
{
    my @hosts = @{shift @_};
    my @ports = @{shift @_};

    my @aliases = ();

    for my $host (@hosts)
    {
        my $i = 0;
        while($i <= $#ports)
        {
            if($ports[$i] =~ /-/)
            {
                my ($first, $last) = $ports[$i] =~ /{([0-9]+)-([0-9]+)}/;
                splice @ports, $i, 1, ($first .. $last);
                push @aliases, ($host . "_p" . $ports[$i]);
            }
            else
            {
                push @aliases, ($host . "_p" . $ports[$i]);
            }
            ++$i;
        }
    }

    return (\@aliases, \@ports);
}

sub get_ionames
{
    my($ioline);
    $ioline ='';
    if (!$opt_quiet) {
        print "Next you must list the hostnames of the machines that will act as\n";
        print "I/O servers.  Acceptable syntax is \"node1, node2, ...\" or \"node{#-#,#,#}\".\n";
    }
    if (@opt_ioservers) {
        foreach $server (@opt_ioservers) {
            $ioline = $ioline . ' ' .  $server;
        }
    } else {
        $ioline = prompt_word("Enter hostnames [Default is localhost]: ","localhost");
    }

    @io_hosts = parse_hostlist($ioline, $port);
    if(@opt_ioports)
    {
        ($a, $p) = expand_ports(\@io_hosts, \@opt_ioports);
        @io_aliases = @{$a};
        @io_ports = @{$p};
    }
    else
    {
        @io_aliases = @io_hosts;
    }

    @io_aliases = sort host_sort @io_aliases;
    return (\@io_hosts, \@io_aliases, \@io_ports);
}

sub get_metanames
{
    my($metaline);
    $metaline = '';
    if (!$opt_quiet) {
        print "Now list the hostnames of the machines that will act as Metadata\n";
        print "servers.  This list may or may not overlap with the I/O server list.\n";
    }
    if (@opt_metaservers) {
        foreach $server (@opt_metaservers) {
            $metaline = $metaline . ' ' . $server;
        }
    } else {
        $metaline = prompt_word("Enter hostnames [Default is localhost]: ","localhost");
    }
    @meta_hosts = parse_hostlist($metaline, $port);
    if(@opt_metaports)
    {
        ($a, $p) = expand_ports(\@meta_hosts, \@opt_metaports);
        @meta_aliases = @{$a};
        @meta_ports = @{$p};
    }
    else
    {
        @meta_aliases = @meta_hosts;
    }

    @meta_aliases = sort host_sort @meta_aliases;

    return (\@meta_hosts, \@meta_aliases, \@meta_ports);
}

# bmi_prot_mung(): makes bmi strings of the form protocol//host:port
sub bmi_prot_mung
{
    my @bmi_array = ();
    my @hosts = @{shift @_};
    my @ports = @{shift @_};

    my $i = 0;
    for my $host (@hosts)
    {
        if(@ports)
        {
            for my $port (@ports)
            {
                $bmi_array[$i] = "$type://$host:$port";
                ++$i;
            }
        }
        else
        {
            @typebmis = map { "$_://$host:" . $port{$_} } split(',', $type);
            $bmi_array[$i] = join(',', @typebmis);
            ++$i;
        }
    }
    return @bmi_array;
}

# ---------------------
# entry point of script
# ---------------------

my $using_stdout = 0;
my $show_help = '';

$opt_quiet = 0;
GetOptions('protocol=s'    => \$opt_protocol,
       'port=i'        => \$opt_port,
       'tcpport=i'     => \$opt_tcpport,
       'gmport=i'      => \$opt_gmport,
       'ibport=i'      => \$opt_ibport,
       'ioservers=s'   => \@opt_ioservers,
       'metaservers=s' => \@opt_metaservers,
       'logfile=s'     => \$opt_logfile,
       'logging=s'     => \$opt_logging,
       'logstamp=s'    => \$opt_logstamp,
       'first-handle=i' => \$opt_first_handle,
       'last-handle=i' => \$opt_last_handle,
       'default-num-dfiles=i' => \$opt_default_num_dfiles,
       'root-handle=i' => \$opt_root_handle,
       'fsid=i'        => \$opt_fsid,
       'trusted=i'        => \$opt_security,
       'server-job-timeout=i' => \$opt_server_job_timeout,
       'client-job-timeout=i' => \$opt_client_job_timeout,
       'storage=s'     => \$opt_storage,
       'help'          => \$show_help,
       'quiet!'        => \$opt_quiet,
       'trovesync!'    => \$opt_trovesync,
       'ioports=s'     => \@opt_ioports,
       'metaports=s'   => \@opt_metaports, 
       '-'           => \$using_stdout)
    or die "Could not parse arguments.  See -h for help.\n";

if($show_help) {
    usage();
    exit;
}

if ($using_stdout) {
    $output_target = STDOUT
}
elsif (@ARGV != 2)
{
    die "Bad arguments.  See -h for help.\n";
}
else
{
    $output_target = FILEOUT;
    unless (open($output_target, ">", $ARGV[0]))
    {
        die "Can't open specified file $ARGV[0]: $!\n";
    }
}
 
die "-ioports requires -metaports." if(@opt_ioports && !@opt_metaports);
die "-metaports requires -ioports." if(@opt_metaports && !@opt_ioports);

#$num_unexp_reqs = prompt_num("How many unexpected requests should we be prepared to receive?  ");
$num_unexp_reqs = 50;

print_welcome();

$type = get_protocol();
foreach (split(',', $type)) {
    if ($_ eq "tcp") {
        $port{'tcp'} = tcp_get_port();
        if ($opt_security == 1)
        {
            $opt_trusted_port = get_portlist();
            $str = get_network();
            $cnt = 0;
            foreach (split(',', $str)) {
                $_ =~ s/\s/ /g; 
                $_ =~ s/ +/ /g;
                $_ =~ s/^ +//;
                $_ =~ s/ +$//;
                if ($cnt == 0)
                {
                    $opt_trusted_network = "tcp://" . $_;
                }
                else 
                {
                    $opt_trusted_netmask = "tcp://" . $_;
                }
                $cnt = $cnt + 1;
            }
        }
    } elsif ($_ eq "gm") {
        $port{'gm'} = gm_get_port();
    } elsif ($_ eq "ib") {
        $port{'ib'} = ib_get_port();
    } else {
        die "Sorry.  At this time, only the tcp,gm, and ib protocols are available\nfor use with this configuration utility.\n";
    }
}
$bmi_module = join(',', map("bmi_" . $_, split(',', $type)));

($h, $a, $p) = get_ionames();
@io_hosts = @{$h};
@io_aliases = @{$a};
@io_ports = @{$p};

($h, $a, $p) = get_metanames();
@meta_hosts = @{$h};
@meta_aliases = @{$a};
@meta_ports = @{$p};

# union is the union of io and meta nodes
my %nonunion_hosts = ();
foreach(@io_hosts)
{
    if(@io_ports)
    {
        for my $port (@io_ports)
        {
            $nonunion_hosts{$_ . "_p$port"}=1;
        }
    }
    else
    {
        $nonunion_hosts{$_}=1;
    }
}

foreach(@meta_hosts)
{
    if(@meta_ports)
    {
        for my $port (@meta_ports)
        {
            $nonunion_hosts{$_ . "_p$port"}=1;
        }
    }
    else
    {
        $nonunion_hosts{$_}=1;
    }
}

@union_hosts = keys %nonunion_hosts;
@union_hosts = sort host_sort @union_hosts;

# tack on BMI style notation
@io_bmi = bmi_prot_mung(\@io_hosts, \@io_ports);
@meta_bmi = bmi_prot_mung(\@meta_hosts, \@meta_ports);

my %nonunion_bmi = ();
foreach(@io_bmi, @meta_bmi)
{
    $nonunion_bmi{$_} = 1;
}

@union_bmi = keys %nonunion_bmi;
@union_bmi = sort host_sort @union_bmi;

if (!$opt_quiet) {
    print "Configured a total of ", ($#union_bmi + 1), " servers:\n";
    print "", ($#io_bmi + 1), " of them are I/O servers.\n";
    print "", ($#meta_bmi + 1), " of them are Metadata servers.\n";
    if ($opt_security == 1)
    {
        print "Configured trusted connection settings\n";
        print "Trusted port list : ", $opt_trusted_port, "\n";
        print "Trusted network,netmask : ", $opt_trusted_network, ",", $opt_trusted_netmask, "\n";
    }
}

if (!$opt_quiet) {

    $verify_svr_flag = prompt_word("Would you like to verify server list (y/n) [Default is n]? ","n");

    if($verify_svr_flag eq "y" or $verify_svr_flag eq "yes")
    {
        print "****** I/O servers:\n";
        foreach(@io_bmi)
        {
            print "$_\n";
        }
        print "\n****** Metadata servers:\n";
        foreach(@meta_bmi)
        {
            print "$_\n";
        }
        my $ok_flag = prompt_word("Does this look ok (y/n) [Default is y]? ","y");
        if(!($ok_flag eq "y" or $ok_flag eq "yes"))
        {
            die "Aborting...\n";
        }
    }
}


$logfile = get_logfile();
$storage = get_storage();
$logging = get_logging();
$logstamp = get_logstamp();
$first_handle = get_first_handle();
$last_handle = get_last_handle();
$default_num_dfiles = get_default_num_dfiles();
$root_handle = get_root_handle();
$fsid = get_fsid();
$server_job_timeout = get_server_job_timeout();
$client_job_timeout = get_client_job_timeout();

# could prompt for handle recycle time, but it is probably best if we pick a
# reasonable value w/o prompting
$handle_recycle_timeout_seconds = 360;

# ----------------------------------------------------------
# now that we have all the info, emit the configuration data
# ----------------------------------------------------------

# NOTE: we assume that server_aliases and server_addrs
# arrays are properly populated from above

if (!$opt_quiet) {
    print "Writing fs config file... ";
    if ($using_stdout == 1)
    {
        print "\n";
    }
}

emit_defaults($output_target, $num_unexp_reqs,
              $bmi_module, $handle_recycle_timeout_seconds);
if ($opt_security == 1) 
{
    emit_security($output_target, $opt_trusted_port, $opt_trusted_network, $opt_trusted_netmask);
}
emit_aliases($output_target);
emit_filesystem($output_target, "pvfs2-fs", $fsid, $root_handle);

# close fs.conf
if ($using_stdout == 0)
{
    close($output_target);
}

if (!$opt_quiet) {
    print "Done.\n";

    print "Writing ", ($#union_bmi + 1), " server config file(s)... ";
    if ($using_stdout == 1)
    {
        print "\n";
    }
}
for($i = 0; $i <= $#union_bmi; $i++)
{
    my($filename);

    # and open server.conf files
    if ($using_stdout == 0)
    {
        $filename = "$ARGV[1]-$union_hosts[$i]";
        unless (open(FILEOUT, ">", $filename))
        {
            die "Can't open specified file $ARGV[1]: $!\n";
        }
    }
    
    my $pstorage = $storage;
    my $plogfile = $logfile;
    if($union_hosts[$i] =~ /_(p[0-9]+)$/)
    {
        $pstorage .= "-" . $1;
        $plogfile .= "-" . $1;
    }

    emit_server_conf($output_target, $union_bmi[$i], $pstorage, $plogfile);

    if ($using_stdout == 0)
    {
        close(FILEOUT);
    }
}
if (!$opt_quiet) {
    print "Done.\n";
}

# Local variables:
#  c-indent-level: 4
#  c-basic-offset: 4
# End:
#  
# vim: ts=8 sts=4 sw=4 expandtab


