#!/usr/bin/perl
#
# This script is a component of Warewulf,
# http://www.runlevelzero.net/greg/warewulf
#
#########################################################################
#
# Copyright (c) 2003, The Regents of the University of California, through
# Lawrence Berkeley National Laboratory (subject to receipt of any
# required approvals from the U.S. Dept. of Energy).  All rights reserved.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# The GNU GPL Document can be found at:
# http://www.gnu.org/copyleft/gpl.html
#
#########################################################################
#
# Written and maintained by:
#       Greg Kurtzer, <gmkurtzer@lbl.gov>

use lib "/usr/lib/warewulf/", "/usr/lib64/warewulf/";
use Warewulf::Config;
use Warewulf::Util;
use File::Path;
use File::Basename;
use Getopt::Long;

my $usage = "USAGE $0 [options] [kernel version]
  About:
    Management tool for the installed VNFS file systems

  Options:
    --vnfs          What vnfs do you wish to work on (default: default)
    --build         Build the runtime VNFS node image
      --excludes    Excludes file (default: /etc/warewulf/vnfs/excludes)
      --includes    Master includes file (default: /etc/warewulf/vnfs/includes)
      --hybrid      Links things excluded to NFS mount point (RAM/NFS hybrid)
      --shell       Spawn an interactive shell in the temporary file system
    --shell         Spawn an interactive shell in the VNFS directory
    --quiet         Be quiet
    --help          Show this banner

  This tool is part of the Warewulf cluster distribution
       http://warewulf-cluster.org/
";

GetOptions(
   'build'        => \$vnfs_build,
   'vnfs=s'       => \$vnfs,
   'excludes=s'   => \$excludes,
   'includes=s'   => \$includes,
   'hybrid'       => \$nfshybrid,
   'shell'        => \$shell,
   'quiet'        => \$quiet,
   'help'         => \$help,
);

if ( $help ) {
   print $usage;
   exit;
}

if ( ! $vnfs ) {
   $vnfs = 'default';
}

%master = &master_config();

if ( ! -d "$master{'paths'}{'vnfs dir'}" ) {
   die "ERROR: the master's 'vnfs dir' is not configured!\n";
}

if ( ! -d "$master{'paths'}{'vnfs dir'}/$vnfs" ) {
   die "ERROR: I don't see the VNFS '$vnfs' defined in $master{'paths'}{'vnfs dir'}!\n";
}


$vnfs_path = $master{'paths'}{'vnfs dir'} . "/$vnfs";
$tftpboot = "/tftpboot";
$pxecfgdir = "$tftpboot/pxelinux.cfg";

if ( $vnfs_build ) {
   if ( $< != '0' ) {
      die "This operation can only be done as root!\n";
   }

   if ( ! $quiet ) {
      print "Inializing build\n";
   }

   if ( ! -d "$vnfs_path/bin" or ! -d "$vnfs_path/sbin" or ! -d "$vnfs_path/lib" ) {
      die "ERROR: No VNFS installed in: $vnfs_path\n";
   }

   if ( -f "$vnfs_path/proc/cpuinfo" ) {
      warn "WARNING: unmounting $vnfs{$vnfs}{path}/proc/...\n";
      system("umount $vnfs_path/proc");
      if ( $? != '0' ) {
         die "ERROR: could not umount $vnfs_path/proc!\n";
      }
   }

   if ( ! $excludes ) {
      $excludes_entry = "--exclude-from=/etc/warewulf/vnfs/excludes";
   } elsif ( -f "$excludes" ) {
      $excludes_entry = "--exclude-from=$excludes";
   } elsif ( -f "/etc/warewulf/vnfs/$excludes" ) {
      $excludes_entry = "--exclude-from=/etc/warewulf/vnfs/$excludes";
   } elsif ( -f "/etc/warewulf/vnfs/excludes-$excludes" ) {
      $excludes_entry = "--exclude-from=/etc/warewulf/vnfs/excludes-$excludes";
   } else {
      $excludes_entry = ();
   }

   if ( ! $includes ) {
      $includes_entry = "--files-from=/etc/warewulf/vnfs/includes";
   } elsif ( -f "$includes" ) {
      $includes_entry = "--files-from=$includes";
   } elsif ( -f "/etc/warewulf/vnfs/$includes" ) {
      $includes_entry = "--files-from=/etc/warewulf/vnfs/$includes";
   } elsif ( -f "/etc/warewulf/vnfs/includes-$includes" ) {
      $includes_entry = "--files-from=/etc/warewulf/vnfs/includes-$includes";
   } else {
      $includes_entry = ();
   }

   $random = &generate_random_string("10");
   $tmp_dir = "/var/tmp/vnfs-$random";
   mkpath("$tmp_dir") unless ( -d "$tmp_dir");

   if ( ! -d "/srv/vnfs/$vnfs" ) {
      # Build enviornment only if it didn't exsist previously
      mkpath("/srv/vnfs/$vnfs");
      system("cp /usr/lib/warewulf/wwinitrc /srv/vnfs/$vnfs/wwinitrc");
      system("cp /usr/lib/warewulf/wwpostrc /srv/vnfs/$vnfs/wwpostrc");
   }

   if ( ! $quiet ) {
      print "Building temporary VNFS template...\n";
   }
   system("cd $vnfs_path; rsync -qaSH $excludes_entry . $tmp_dir/");
   if ( $includes_entry ) {
      system("rsync -qaSH $includes_entry / $tmp_dir/ 2>/dev/null");
   }

   if ( ! $quiet ) {
      print "Configuring VNFS template...\n";
   }
   system("rm -f $tmp_dir/dev/log* 2>/dev/null");

   open(HOSTS, "> $tmp_dir/etc/hosts");
   print HOSTS "127.0.0.1\t\tlocalhost localhost.localdomain\n";
   print HOSTS "$master{'network'}{'admin ipaddr'}\t\t$master{'network'}{'nodename'}-admin\n";
   close HOSTS;

   open(RHOSTS, "> $tmp_dir/root/.rhosts");
   print RHOSTS "$master{'network'}{'admin ipaddr'} root\n";
   if ( $master{'network'}{'admin ipaddr'} ne $master{'network'}{'cluster ipaddr'} ) {
      print RHOSTS "$master{'network'}{'cluster ipaddr'} root\n";
   }
   close RHOSTS;

   open(WULFD_TEMPLATE, "$vnfs_path/etc/sysconfig/wulfd");
   while(<WULFD_TEMPLATE>) {
      chomp;
      if ( $_ =~ /^ADMIN_MASTER=$/ or $_ =~ /^ADMIN_MASTER=.?localhost.?$/ ) {
         $wulfd_out .= "ADMIN_MASTER=$master{'network'}{'nodename'}-admin\n";
      } else {
         $wulfd_out .= "$_\n";
      }
   }
   close WULFD_TEMPLATE;

   open(WULFD, "> $tmp_dir/etc/sysconfig/wulfd");
   print WULFD $wulfd_out;
   close WULFD;

   open(SYSLOG, "> $tmp_dir/etc/syslog.conf");
   print SYSLOG "*.alert\t\t\t\@$master{'servers'}{'syslog'}\n";
   print SYSLOG "local7.*\t\t\@$master{'servers'}{'syslog'}\n";
   print SYSLOG "*.emerg\t\t\t\@$master{'servers'}{'syslog'}\n";
   close SYSLOG;

   open(FSTAB, "$tmp_dir/etc/fstab");
   while(<FSTAB>) {
      $fstab_out .= $_;
   }
   close FSTAB;
   
   $fstab_out =~ s/%{sharedfs entry}/$master{'servers'}{'home nfs'}:$master{'paths'}{'sharedfs dir'}\t$master{'paths'}{'sharedfs dir'}\tnfs\tnfsvers=2,udp\t0 0/g;
   if ( $nfshybrid ) {

      $fstab_out =~ s/%{vnfs entry}/$master{'servers'}{'vnfs nfs'}:$master{'paths'}{'vnfs dir'}\t$master{'paths'}{'vnfs dir'}\tnfs\tnfsvers=2,udp\t0 0/g;
   } else {
      $fstab_out =~ s/%{vnfs entry}/# VNFS entry not added because hybrid build was not enabled/g;
   }

   open(FSTAB, "> $tmp_dir/etc/fstab");
   print FSTAB $fstab_out;
   close FSTAB;

   if ( $nfshybrid ) {
      if ( ! $quiet ) {
         print "Building hybrid links...\n";
      }
      open(FILES, "cd $tmp_dir; find . |");
      while(<FILES>) {
         chomp;
         if ( $_ =~ /^\/dev/ ) {
            next;
         }
         if ( -f "$tmp_dir/$_" ) {
            $vnfs_file{"$_"} = '1';
         } elsif ( -d "$tmp_dir/$_" ) {
            $vnfs_dir{"$_"} = '1';
         }
      }
      close FILES;
      open(FILES, "cd $vnfs_path; find . -type f | sort |");
      while(<FILES>) {
         chomp;
         if ( $_ =~ /^\/dev/ ) {
            next;
         }
         if ( ! -f "$tmp_dir/$_" ) {
            #print "symlink file /vnfs/$vnfs/$_, $tmp_dir/$_\n";
            symlink("/vnfs/$vnfs/$_", "$tmp_dir/$_");
         }
      }
      open(FILES, "cd $vnfs_path; find . -type l | sort |");
      while(<FILES>) {
         chomp;
         if ( $_ =~ /^\/dev/ ) {
            next;
         }
         if ( ! -f "$tmp_dir/$_" ) {
            #print "symlink file /vnfs/$vnfs/$_, $tmp_dir/$_\n";
            symlink("/vnfs/$vnfs/$_", "$tmp_dir/$_");
         }
      }
      close FILES;
      open(DIRS, "cd $vnfs_path; find . -type d | sort |");
      while(<DIRS>) {
         chomp;
         if ( $_ =~ /^\/dev/ ) {
            next;
         }
         if ( ! -d "$tmp_dir/$_" ) {
            #print "symlink dir /vnfs/$vnfs/$_, $tmp_dir/$_\n";
            symlink("/vnfs/$vnfs/$_", "$tmp_dir/$_");
         }
      }
      close DIRS;
      system("mkdir -p $tmp_dir/$master{'paths'}{'vnfs dir'}");
   }

   if ( -f "/var/warewulf/vnfs-version" ) {
      open(VERS, "/var/warewulf/vnfs-version");
      while(<VERS>){
         chomp;
         $version = $_;
         last;
      }
      close VERS;
   }
   if ( $version =~ /^\d+$/ ) {
      $version++;
   } else {
      $version = 1;
   }
   print "Versioning the VNFS at $version\n";

   open(VERS, "> /var/warewulf/vnfs-version");
   print VERS $version;
   close VERS;
   open(VERS, "> $tmp_dir/.vnfs-version");
   print VERS $version;
   close VERS;

   if ( $shell ) {
      print "Spawning a shell in the temporary file system, type 'exit' when done.\n";
      system("/usr/sbin/chroot $tmp_dir /bin/sh -l");
      if ( ! $quiet ) {
         print "Continuing with the build...\n";
      }
   }

   if ( ! $quiet ) {
      print "Building VNFS image...\n";
   }
   system("cd $tmp_dir; tar czf /srv/vnfs/$vnfs/vnfs.tar.gz .");

   chomp($fssize=`du -sm $tmp_dir | awk '{print \$1}'`);
   chomp($txsize=`du -sm /srv/vnfs/$vnfs/vnfs.tar.gz | awk '{print \$1}'`);
   if ( ! $quiet ) {
      print "Cleaning up...\n";
   }
   system("rm -rf $tmp_dir");

   print "VNFS '$vnfs': system is $fssize MB, transfer size is $txsize MB\n";
} elsif ( $shell ) {
   print "Spawning a shell in the VNFS '$vnfs', type 'exit' when done.\n";
   system("/usr/sbin/chroot $vnfs_path /bin/sh -l");
} else {
   print $usage;
}

