#! /usr/bin/perl -I. # # @(#)poc-installer.pl # (C) The Virtual Laboratory for e-Science Project 2004 # disclaimers apply, refer to documentation before using this product # use Getopt::Long; $prodname="VL-E PoC release 0.1"; $0 =~ s/.*\///; $Getopt::Long::ignorecase=0; $opt_v=0; @optdef=qw ( noint|q|quiet nc|noconfigure|installonly ni|noinstall|configureonly baseurl=s conf|c=s dialog=s wget=s tmp|tmpdir=s v:i filelist=s ); &GetOptions(@optdef); # setting default values if needed $opt_dialog="dialog" unless $opt_dialog; $opt_wget="wget" unless $opt_wget; $opt_conf="poc.conf" unless $opt_conf; $opt_tmp=$ENV{"TMPDIR"} unless $opt_tmp; $opt_tmp=$ENV{"TEMP"} unless $opt_tmp; $opt_tmp="/tmp" unless $opt_tmp; $opt_baseurl="http://www.dutchgrid.nl/install/poc" unless $opt_baseurl; $opt_filelist="filelist" unless $opt_filelist; &init_get_wget; # we MUST have wget # create our cache directory for downloads and temporary files $wd="$opt_tmp/poc-installer-$$"; &Debug(3,"Creating working directory $wd"); mkdir $wd,0700 or die "Cannot create temporary install directory $wd: $!\n"; chdir $wd; eval "use Config::IniFiles"; "$@" and do { &Debug(2,"Obtaining IniFiles.pm from the web in $wd"); &wget(1,"-N","IniFiles.pm"); push @INC,"$wd"; eval "use IniFiles"; }; -f "$opt_conf" or &wget(1,"-N","$opt_conf"); my $cfg = new Config::IniFiles( -file => $opt_conf , -allowcontinue => 1 ); print "The value is " . $cfg->val( 'download', 'file' ) . "." if $cfg->val( 'download', 'file' ); # use defaults from config file unless set via arguments $opt_noint=$cfg->val('init','nointeractive') unless $opt_noint!=undef; $opt_nc=$cfg->val('init','noconfigure') unless $opt_nc!=undef; $opt_ni=$cfg->val('init','noinstall') unless $opt_ni!=undef; &Debug(1,"Interaction settings: noint=$opt_noint, nc=$opt_nc, ni=$opt_ni"); # get dialog programme if needed $opt_dialog=&init_get_dialog($opt_dialog) unless $opt_noint; &installer() unless $opt_ni; &doconfigure() unless $opt_nc; &msgText(1, "The $prodname installation has finished and this installer will now exit." ); 1; ############################################################################## # INSTALLER section subroutines ############################################################################## # sub installer() { my @packagelist; # ordered list of packagegroup names # find or obtain the yum program. User interaction is needed in # case the baseOS does not support yum by default # yum RPMs are available from the web, via "yumlist" system("which --skip-alias yum > /dev/null 2>&1") and do { my %yumrpmsel,%yumrpm,$rpmname; &msgText(1, "Cannot find \"yum\" in your path. Maybe you are running a ". "version of RedHat Linux 9 or below, or native RHEL? ". "Please download the appropriate version of yum and install ". "that RPM as root on this system"); # assemble the yum options per operating system my @oslist=$cfg->Parameters('yumlist'); foreach (@oslist) { my($url,$state)=split(/\s+/,$cfg->val('yumlist',$_),2); &Debug(4,"Added yum $url in $state as $_"); $state="off" unless $state; $yumrpmsel{"$_"}=$state; $yumrpm{"$_"}=$url; } %yumrpmsel=&menuList("Package sets",%yumrpmsel); foreach $k ( keys %yumrpmsel ) { if ($yumrpmsel{$k} eq "on") { &Debug(3,"Added RPM for yum OS version $k"); $rpmname=$yumrpm{$k}; } } $rpmname or &Fatal("No RPM selected and yum not found. Cannot continue"); &Debug(3,"Downloading yum rpm from $rpmname"); &wget(1,"-N","$rpmname"); $rpmname=~s/.*\///; # download will truncare URL dirname if ( -f "$rpmname" ) { &msgText(0,"About to verify $rpmname from $wd."); print "Running RPM test transaction for $rpmname\n"; system("rpm --test -i $rpmname"); &msgText(1,"About to install $rpmname from $wd. Press to cancel"); print "RPM install of $rpmname\n"; system("rpm -ivh $rpmname"); } else { &Fatal("Could not download RPM $rpmname correctly, aborted."); } }; # but did installing the RPM help? system("which --skip-alias yum > /dev/null 2>&1") and &Fatal("Cannot find yum command, and could not install it from RPM. ". "Please download and install yourself."); # assemble the package groups and heads my @packagelist=$cfg->Parameters('packages'); my $i=0; foreach (@packagelist) { my $c=$cfg->val('packages',$_); $c=~s/^\s*//; my ($state,$rpmname)=split(/\s+/,$c,2); my $itemname=sprintf("%03d:%s",$i++,$_); #maintain sort order $choices{$itemname}=$state; $package{$itemname}=$rpmname; &Debug(5,"Added package $rpmname ($state) to menu as $itemname"); } %choices=&checkList("Package sets",%choices); foreach $k ( keys %choices ) { if ($choices{$k} eq "on") { &Debug(3,"Added package group $k"); push @packages,$package{$k}; } else { &Debug(3,"No package group $k"); } } # # create a yum.conf file for installing the VL-E POC RPMs # $yumconf=($cfg->val('init','yumconf')? $cfg->val('init','yumconf'):"/etc/yum.conf"); $yumconf=&getText("Your yum configuration file",$yumconf); &Debug(3,"Yum configuration is $yumconf"); open YUMCONF,"$yumconf" or die "Cannot open yum configuration $yumconf: $!\n"; open VLEYUMCONF,">yum.conf" or die "Cannot open new yum config in $wd: $!\n"; while () { /VLEPOC/ and $haspoc++; /pkgpolicy.*=.*newest/ and $_="pkgpolicy=last\n"; print VLEYUMCONF $_; }; close YUMCONF; if ( $haspoc ) { &msgText(1,"Your $yumconf file seems to have the $prodname repository ". "as a standard component. If your yum configuration is outdated, ". "strange results may ensue.\nPress escape to cancel this install."); } else { # add stuff only if we have some basic knowledge of the conf print VLEYUMCONF "# VLEPOC sections follow\n"; my @sections=split(/\s+/,$cfg->val('init','yumsections')); open F,$cfg->val('init','yumosnamefile') or &Fatal("Cannot open OS release file ". $cfg->val('init','yumosnamefile').": $!"); chomp(my $osname=); close F; foreach $sec ( @sections ) { my $oscond=$cfg->val("yum:$sec",'oscondition'); &Debug(4,"For yum section $sec: condition is $oscond to match $osname"); $oscond and ( $osname=~/$oscond/ or next ); if ($cfg->val("yum:$sec",'yumurl') and $cfg->val("yum:$sec",'yumname')) { my $gpgcheck=($cfg->val("yum:$sec",'yumgpgcheck')? $cfg->val("yum:$sec",'yumgpgcheck'):0); print VLEYUMCONF "[$sec]\n"; print VLEYUMCONF "name=".$cfg->val("yum:$sec",'yumname')."\n"; print VLEYUMCONF "baseurl=".$cfg->val("yum:$sec",'yumurl')."\n"; print VLEYUMCONF "gpgcheck=".$gpgcheck."\n"; } else { &Fatal("attributes yumurl or yumname in yum:$sec missing in $opt_conf"); } } } close VLEYUMCONF; &Debug(3,"Written new yum.conf file"); # known bugs and work-arounds -f "/etc/java/java.conf" and do { chomp(my $jrpm=`rpm -qf /etc/java/java.conf`); if ( $jrpm !~ /j2sdk_profile-/ ) { print "Found /etc/java/java.conf, which may conflict with a package\n"; print "to be installed. This file is owned by $jrpm.\n"; print "It may be that a packaged java installation already exists on\n"; print "this host, so be prepared to repair /etc/java/java.conf\n"; print "after this installation is complete (test using edg-rm)\n"; print "Consider removing this RPM and RPMs depending on it, and\n"; print "exit this shell when done (by typing \"exit\" on the prompt)\n\n"; system("/bin/sh"); } }; print "\nStart installing RPM packages using yum, please wait\n"; foreach $p ( @packages ) { $packages.=" $p"; } &Debug(2,"Calling yum with packages $packages"); system("yum -c ./yum.conf install $packages") and &Warn("Installation of packages may have failed: $!"); } ############################################################################## # CONFIGURATOR section subroutines ############################################################################## # sub doconfigure() { my $prefix=$cfg->val('common','installprefix'); $prefix="/opt/edg" unless $prefix; # first create the relevant directories, if specified in the # configuration file foreach ( $cfg->val('conf','mkdir') ) { my ($dirname,$mode)=split(/\s+/,$_); -d $dirname or (mkdir $dirname,$mode or &Fatal("Cannot create $dirname")); } # do we need to preserve some files my $pdir=$cfg->val('conf','preservedir'); $pdir and do { foreach ( $cfg->val('conf','preserve') ) { s/\s+.*$//; s/\$CONF/$opt_conf/; system("cp -p $_ $preservedir/"); } }; # is there a reason to add stuff to ld.so.conf? open LD,"/etc/ld.so.conf" and do { my %ldlines, $newlines; while() { chomp($_); $ldlines{$_}++; } close LD; foreach ( $cfg->val('conf','ldconfig') ) { $ldlines{$_} and next; ! -d $_ and next; #makes no sense if not there $newlines.="$_\n"; } $newlines ne "" and do { &msgText(0, "Adding library paths to /etc/ld.so.conf"); open LD,">>/etc/ld.so.conf" and print LD $newlines and close LD; system("ldconfig"); }; }; # get list of VOs and infrastructures to join my $i; my @vos=split(/\s+/,$cfg->val('common','vo')); foreach $vo ( @vos ) { my $vosec="vo:".$vo; &Debug(4,"Adding VO $vo to list of potential VOs"); my @items=$cfg->Parameters($vosec); my %cfg=(); foreach $attr ( @items ) { $cfg{$attr}=$cfg->val($vosec,$attr); &Debug(6,"For VO $vo added $attr=".$cfg{$attr}); } $cfg{"serial"}=$i++; $items{$vo}=\%cfg; } %choices=(); foreach $vo ( keys %items ) { my ($menutext)=sprintf("%03d:%s", ($items{$vo})->{"serial"},($items{$vo})->{"name"} ); $choices{$menutext}=($items{$vo})->{"status"}; $indices{$menutext}=$vo; } %choices=&checkList("Supported VOs and Infrastructures",%choices); foreach $vo ( keys %choices ) { ($choices{$vo} eq "on") and do { &Debug(3,"Adding VO $vo at index ".$indices{$vo}); $supported{$indices{$vo}}=$items{$indices{$vo}}; }; } # do we want this host to act as a server as well? my $isservicehost=$cfg->val('common','servicehost'); $isservicehost=0 if $isservicehost==undef; $isservicehost=&getYesNo("Do you want permit VO access to this host?", $isservicehost); $isservicehost eq "yes" and do { -f $cfg->val('common','gridmapfile') and do { my $presrv=&getYesNo("Do you want to re-use your existing gridmapfile?", $cfg->val('conf','preservemapfile') ); $presrv eq "yes" and do { system("cat ".$cfg->val('common','gridmapfile')." >> ". $cfg->val('common','gmflocal') ) and &Fatal("Cannot append grid-mapfile to ".$cfg->val('common','gmflocal'). ": error: ".$!); }; }; &write_mkgridmap_conf($cfg->val('common','mkgridmapconf'),%supported); &write_globuscfg($cfg->val('conf','preservedir')."/globus.cfg"); &msgText(0, "Adding grid-mapfile retrieval to the cron.d directory"); open CRONTAB,">/etc/cron.d/mkgridmap" or &Fatal("Cannot open>mkgridmap cron"); print CRONTAB "# edg-mkgridmap cron.d\n"; print CRONTAB int(rand(59))." 1,7,13,19 * * * ". "$prefix/sbin/edg-mkgridmap --safe ". " --output=".$cfg->val('common','gridmapfile'). " --conf=".$cfg->val('common','mkgridmapconf')."\n"; close CRONTAB; }; &write_voconfig("$prefix/etc",%supported); -f "$prefix/etc/cron/edg-fetch-crl-cron" and do { &msgText(0, "Adding periodic CRL retrieval to the cron.d directory"); open CRONTAB,">/etc/cron.d/fetch-crl-cron" or &Fatal("Cannot open > fetch-crl cron"); print CRONTAB "# fetch-crl cron.d\n"; print CRONTAB int(rand(59))." 1,7,13,19 * * * ". "$prefix/etc/cron/edg-fetch-crl-cron\n"; close CRONTAB; }; &msgText(0, "Adding basic system configuration in /etc/sysconfig"); &write_globus_config("/etc/sysconfig/globus"); &write_edg_config($prefix,"/etc/sysconfig/edg"); &write_edg_wl_var_config("$prefix/etc/edg_wl_ui_cmd_var.conf"); -f "$prefix/sbin/edg-replica-manager-configure" and &write_edgrm_config("$prefix/etc/edg-replica-manager/edg-replica-manager.conf.poc"); } # --------------------------------------------------------------------------- # support routines for the configurator # sub write_voconfig() { my ($path,%volist) = (@_); foreach $vo ( keys %volist ) { %vinfo=%{$volist{$vo}}; &Debug(2,"Adding $vo (".$vinfo{"name"}.")"); ( $vinfo{"ns"} and $vinfo{"lb"} ) or next; mkdir "$path/".$vinfo{"alias"},0755 or next; open WL,">$path/".$vinfo{"alias"}."/edg_wl_ui.conf" or do { &Warn("Cannot write to file for $alias"); next; }; print WL "# This file is $path/".$vinfo{"alias"}."/edg_wl_ui.conf\n"; print WL "# For the VO ".$vinfo{"name"}."\n"; print WL "[\nVirtualOrganisation = \"".$vinfo{"alias"}."\";\n"; print WL "NSAddresses = \"".$vinfo{"ns"}."\";\n"; print WL "LBAddresses = \"".$vinfo{"lb"}."\";\n"; print WL "#HLRLocation is for accounting purposes\n"; print WL "#HLRLocation = \"unknown\"\n"; print WL "MyProxyServer = \"".$vinfo{"myproxy"}."\";\n"; print WL "]\n"; close WL; } } sub write_mkgridmap_conf() { my ($name,%volist) = (@_); open CONF,">$name" or &Fatal("Cannot open $name for writing mkgridmap.conf"); print CONF "# edg-mkgridmap.conf generated from poc-configurator.pl\n"; foreach $vo ( keys %volist ) { %vinfo=%{$volist{$vo}}; &Debug(2,"Adding $vo (".$vinfo{"name"}.")"); print CONF "# VO $vo (".$vinfo{"name"}.")\n"; $vinfo{"group"} and print CONF "group ".$vinfo{"group"}." ". $vinfo{"account"}."\n"; $vinfo{"auth"} and print CONF "auth ".$vinfo{"auth"}."\n"; } print CONF "# Local grid-mapfile to import and overide all the above.\n"; print CONF "# eg, gmf_local /opt/edg/etc/grid-mapfile-local\n"; $cfg->val('common','gmflocal') and print CONF "gmf_local ". $cfg->val('common','gmflocal') ."\n"; print CONF "\n\n"; } sub write_globus_config() { my ($f) = (@_); open CFG,">$f" or &Fatal("globus_config: Cannot open > $f"); print CFG "# Written by poc-configurator.pl to $f\n"; print CFG "GLOBUS_LOCATION=".$cfg->val('common','globuslocation')."\n"; print CFG "GLOBUS_CONFIG=/etc/globus.conf\n"; print CFG "# make sure to reconfigure this by hand as needed!\n"; print CFG "GLOBUS_TCP_PORT_RANGE=\"".$cfg->val('common','tcprange')."\"\n"; close CFG; # need to run /opt/globus/sbin/globus-initialization.sh afterwards my $globuslocation=$cfg->val('common','globuslocation'); $globuslocation="/opt/globus" unless $globuslocation; [ -f "$globuslocation/sbin/globus-initialization.sh" ] and do { system("$globuslocation/sbin/globus-initialization.sh") and &Warn("Cannot run globus initialisation script: $!\n". " Name: $globuslocation/sbin/globus-initialization.sh\n"); }; } sub write_globuscfg() { my ($f) = (@_); my $globusloc=$cfg->val('common','globuslocation'); my $certdir=$cfg->val('common','hostcertdir'); my $certname=$cfg->val('common','hostcertname'); my $keyname=$cfg->val('common','hostkeyname'); my $gridmap=$cfg->val('common','gridmapfile'); my $mapdir=$cfg->val('common','gridmapdir'); open CFG,">$f" or &Fatal("globuscfg: Cannot open > $f"); print CFG <$f" or &Fatal("edg_config: Cannot open > $f"); print CFG "# Written by poc-configurator.pl to $f\n"; print CFG "EDG_LOCATION=$prefix\n"; print CFG "EDG_WL_LOCATION=$prefix\n"; print CFG "EDG_LOCATION_VAR=$prefix/var\n"; print CFG "EDG_TMP=/tmp\n"; close CFG; } sub write_edg_wl_var_config() { my ($f) = (@_); open CFG,">$f" or &Fatal("edg_wl_var_config: Cannot open > $f"); print CFG "# Written by poc-configurator.pl to $f\n"; my $logdest=&getText("Brokered job service logging destination", $cfg->val('common','logdest')); my $vo=&getText("Default VO to submit for (for clarity use \"unspecified\")", $cfg->val('common','defaultvo')); print CFG <$f" or &Fatal("Cannot open > $f"); my $iiinfo=&getText("Location of the top-level information service", $cfg->val('common','iilocation')); my ($iihost,$iiport)=split(/:/,$iiinfo,2); $iiport=2170 unless $iiport; $iihost="boswachter.nikhef.nl" unless $iihost; my $closese=&getText("Hostname of your closest Storage Element", $cfg->val('common','closese')); my $closece=&getText("Hostname of your closest Computing Element", $cfg->val('common','closece')); my $localdomain=$cfg->val('common','localdomain'); chomp($localdomain=`dnsdomainname`) unless $localdomain; $localdomain="localdomain" unless $localdomain; my $prefix=$cfg->val('common','installprefix'); print CFG <&1 1>/dev/null |"; $msg=; close DLG; $? and &Fatal("User terminated input dialog box, exiting."); chomp($msg); return $msg; } sub getYesNo() { my ($title,$default) = (@_); my ($msg); $opt_noint and do { $default="yes" unless $default; return $default }; $default="yes" if ($default eq "true" or $default == 1); open DLG,"$opt_dialog --cr-wrap --yesno \'$title\' 12 70 2>&1 1>/dev/null |"; $msg=; close DLG; &Debug(4,"YesNo resulted in $?"); if ($? == 0) { return "yes"; } elsif ($? <= 256 ) { return "no"; } else { &Fatal("User terminated input dialog box, exiting.");} } sub msgText() { my ($persist,$title,$default) = (@_); my ($boxtype); $opt_noint and return 0; if ( $persist ) { $boxtype="msgbox"; } else { $boxtype="infobox"; } open DLG,"$opt_dialog --cr-wrap --$boxtype \'$title\' 12 70 2>&1 1>/dev/null|"; $msg=; close DLG; $? and &Fatal("User terminated input dialog box, exiting."); return 0; } sub checkList() { my ($title,%choices) = (@_); my ($cmd,$i,$k); my ($msg,$text,@list); $opt_noint and return %choices; $cmd="$opt_dialog --checklist \"$title\" 16 70 11 "; $i=1; foreach $k ( sort keys %choices ) { $list[$i]=$k; ($text=$k)=~s/.*://; $cmd.=$i++." \"".$text."\" ".$choices{$k}." "; } &Debug(4,$cmd); open DLG,"$cmd 2>&1 1>/dev/null |"; $msg=; close DLG; $? and &Fatal("User terminated input dialog box ($?), exiting."); chomp($msg); &Debug(4,"checkList result: <$msg>"); $msg=~s/"//g; split(/\s+/,$msg); foreach $k ( keys %choices ) { $choices{$k}="off"; } foreach $cmd ( @_ ) { $choices{$list[$cmd]}="on"; } return %choices; } sub menuList() { my ($title,%choices) = (@_); my ($cmd,$i,$k); my ($msg,$text,@list); $opt_noint and return %choices; $cmd="$opt_dialog --menu \"$title\" 16 70 11 "; $i=1; foreach $k ( sort keys %choices ) { $list[$i]=$k; ($text=$k)=~s/.*://; $cmd.=$i++." \"".$text."\" "; } &Debug(4,$cmd); open DLG,"$cmd 2>&1 1>/dev/null |"; $msg=; close DLG; $? and &Fatal("User terminated input dialog box, exiting."); chomp($msg); &Debug(4,"menuList result: <$msg>"); $msg=~s/"//g; split(/\s+/,$msg); foreach $k ( keys %choices ) { $choices{$k}="off"; } foreach $cmd ( @_ ) { $choices{$list[$cmd]}="on"; } return %choices; } sub radioList() { my ($title,%choices) = (@_); my ($cmd,$i,$k); my ($msg,$text,@list); $opt_noint and return %choices; $cmd="$opt_dialog --menu \"$title\" 16 70 11 "; $i=1; foreach $k ( sort keys %choices ) { $list[$i]=$k; ($text=$k)=~s/.*://; $cmd.=$i++." \"".$text."\" "; } &Debug(4,$cmd); open DLG,"$cmd 2>&1 1>/dev/null |"; $msg=; close DLG; $? and &Fatal("User terminated input dialog box, exiting."); chomp($msg); &Debug(4,"radioList result: <$msg>"); $msg=~s/"//g; split(/\s+/,$msg); foreach $k ( keys %choices ) { $choices{$k}="off"; } foreach $cmd ( @_ ) { $choices{$list[$cmd]}="on"; } return %choices; } sub wget { my($ftl,$opt,$url) = @_; $url=~/^http:/ or $url=$opt_baseurl."/".$url; system("$opt_wget -q $opt $url") and do { if ($ftl) { &Fatal("Cannot download $url using $opt_wget"); } else { &Warn("Cannot download $url using $opt_wget"); } }; } ############################################################################## # # Initialisation and web routines # # ---------------------------------------------------------------------------- # void init_get_wget() # Function: # test whether the "wget" program is there, or abort # Dependencies: # "which" must exist in current $PATH # sub init_get_wget() { system("which --skip-alias $opt_wget > /dev/null 2>&1") and do { print STDERR "Cannot locate the wget programme as $opt_wget\n"; print STDERR "You MUST install wget before installing the $prodname\n"; print STDERR "\n"; print STDERR "If you have wget installed but invoked with a different\n"; print STDERR "name use the \"--wget=\" argument to the installer\n"; exit(1); }; } # ---------------------------------------------------------------------------- # string init_get_dialog(dialog_guess) # Function: # test whether the guessed dialog location exists or, if not, # obtain a copy of the dialog programme from the web # Dependencies: # &wget() subroutine # $wd - temporary installer cache area # &Debug() subroutine # sub init_get_dialog() { my ($dialog) = (@_); # verify we have the dialog or Xdialog programme, or obtain from web system("which --skip-alias $dialog > /dev/null 2>&1") and do { $dialog="Xdialog"; system("which --skip-alias $dialog > /dev/null 2>&1") and do { &wget(1,"-O $wd/dialog","dialog"); $dialog="$wd/dialog"; chmod 0755,$dialog; &Debug(4,"Dialog downloaded from $opt_baseurl/dialog"); }; }; &Debug(3,"Dialog programme path set to $dialog"); return $dialog; } ############################################################################## # # Debug and error functions # sub Fatal { my ($msg)=@_; print STDERR "Fatal error encountered.\n$msg\n"; exit(1); } sub Warn { my ($msg)=@_; print STDERR "Warning: $msg\n"; } sub Debug { my ($lvl,@msg)=@_; $lvl<=$opt_v and do { foreach $l ( @msg ) { print "DBG[$lvl]: $l\n"; } }; }