Hi all,
Ferrol has been working on the extension of SystemImager for a diskless
support. He did a pretty good job, the implementation being not intrusive.
Please find a description of the modification, a patch for si_prepareclient and
a new Perl module for SystemImager that actually implements the diskless
support. It does not aim at being perfect but it is a small extension that
enables a basic diskless support, and we think the community may be interested
by such a capability.
Is it something that could be included directly into SystemImager?
Of course, if you have any questions or remarks, feel free to contact us.
Thanks,
Description of changes made:
----------------------------
1. The user needs only modify a configuration file. This file is
/etc/systemimager/diskless.conf.
<ip> <path_to_kernel> <method_of_boot>
The <method_of_boot> section can define three methods:
nfs-root, ramfs-rsync, ramfs-http.
2. /usr/sbin/si_prepareclient was modified in order to include a flag that can
be used to signify whether or not modifications should be done.
The proper call is:
/usr/sbin/si_prepareclient --diskless
It looks to see if that flag is there and then calls a function named
diskless(). This function makes use of a perl module,
/usr/lib/systemimager/perl/SystemImager/Diskless.pm, that contains
most of the code that was used in si_mkdiskless.
After the function, diskless(), returns si_prepareclient exits. This
can be changed, it was just seen as a possible annoyance to have some
parts of si_prepareclient to run if the user only wanted the diskless
portion to run.
--- /usr/sbin/si_prepareclient 2007-12-06 06:18:34.000000000 -0500
+++ ./si_prepareclient 2008-04-22 11:19:07.000000000 -0400
@@ -6,6 +6,10 @@
# Copyright (C) 2002 Internation Business Machines
# Sean Dague <[EMAIL PROTECTED]>
#
+# Copyright (c) Oak Ridge National Labratory
+# Ferrol Aderholdt <[EMAIL PROTECTED]>
+# All rights reserved
+#
# $Id: si_prepareclient 4336 2007-11-12 15:01:04Z arighi $
# vi:set filetype=perl:
#
@@ -83,6 +87,7 @@
use vars qw($VERSION);
use SystemImager::Common;
use SystemImager::UseYourOwnKernel;
+use SystemImager::Diskless;
# set version
$VERSION = "4.0.2";
@@ -220,6 +225,11 @@
/usr/share/doc/systemimager-server-4.0.2/local.cfg
for a well commented example.
+ --diskless
+ Indicates that diskless nodes are present in
+ /etc/systemimager/diskless.conf file. This will then do what is needed
+ for those nodes to boot.
+
Download, report bugs, and make suggestions at:
http://systemimager.org/
@@ -241,6 +251,7 @@
"image=s" => \my $image,
"filesystem=s" => \my $filesystem,
"version" => \my $version,
+ "diskless" => \my $diskless,
"server=s" => \my $server,
"yes" => \my $yes,
"ssh-key=s" => \my $ssh_key,
@@ -268,6 +279,11 @@
exit 0;
}
+if($diskless) {
+ &diskless();
+ exit 0;
+}
+
# bail if not root
if ($> != 0) {
print "Must be run as root!\n";
@@ -1253,6 +1269,50 @@
return @ips;
}
+##############################################################################
+#
+# Description:
+# Does what is necessary so that any diskless nodes will boot properly.
+#
+# Added in on April 15, 2008 by Ferrol Aderholdt
+#
+# usage:
+# diskless();
+sub diskless {
+ my $diskless_file = "/etc/systemimager/diskless.conf";
+ my ($is_nfs, $is_http, $is_rsync) = SystemImager::Diskless::computenode_client_info($diskless_file);
+ SystemImager::Diskless::fix_pxe();
+
+ if($is_nfs) {
+ print "Starting NFS-Root modifications...\n";
+ my $distro = "fedora" if -e "/etc/fedora-release";
+ $distro = "debian" if -e "/etc/debian_version";
+ SystemImager::Diskless::fix_dhcpd();
+ SystemImager::Diskless::fix_exports();
+ SystemImager::Diskless::move_kernels();
+ SystemImager::Diskless::create_skeleton_directory_structure();
+ SystemImager::Diskless::fix_initrc($distro);
+ SystemImager::Diskless::fix_network($distro);
+ SystemImager::Diskless::modify_node_fstab();
+ SystemImager::Diskless::setup_node_swap();
+ SystemImager::Diskless::si_report();
+ print "Finished NFS-Root modifications...\n";
+ }
+ if($is_rsync) {
+ print "Starting ramfs-rsync modifications...\n";
+ SystemImager::Diskless::modify_rsyncd();
+ SystemImager::Diskless::create_initrd();
+ print "Finished ramfs-rsync modifications...\n";
+ }
+ if($is_http) {
+ print "Starting ramfs-http modifications...\n";
+ SystemImager::Diskless::create_tarballs();
+ SystemImager::Diskless::create_initrd();
+ print "Finished ramfs-http modifications...\n";
+ }
+
+ print "Diskless modifications complete.\n";
+}
### END functions
package SystemImager::Diskless;
#
# Copyright (c) Oak Ridge National Laboratory
# Ferrol Aderholdt <[EMAIL PROTECTED]>
# All rights reserved
#
#
use lib "/usr/lib/systemimager/perl","/usr/lib/systeminstaller";
use Carp;
use Cwd;
use File::Copy;
use File::Path;
use File::stat;
use SIS::DB;
use SIS::Client;
use SIS::Adapter;
use SIS::Image;
use SystemImager::Config;
use SystemImager::Server;
use strict;
our $config = $SystemImager::Config::config;
############################################################
#
# Subroutines in this module include:
#
# computenode_client_info
# create_skeleton_directory_structure
# find_ip
# find_image_path
# fix_dhcpd
# fix_exports
# fix_initrc
# fix_network
# fix_pxe
# ip2subnet
# ip2hex
# modify_node_fstab
# move_kernels
# setup_node_swap
# si_report
# create_tarballs
# create_initrd
# generate_linuxrc
# modify_rsyncd
#
###########################################################
my @computenode_ips;
my @ramfs_http_ips;
my @ramfs_rsync_ips;
my %ip_to_kernel = ();
my %ip_to_initrd = ();
my %ip_to_image = ();
my %ip_to_host = ();
my %paths = ();
my @images_used;
###########################
# computenode_client_info
#
# reads a diskless config file that is passed in to discover
# all diskless nodes and fills in the proper hashes
# and arrays to reflect this. returns a tuple of values
# that are either 0, for false, or 1, for true, to
# indicate if there are any nodes with a diskless
# boot using NFS-Root, a ramfs diskless boot using tarballs
# and httpd server, or ramfs diskless boot using rsync.
#
# Usage:
# ($nfs, $ramfs_http, $ramfs_rsync) = computenode_client_info($filename)
###########################
sub computenode_client_info {
my $file = shift;
my @computenodes;
my @ramfs_computenodes;
my %ip_to_type = ();
my @hosts = &list_client();
my @computenode_temp = &list_adapter();
my ($is_nfs, $is_http, $is_rsync) = (0, 0, 0);
my $line_count = 1;
open(FILE, "<$file") or croak("could not open $file for reading $!");
while(<FILE>) {
chomp($_);
my $temp_ip = "";
my $temp_kernel = "";
my $temp_type = "";
#remove comments
$_ =~ s/(.*)\s*#.*/$1/;
unless($_ =~ /.+/) {
next;
}
#made this section more strict to the user
#so that there aren't major problems later
if( /(.*) (.*) (.*)/ ) {
$temp_ip = $1;
$temp_kernel = $2;
$temp_type = $3;
#} elsif( /(.*) (.*)/ ) {
# $temp_ip = $1;
# $temp_kernel = $2;
} else {
croak("Config file $file is not written correctly");
}
#do some error checking
if($temp_ip =~ m/[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+/) {
push(@computenodes, $temp_ip);
} else {
croak("Invalid IP used on line $line_count of $file");
}
#no longer guessing kernel since that is not a good
#fix for user laziness
#map ips for computenodes to kernels
#also map ips for computenodes to initrds if needed
$ip_to_kernel{$temp_ip} = $temp_kernel;
$ip_to_type{$temp_ip} = $temp_type;
$line_count++;
}
close(FILE);
#no work needs to be done, leave...
unless(@computenodes) {
print "No work to be done. Leaving...\n";
exit 0;
}
#more error checking
foreach my $ips (@computenodes) {
foreach my $temp_ip (@computenode_temp) {
if($temp_ip->ip =~ /$ips/) {
#this is loosely checked...
if($ip_to_type{$ips} =~ /ramfs-rsync/) {
$is_rsync = 1;
push(@ramfs_rsync_ips, $ips);
} elsif($ip_to_type{$ips} =~ /ramfs-http/) {
$is_http = 1;
push(@ramfs_http_ips, $ips);
} else {
$is_nfs = 1;
push(@computenode_ips, $ips);
}
# if($ip_to_kernel{$ips} =~ /ramfs/) {
# if($ip_to_kernel{$ips} =~ /http/) {
# $is_http = 1;
# push(@ramfs_http_ips, $ips);
# } else {
# $is_rsync = 1;
# push(@ramfs_rsync_ips, $ips);
# }
# } else {
# $is_nfs = 1;
# push(@computenode_ips, $ips);
# }
foreach my $temp_hosts (@hosts) {
if($temp_hosts->hostname =~ $temp_ip->client) {
my $hostname = $temp_hosts->hostname;
#strip out domain information
$hostname =~ s/(.*)\..*\..*/$1/;
$ip_to_image{$ips} = $temp_hosts->imagename;
$ip_to_host{$ips} = $hostname;
push(@images_used, $temp_hosts->imagename);
}
}
}
}
}
return ($is_nfs, $is_http, $is_rsync);
}
###########################################################
# create_skeleton_directory_structure
#
# creates a directory structure for the nodes using
# NFS-Root. This requires that computenode_client_info
# be called before this or else it will not work properly.
#
# Usage:
# create_skeleton_directory_structure();
###########################################################
sub create_skeleton_directory_structure {
my @shared_directories = qw/media mnt scratch selinux srv usr/;
my @unshared_directories = qw/bin dev etc home lib opt proc root sbin sys var/;
my $tftpdir = "/tftpboot";
foreach my $image (@images_used) {
my $imagedir = &find_image_path($image);
#example: create the /tftpboot/192.168.0.2
foreach my $ips (@computenode_ips) {
unless(-d "$tftpdir/$ips") {
mkpath("$tftpdir/$ips");
}
foreach my $dir (@shared_directories, @unshared_directories) {
unless(-d "$tftpdir/$ips/$dir") {
mkpath("$tftpdir/$ips/$dir");
}
}
#/lib won't mount correctly through NFS-Root.
#copy over necessary files then mount it with nfs
foreach my $dir (@unshared_directories) {
if($dir =~ /lib/) {
system("cp -u $imagedir/$dir/* $tftpdir/$ips/$dir 2>/dev/null");
} else {
!system("cp -ur --remove-destination $imagedir/$dir $tftpdir/$ips")
or croak("Could not copy $imagedir/$dir to $tftpdir/$ips");
}
}
}
}
}
###########################################################
# find_ip
#
# find_ip opens /etc/hosts and looks for the ip containing
# oscar_server. it then returns both the headnode ip and
# netmask.
#
# Usage:
# ($ip, $netmask) = find_ip();
# my ($ip, $netmask) = find_ip();
###########################################################
sub find_ip {
my ($headnode_ip, $headnode_netmask) = ("","");
my $test_ip;
#look for line in /etc/hosts containing oscar_server
open(IN, "</etc/hosts");
while(<IN>) {
if(/^(\d+.\d+.\d+.\d+).*oscar_server.*/) {
$test_ip = $1;
last;
}
}
close(IN);
#checking for use
open(IN, "/sbin/ifconfig |") or croak("Couldn't run /sbin/ifconfig");
while(<IN>) {
if(/^.*:(\d+.\d+.\d+.\d+).*:.*:(\d+.\d+.\d+.\d+)\s*$/) {
($headnode_ip, $headnode_netmask) = ($1, $2);
if($test_ip =~ m/$headnode_ip/) {
last;
}
}
}
close(IN);
#just one last error check
unless($test_ip =~ /$headnode_ip/) {
croak("$test_ip is not actually in use. Fix this and run again.");
}
return ($headnode_ip, $headnode_netmask);
}
###########################################################
# find_image_path
#
# this finds the path to the image passed in
# and then returns it.
#
# Usage:
# my $path = find_image_path($image_name);
###########################################################
sub find_image_path {
my $image = shift;
my @list_of_images = &list_image();
my $path;
#find matching path
foreach my $test_image (@list_of_images) {
if($test_image->name =~ /$image/) {
$path = $test_image->location;
last;
}
}
return $path;
}
###########################################################
# fix_dhcpd
#
# this looks for and adds in, if not already there, the
# line "authoritative" to the dhpcd.conf file wherever it
# may be. this is so computenodes using NFS-Root will boot
# properly.
#
# Usage:
# fix_dhcpd();
###########################################################
sub fix_dhcpd {
my ($ip, $netmask) = &find_ip();
my $dhcpd = "/etc/dhcpd.conf"; #for fedora
$dhcpd = "/etc/dhcp3/dhcpd.conf" if -d "/etc/dhcp3"; #for debian
my $authoritative_line = "authoritative";
my $cmd = "/etc/init.d/dhcpd restart";
#make sure not authoritative is not in the dhcpd.conf
if(system("grep -qG \"^[a-z]*[[:space:]]$authoritative_line;\" $dhcpd")) {
copy($dhcpd, "$dhcpd.bak");
open(IN, "<$dhcpd.bak") or croak("Couldnt open $dhcpd.bak for $!");
open(OUT, ">$dhcpd") or croak("Couldnt open $dhcpd for $!");
my $flag = 0;
my $subnet = &ip2subnet($ip, $netmask);
while(<IN>) {
if(/$subnet/) {
$flag = 1;
}
print OUT "$_";
if($flag) {
print OUT "\t$authoritative_line; #added in for nfs-root\n";
$flag = 0;
}
}
close(OUT);
close(IN);
}
#restart dhcp server to make
if($dhcpd =~ /.*3.*/) {
$cmd = "/etc/init.d/dhcp3-server restart";
}
!system($cmd) or croak("Couldn't restart dhcpd server");
}
###########################################################
# fix_exports
#
# this guarantees that the NFS-Root computenodes will be
# able to mount. this requires that computenode_client_info
# be called before it is called.
#
# Usage:
# fix_exports();
###########################################################
sub fix_exports {
my $exports = "/etc/exports";
my ($ip, $netmask) = &find_ip();
my $tftpdir = "/tftpboot";
my $nfs = "nfs"; #supports fedora/redhat
$nfs = "nfsserver" if -x "/etc/init.d/nfsserver"; #suse
$nfs = "nfs-kernel-server" if -x "/etc/init.d/nfs-kernel-server"; #debian
$nfs = "nfs-user-server" if -x "/etc/init.d/nfs-user-server"; #also debian
#if the line isn't there, add it
if(system("grep -q /lib $exports")) {
system("echo \"/lib $ip/$netmask(async,ro,no_root_squash)\" >> /etc/exports");
}
#check for lines for /usr /srv and /selinux
foreach my $image (@images_used) {
my $imagedir = &find_image_path($image);
if(system("grep -q $imagedir/usr $exports")) {
open (OUT, ">>$exports");
print OUT "$imagedir/usr $ip/$netmask(async,ro,no_root_squash)\n";
close (OUT);
}
if(system("grep -q $imagedir/srv $exports")) {
open (OUT, ">>$exports");
print OUT "$imagedir/srv $ip/$netmask(async,ro,no_root_squash)\n";
close (OUT);
}
if(system("grep -q $imagedir/selinux $exports")) {
open (OUT, ">>$exports");
print OUT "$imagedir/selinux $ip/$netmask(async,ro,no_root_squash)\n";
close (OUT);
}
}
foreach my $ips (@computenode_ips) {
my $flag = 0;
open (IN, "<$exports");
open (OUT, ">>$exports");
while(<IN>) {
if( m.$tftpdir/$ips .) {
$flag = 1;
last;
}
}
#need to share $tftpdir/$ips for computenodes to
#even boot.
unless($flag) {
print OUT "$tftpdir/$ips $ip/$netmask(async,rw,no_root_squash)\n";
}
close (OUT);
close (IN);
}
!system("/etc/init.d/$nfs restart") or croak("Couldn't restart $nfs");
}
###########################################################
# fix_initrc
#
# fixes the boot of the NFS-Root computenodes so that
# they will boot properly. this requires
# computenode_client_info be called before hand.
#
# Usage:
# fix_initrc($distro_name); # either fedora or debian
###########################################################
sub fix_initrc {
my $distro = shift;
my $tftpdir = "/tftpboot";
my ($ip, $netmask) = &find_ip();
if($distro =~ /fedora/) {
foreach my $ips (@computenode_ips) {
my $file = "$tftpdir/$ips/etc/rc.sysinit";
my $backup_file = $file.".bak";
#fixed?
if(system("grep -q \"/var/lib/systemimager/\" $tftpdir/$ips/etc/rc.sysinit")) {
!system("cp $file $backup_file")
or croak("Couldn't copy $file to $backup_file");
open(IN, "<$backup_file") or croak("Couldn't open $backup_file");
open(OUT, ">$file") or croak("Couldn't open $file");
my $flag = 0; #tells when to place new lines in file
while(<IN>) {
if($flag) {
my $image = $ip_to_image{$ips};
print OUT "\n\trpc.lockd\n\tportmap\n";
print OUT "\tmount -n -t nfs $ip:/var/lib/systemimager/images/$image/usr /usr\n";
print OUT "\tmount -n -t nfs $ip:/lib /lib\n";
print OUT "\tmount -n -t nfs $ip:/var/lib/systemimager/images/$image/selinux /selinux\n";
print OUT "\tmount -n -t nfs $ip:/var/lib/systemimager/images/$image/srv /srv\n";
print OUT "\tmount -n -t tmpfs tmpfs /tmp -o size=4G\n";
$flag = 0;
} elsif(/mount.*sysfs/) {
$flag = 1;
}
print OUT "$_";
}
close (IN);
close (OUT);
}
}
} else { #only also have debian.... should also support suse later
#need to modify rc.local, then mv Swhatever rc.local in rc2.d to S05
foreach my $ips (@computenode_ips) {
my $file = "$tftpdir/$ips/etc/rc.local";
my $backup_file = $file.".bak";
copy($file, $backup_file);
open(OUT, ">$file");
my $image = $ip_to_image{$ips};
print OUT "\n\trpc.lockd\n\tportmap\n";
print OUT "\tmount -n -t nfs $ip:/var/lib/systemimager/images/$image/usr /usr\n";
print OUT "\tmount -n -t nfs $ip:/lib /lib\n";
print OUT "\tmount -n -t nfs $ip:/var/lib/systemimager/images/$image/selinux /selinux\n";
print OUT "\tmount -n -t nfs $ip:/var/lib/systemimager/images/$image/srv /srv\n";
print OUT "\tmount -n -t tmpfs tmpfs /tmp -o size=4G\n";
print OUT "exit 0\n";
close (OUT);
open(IN, "ls $tftpdir/$ips/etc/rc2.d/ |")
or croak("Couldn't run: ls $tftpdir/$ips/etc/rc2.d/");
while(<IN>) {
if(/rc\.local/) {
!system("mv $_ S05rc.local")
or croak("Couldn't rename $_");
}
}
close(IN);
}
}
}
###########################################################
# fix_network
#
# during boot some services will not start unless the
# file /etc/sysconfig/network is present for fedora
# machines. the distro is passed in and actions are taken
# for a proper boot.
#
# Usage:
# fix_network($distro_name); #either fedora or debian
###########################################################
sub fix_network {
my $distro = shift;
my $tftpdir = "/tftpboot";
foreach my $ips (@computenode_ips) {
my $hostname = $ip_to_host{$ips};
if($distro =~ /fedora/) {
open(IN, "</etc/sysconfig/network")
or croak("Couldn't open /etc/sysconfig/network");
open(OUT, ">$tftpdir/$ips/etc/sysconfig/network")
or croak("Couldn't open $tftpdir/$ips/etc/sysconfig/network for writing");
while(<IN>) {
if(/HOSTNAME/) {
#make sure compute nodes boot correctly
print OUT "HOSTNAME=$hostname\n";
} else {
print OUT "$_";
}
}
} else {
#something tells me this won't work...
open(OUT, ">$tftpdir/$ips/etc/hostname");
}
close(OUT);
close(IN);
}
}
###########################################################
# fix_pxe
#
# fix_pxe writes pxe boot files so that diskless
# nodes will not use the systemimager generated
# default.
#
# Usage:
# fix_pxe();
###########################################################
sub fix_pxe {
my ($ip,$netmask) = &find_ip();
my $tftpdir = "/tftpboot";
my $pxe =<<"EOF";
DEFAULT systemimager
#
# Uncomment next line to send pxelinux boot prompt over serial port 0.
# NOTE: Be sure your serial port speed is appropriate (57600, 9600, etc.)
#
# SERIAL 0 57600
DISPLAY message.txt
PROMPT 1
TIMEOUT 50
# Add the following to the append line above to use your first serial port
# (ttyS0) as a console in addition to your monitor (tty0). NOTE: Be sure
# your serial port speed is appropriate (57600, 9600, etc.)
#
# console=ttyS0,57600
# Add the following to the append line above to increase the size of your tmpfs
# filesystem. About 100MB larger than your image size should suffice.
#
#
# tmpfs_size=800M
# ramdisk_size=80000
#
# Other tmpfs mount options are also supported.
#
# See http://wiki.systemimager.org/index.php/Troubleshooting for details.
#
EOF
#for compute nodes using NFS-Root
foreach my $ips (@computenode_ips) {
my $append = "APPEND vga=normal root=/dev/nfs rw ";
$append .= "nfsroot=$ip:$tftpdir/$ips/ ip=dhcp\n\n";
my $temp_kernel = $ip_to_kernel{$ips};
#remove the path and keep only the name for the kernel
# unless($temp_kernel =~ m/ramfs/) {
$temp_kernel =~ s,.*/(.*)$,$1,;
# }
open(OUT, ">$tftpdir/pxelinux.cfg/".ip2hex($ips))
or croak("Couldn't open pxe file for writing");
print OUT "$pxe";
print OUT "LABEL systemimager\n";
print OUT "KERNEL $temp_kernel\n";
print OUT "$append";
close(OUT);
}
#for compute nodes using ramfs via a tarball
foreach my $ips (@ramfs_http_ips) {
my $append = "APPEND vga=extended initrd=ramfs-http-initrd.img";
$append .= " root=/dev/ram MONITOR_SERVER=$ip MONITOR_CONSOLE=yes";
my $temp_kernel = $ip_to_kernel{$ips};
open(OUT, ">$tftpdir/pxelinux.cfg/".ip2hex($ips))
or croak("Couldnt open pxe file for writing");
$temp_kernel =~ s,.*/(.*)$,$1,;
print OUT "$pxe";
print OUT "LABEL systemimager\n";
print OUT "KERNEL $temp_kernel\n";
print OUT "$append";
close(OUT);
}
foreach my $ips (@ramfs_rsync_ips) {
my $append = "APPEND vga=extended initrd=ramfs-rsync-initrd.img ";
$append .= "root=/dev/ram MONITOR_SERVER=$ip MONITOR_CONSOLE=yes";
my $temp_kernel = $ip_to_kernel{$ips};
$temp_kernel =~ s,.*/(.*)$,$1,;
print "$tftpdir/pxelinux.cfg/".ip2hex($ips);
open(OUT, ">$tftpdir/pxelinux.cfg/".ip2hex($ips))
or croak("Couldnt open pxe file");
print OUT "$pxe";
print OUT "LABEL systemimager\n";
print OUT "KERNEL $temp_kernel\n";
print OUT "$append";
close(OUT);
}
}
###########################################################
# ip2subnet
#
# ip2subnet takes an ip and netmask and returns the
# subnet.
#
# Usage:
# my $subnet = ip2subnet($ip, $netmask);
###########################################################
sub ip2subnet {
my $temp_ip = shift;
my $temp_net = shift;
my ($ip_1, $ip_2, $ip_3, $ip_4);
my ($net_1, $net_2, $net_3, $net_4);
my ($final_1, $final_2, $final_3, $final_4);
$ip_1 = $ip_2 = $ip_3 = $ip_4 = $temp_ip;
$net_1 = $net_2 = $net_3 = $net_4 = $temp_net;
#find which octet goes with which variable
$ip_1 =~ s/([0-9]*)\..*/$1/;
$ip_2 =~ s/[0-9]*\.([0-9]*)\..*/$1/;
$ip_3 =~ s/[0-9]*\.[0-9]*\.([0-9]*)\..*/$1/;
$ip_4 =~ s/[0-9]*\.[0-9]*\.[0-9]*\.(.*)/$1/;
$net_1 =~ s/([0-9]*)\..*/$1/;
$net_2 =~ s/[0-9]*\.([0-9]*)\..*/$1/;
$net_3 =~ s/[0-9]*\.[0-9]*\.([0-9]*)\..*/$1/;
$net_4 =~ s/[0-9]*\.[0-9]*\.[0-9]*\.(.*)/$1/;
#simple ANDs
$final_1 = int($ip_1) & int($net_1);
$final_2 = int($ip_2) & int($net_2);
$final_3 = int($ip_3) & int($net_3);
$final_4 = int($ip_4) & int($net_4);
my $string_ip = sprintf("%d.%d.%d.%d",$final_1,$final_2,$final_3,$final_4);
return $string_ip;
}
#########################
# taken from HostRange.pm verbatim
#########################
sub ip2hex {
my @bytes = split(/\./, $_[0]);
return 0 unless @bytes == 4 && ! grep {!(/\d+$/ && ($_ <= 255) && ($_ >= 0))} @bytes;
return sprintf("%02X%02X%02X%02X", @bytes);
}
###########################################################
# modify_node_fstab
#
# modifies the computenode's fstab file since it is
# normally written by systemconfigurator. requires
# that computenode_client_info be called prior.
#
# modify_node_fstab();
###########################################################
sub modify_node_fstab {
my ($ip, $netmask) = &find_ip();
my $tftpdir = "/tftpboot";
my $fstab =<<"EOF";
proc /proc proc defaults 0 0
sysfs /sys sysfs defaults 0 0
devpts /dev/pts devpts defaults 0 0
$ip:/home /home nfs defaults 1 1
EOF
foreach my $ips (@computenode_ips) {
open(OUT, ">$tftpdir/$ips/etc/fstab")
or croak("Couldn't write fstab for computenode");
print OUT $fstab;
}
close(OUT);
}
#######################
# move_kernels
#
# simply copies the kernel from location presented
# in conf file to /tftpboot
#
# Usage:
# move_kernels();
#######################
sub move_kernels {
my $tftpdir = "/tftpboot";
foreach my $ips (@computenode_ips, @ramfs_http_ips, @ramfs_rsync_ips) {
if($ip_to_kernel{$ips}) {
copy($ip_to_kernel{$ips}, $tftpdir) or
croak("Could not find $ip_to_kernel{$ips}");
} else {
croak("Cannot find kernels for use with node $ips\n");
}
}
}
###########################################################
# setup_node_swap
#
# this looks for any hds with a swap partition and then
# mounts it.
#
# Usage:
# setup_node_swap();
###########################################################
sub setup_node_swap {
my $tftpdir = "/tftpboot";
my $swaps=<<"EOF";
#!/bin/bash
cdroms=\'cat /proc/sys/dev/cdrom/info 2>/dev/null | grep "drive name:" | sed -e "s/^drive name:[[:space:]]*//"\'
if [ ! -z \${cdroms} ]
then
hd=\'cat /proc/diskstats | grep -e ".*[h,s]d[a-z]" | sed -e "s/.*\\([h,s]d[a-z]\\).*/\\1/" | sort -u | grep -v \${cdroms}\'
fi
if [ -z \${hd} ]
then
echo "no hard drives found... bailing out"
else
for test in \${hd}
do
swap=\'fdisk -l /dev/\${test} | grep "Linux swap" | sed -e "s/\\/dev/\\/\${test}\\([0-9]*\\)[[:space:]].*/\\1/"\'
swapon /dev/\${test}\${swap}
done
for test in \${hd}
do
num=\'fdisk -l /dev/\${test} | grep " 83 " | sed -e "s/\\/dev/\\/\${test}\\([0-9]*\\).*/\\1/"\'
mkdir -p /mnt/scratch-\${test}\${num}
mount /dev/\${test}\${num} /mnt/scratch-\${test}\${num}
done
fi
EOF
foreach my $ips (@computenode_ips) {
open(OUT, ">$tftpdir/$ips/etc/diskless_swaps");
print OUT $swaps;
close(OUT);
system("chmod 755 $tftpdir/$ips/etc/diskless_swaps");
if(system("grep -q diskless_swaps $tftpdir/$ips/etc/rc.local")) {
system("grep -v \"exit 0\" $tftpdir/$ips/etc/rc.local > $tftpdir/$ips/etc/rc.local.temp");
system("cat $tftpdir/$ips/etc/rc.local.temp > $tftpdir/$ips/etc/rc.local");
system("chmod 755 $tftpdir/$ips/etc/rc.local");
open(LOCAL, ">>$tftpdir/$ips/etc/rc.local");
print LOCAL "/etc/diskless_swaps\nexit 0\n";
close(LOCAL);
}
}
}
###########################################################
# si_report
#
# writes a script that is taken straight from systemimager
# that will report to the si_monitor on the headnode.
#
# Usage:
# si_report();
###########################################################
sub si_report {
my ($ip, $netmask) = &find_ip();
my $tftpdir = "/tftpboot";
my $report=<<"EOF";
#!/bin/bash
#
# Description: a modded out version of 95all.monitord_rebooted from si
#
TIMEOUT=30
write_monitor_msg() {
MONITOR_SERVER=$ip
MONITOR_PORT=8181
msg=`echo "\$\@"`
if [ -z "\$mac" ]; then
mac=`ifconfig eth0 2>/dev/null | sed -ne "s/.*HWaddr //p" | sed "s/ //g" | sed s/:/./g`
fi
#not an easy way to determine, accurately, the imagename
#just send the distro name for now
if [ -e /etc/redhat-release ]; then
distro='redhat/fedora'
elif [ -e /etc/SuSE-release ]; then
distro='SuSE'
elif [ -e /etc/debian_version ]; then
distro='debian'
fi
kernel=`uname -r` #determine kernel version
hostname=`uname -n`
ipaddr=`hostname --ip-address`
total_mem=`awk '{ print \$2 }' <(free -k | grep Mem:)`
used_mem=`awk '{ print \$3 }' <(free -k | grep Mem:)`
send_msg=`echo "mac=\$mac:ip=\$ipaddr:host=\$hostname:kernel=\$kernel:mem=\$total_mem:tmpfs=\$used_mem:os=\$distro:\$msg"`
netcat=`(which netcet || which nc) 2>/dev/null`
echo "\$send_msg" | \$netcat -w \$TIMEOUT \$MONITOR_SERVER \$MONITOR_PORT
}
write_monitor_msg "status=102:speed=0"
EOF
#give the compute nodes the report to send back
foreach my $ips (@computenode_ips) {
open(OUT,">$tftpdir/$ips/etc/report_to_monitor");
print OUT $report;
close (OUT);
system("chmod 755 $tftpdir/$ips/etc/report_to_monitor");
if(system("grep -q report_to_monitor $tftpdir/$ips/etc/rc.local")) {
system("grep -v \"exit 0\" $tftpdir/$ips/etc/rc.local > $tftpdir/$ips/etc/rc.local.temp"); #soo help me if this breaks something
system("cat $tftpdir/$ips/etc/rc.local.temp > $tftpdir/$ips/etc/rc.local");
system("chmod 755 $tftpdir/$ips/etc/rc.local"); #insurance
open(LOCAL, ">>$tftpdir/$ips/etc/rc.local"); #only for fedora right now...need to change this
print LOCAL "/etc/report_to_monitor\nexit 0\n";
close (LOCAL);
}
}
}
###########################################################
# create_tarballs
#
# if there are any nodes that are using the tarball/http
# method, this will create the tarballs and place them
# in the proper place.
#
# Usage:
# create_tarballs();
###########################################################
sub create_tarballs {
for my $test (@ramfs_http_ips) {
my $image = $ip_to_image{$test};
my $path = &find_image_path($image);
my $pwd = getcwd(); #come back to this
chdir("$path/");
my $tarball = "/var/www/tar.$image.bz2";
$tarball = "/var/www/html/tar.$image.bz2" if -d "/var/www/html";
my $symlink = "/var/www/tar.$test.bz2";
$symlink = "/var/www/html/tar.$test.bz2" if -d "/var/www/html";
unless( -e "$tarball") {
if(system("tar cjvf $tarball ./*")) {
croak("cannot create tarball tar.$image.bz2");
}
}
unless( -e "$symlink") {
system("ln -s $tarball $symlink");
}
print "ip: $test\n image: $image\n";
chdir("$pwd");
}
}
###########################################################
# create_initrd
#
# this creates the initrd for the diskless boot. Only
# creates an initrd with cramfs.
#
# Usage:
# create_initrd();
###########################################################
sub create_initrd {
my @ramfs_dirs = qw/bin dev lib new_root proc sbin usr var/;
my @bin = qw/bash cat grep gzip mkdir mount sed tar umount/;
my @sbin = qw/dhclient ifconfig dhclient-script/;
my @usr_bin = qw/bzip2 rsync wget/;
my $ramdir = "/tmp/initrd";
my @required;
print "creating initrd for diskless boot.\n";
#create a new initrd
if(-d "$ramdir") {
system("rm -rf $ramdir");
}
#make template
if(system("mkdir -p $ramdir")) {
croak("could not create $ramdir");
}
for my $dir (@ramfs_dirs) {
if(system("mkdir -p $ramdir/$dir")) {
croak("could not create $ramdir/$dir");
}
}
#find path of each file
for my $stuff (@bin,@sbin,@usr_bin) {
#just covering all the bases
my $cmd = "/usr/bin/whereis" if -x "/usr/bin/whereis";
$cmd = "/bin/whereis" if -x "/bin/whereis";
$cmd = "/sbin/whereis" if -x "/sbin/whereis";
open(IN,"$cmd $stuff |") or croak("could not open whereis");
while(<IN>) {
if(/$stuff: (.*?) .*/) {
push(@required,$1);
$paths{$stuff} = $1;
}
}
close(IN);
}
for my $stuff (@required) {
my $tempdir;
if($stuff =~ m/(.*\/).*/) {
$tempdir = $1;
if(system("mkdir -p $ramdir/$tempdir")) {
croak("could not create $ramdir/$tempdir");
}
}
if(system("cp $stuff $ramdir/$tempdir")) {
croak("could not copy $stuff to $ramdir/$tempdir");
}
}
#just do ldd part first
for my $stuff (@required) {
if(-B $stuff) {
open(IN,"/usr/bin/ldd $stuff |") or croak("could not find /usr/bin/ldd");
while(<IN>) {
if(/(.*) => (.*\/lib\/.*) (.*)$/) {
my $file = $2;
my $cmd = "cp ";
my $libdir;
if($file =~ /(.*\/).*$/) {
$libdir = $1;
}
if(system("mkdir -p $ramdir$libdir")) {
croak("something messed up");
}
$cmd .= " $file $ramdir$libdir"; #need to change
if(system($cmd)) {
croak("couldn't copy");
}
}
}
}
}
close(IN);
#the above method of collecting libraries misses ld-linux.so.2 (add it in)
if(system("cp /lib/ld-linux.so.2 $ramdir/lib")) {
croak("could not copy /lib/ld-linux.so.2");
}
#copy dev over from systemimager's initrd_template
my $initrd_template = "/usr/share/systemimager/boot/i386/standard/initrd_template/dev";
system("cp -r $initrd_template $ramdir"); #IT WILL NOT FAIL!
if(@ramfs_http_ips) {
&generate_linuxrc(1);
if(system("mkcramfs $ramdir $ramdir/ramfs-http-initrd")) {
croak("cannot run mkcramfs");
}
if(system("gzip -9 -f -S .img $ramdir/ramfs-http-initrd")) {
croak("cannot run gzip");
}
if(system("mv $ramdir/ramfs-http-initrd.img /tftpboot/")) {
croak("cannot move $ramdir/ramfs-http-initrd.img to /tftpboot");
}
}
if(@ramfs_rsync_ips) {
&generate_linuxrc(0);
if(system("mkcramfs $ramdir $ramdir/ramfs-rsync-initrd")) {
croak("cannot run mkcramfs");
}
if(system("gzip -9 -f -S .img $ramdir/ramfs-rsync-initrd")) {
croak("cannot run gzip");
}
if(system("mv $ramdir/ramfs-rsync-initrd.img /tftpboot/")) {
croak("cannot move $ramdir/ramfs-rsync-initrd.img to /tftpboot");
}
}
}
###########################################################
# generate_linuxrc
#
# this generates the linuxrc file used by the initrd. the
# type of linuxrc file generated is based on a passed in
# value. 0 is for one using rsync and 1 is for one using
# http.
#
# Usage:
# generate_linuxrc(0);
###########################################################
sub generate_linuxrc {
my ($ip,$netmask) = &find_ip(); #kind of pointless?
my $choice = shift;
my $ramdir = "/tmp/initrd";
########################
#HTTP VERSION
########################
my $linuxrc_http =<<EOF;
#!/bin/bash
$paths{"mount"} -n -t proc none /proc
echo "Maximum amount of memory is being determined..."
#find memory
MAXMEM=\`$paths{"cat"} /proc/meminfo | $paths{"grep"} MemTotal | $paths{"sed"} -e "s/[a-z,A-Z]*:[[:space:]]*\\([0-9]*\\).*/\\1/"\`
echo "\${MAXMEM} KB detected..."
$paths{"ifconfig"} eth0 up 2>&1>/dev/null
echo "Mounting tmpfs partition to /new_root/"
$paths{"mount"} -n -t tmpfs -o size=\${meminfo}k tmpfs new_root/
$paths{"dhclient"} 2>&1>/dev/null
echo "determining ip address..."
IP=\`$paths{"ifconfig"} eth0 | $paths{"grep"} "inet addr" | $paths{"sed"} -e "s/.*inet addr:\\([0-9]*.[0-9]*.[0-9]*.[0-9]*\\) .*/\\1/"\`
echo "ip is \${IP}"
KERNEL=\`$paths{"cat"} /proc/sys/kernel/osrelease | $paths{"grep"} boel\`
#rsync
if [ ! -z \${KERNEL} ]
then
$paths{"rsync"} -aHS $ip\::boot/i386/standard/boel_binaries.tar.gz new_root/
$paths{"tar"} xzvf new_root/boel_binaries.tar.gz -C /new_root
fi
echo "retrieving tarball..."
$paths{"wget"} -O new_root/tar.test.bz2 http://$ip/tar.\${IP}.bz2 ./
echo "done...untarring."
$paths{"tar"} xjvf new_root/tar.test.bz2 -C /new_root
echo "changing root mount point..."
$paths{"umount"} /proc
cd /new_root/
$paths{"mkdir"} old_root/
./sbin/pivot_root . old_root/
cd /
/bin/mount -t proc none /proc
/bin/mount -t sysfs none /sys
if [ ! -z \${KERNEL} ]
then
/bin/rm -f /boel_binaries.tar.gz
fi
/bin/rm -f /tar.test.bz2
cat<<EOL > /etc/fstab
devpts /dev/pts devpts defaults 0 0
$ip:/home /home nfs defaults 1 1
EOL
HOSTNAME=\`cat /etc/hosts | grep "\${IP} " | sed -e "s/.*[[:space:]]//"\`
#for fedora
if [ -f /etc/fedora-release ]
then
cat<<EOL > /etc/sysconfig/network
NETWORKING=yes
HOSTNAME=\${HOSTNAME}
EOL
fi
/sbin/route add default gw $ip eth0
cd /
exec /usr/sbin/chroot . /bin/bash -c "/sbin/init -i"
EOF
##################
#NOW RSYNC's VERSION
##################
my $linuxrc_rsync =<<EOF;
#!/bin/bash
$paths{"mount"} -n -t proc none /proc
#find memory
echo "Maximum amount of memory is being determined..."
MAXMEM=\`$paths{"cat"} /proc/meminfo | $paths{"grep"} MemTotal | $paths{"sed"} -e "s/[a-z,A-Z]*:[[:space:]]*\\([0-9]*\\).*/\\1/"\`
echo "\${MAXMEM} KB detected..."
$paths{"ifconfig"} eth0 up 2>&1>/dev/null
echo "Mounting tmpfs partition to /new_root/"
$paths{"mount"} -n -t tmpfs -o size=\${meminfo}k tmpfs new_root/
$paths{"dhclient"} 2>&1>/dev/null
echo "determining ip address..."
IP=\`$paths{"ifconfig"} eth0 | $paths{"grep"} "inet addr" | $paths{"sed"} -e "s/.*inet addr:\\([0-9]*.[0-9]*.[0-9]*.[0-9]*\\) .*/\\1/"\`
echo "ip is \${IP}"
KERNEL=\`cat /proc/sys/kernel/osrelease | grep boel\`
#rsync
if [ ! -z \${KERNEL} ]
then
$paths{"rsync"} -aHS $ip\::boot/i386/standard/boel_binaries.tar.gz new_root/
$paths{"tar"} xzvf new_root/boel_binaries.tar.gz -C /new_root
fi
echo "beginning rsync of image..."
$paths{"rsync"} -aHS $ip\::\${IP} new_root/
echo "...done!"
echo "changing mount point of root"
$paths{"umount"} /proc
cd /new_root/
$paths{"mkdir"} old_root/
./sbin/pivot_root . old_root/
cd /
/bin/mount -t proc none /proc
/bin/mount -t sysfs none /sys
if [ ! -z \${KERNEL} ]
then
/bin/rm -f /boel_binaries.tar.gz
fi
cat<<EOL > /etc/fstab
proc /proc proc defaults 0 0
sysfs /sys sysfs defaults 0 0
devpts /dev/pts devpts defaults 0 0
$ip:/home /home nfs defaults 1 1
EOL
HOSTNAME=\`cat /etc/hosts | grep "\${IP} " | sed -e "s/.*[[:space:]]//"\`
#for fedora
if [ -f /etc/fedora-release ]
then
cat<<EOL > /etc/sysconfig/network
NETWORKING=yes
HOSTNAME=\${HOSTNAME}
EOL
fi
echo "transferring modules dir to computenode."
rsync -aHS $ip\::modules /lib/modules
/sbin/route add default gw $ip eth0
cd /
exec /usr/sbin/chroot . /bin/bash -c "/sbin/init -i"
EOF
if($choice) {
open(OUT,">$ramdir/linuxrc");
print OUT $linuxrc_http;
close(OUT);
} else {
open(OUT,">$ramdir/linuxrc");
print OUT $linuxrc_rsync;
close(OUT);
}
if(system("chmod 777 $ramdir/linuxrc")) {
croak("cannot run: chmod 755 $ramdir/linuxrc");
}
#need to create symlink in /sbin to make this work
unless(-e "$ramdir/sbin/init") {
if(system("ln -s ../linuxrc $ramdir/sbin/init")) {
croak("could not run \"ln -s ../linuxrc $ramdir/sbin/init\"");
}
}
}
###########################################################
# modify_rsyncd
#
# modifies the rsyncd.conf file to allow for the client
# to use rsync to retreive the image upon boot.
#
# Usage:
# modify_rsyncd();
###########################################################
sub modify_rsyncd {
my $file = "/etc/systemimager/rsyncd.conf";
if(@ramfs_rsync_ips) {
copy("$file","$file.bak"); #make backup
open(OUT,">>$file");
foreach my $ips (@ramfs_rsync_ips) {
#if it's not there, add it
if(system("grep $ips] $file 2>&1>/dev/null")) { #not a typo with $ips]
print OUT "[$ips]\n\tpath=/var/lib/systemimager/images/$ip_to_image{$ips}\n\n";
}
}
close(OUT);
}
}
1;
-------------------------------------------------------------------------
This SF.Net email is sponsored by the Moblin Your Move Developer's challenge
Build the coolest Linux based applications with Moblin SDK & win great prizes
Grand prize is a trip for two to an Open Source event anywhere in the world
http://moblin-contest.org/redirect.php?banner_id=100&url=/
_______________________________________________
sisuite-devel mailing list
sisuite-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/sisuite-devel