#!/usr/bin/perl -w # # EMULAB-COPYRIGHT # Copyright (c) 2003, 2004, 2005, 2006 University of Utah and the Flux Group. # All rights reserved. # # # install-boss.sh - Script to do the initial install of a boss node # # The main things it does not do yet: # * Figure out where to put directories such as /usr/testbed - they must # already exist # * Set up named - we probably have to do that later, when the interfaces table # is filled in # * Set up a sup tree. Not sure what the right thing to do here is! # * Doesn't do anything about SSL certificates for the web # # Configure variables # my $PREFIX = '@prefix@'; my $SRCDIR = '@srcdir@'; my $TOP_SRCDIR = '@top_srcdir@'; my $DBNAME = "@TBDBNAME@"; my $ELABINELAB = @ELABINELAB@; my $WINSUPPORT = @WINSUPPORT@; my $OURDOMAIN = '@OURDOMAIN@'; my $LOGFACIL = '@TBLOGFACIL@'; my $USERNODE = '@USERNODE@'; my $FSNODE = '@FSNODE@'; my $BOSSNODE = '@BOSSNODE@'; my $BOSSNODE_IP= '@BOSSNODE_IP@'; my $USERNODE_IP= '@USERNODE_IP@'; my $FSNODE_IP= '@FSNODE_IP@'; # # Some programs we use # my $SH = "/bin/sh"; my $CHMOD = "/bin/chmod"; my $CHGRP = "/usr/bin/chgrp"; my $CHOWN = "/usr/sbin/chown"; my $PWD = "/bin/pwd"; my $PW = "/usr/sbin/pw"; my $PATCH = "/usr/bin/patch"; my $SSH_KEYGEN = "/usr/bin/ssh-keygen"; my $PKG_INFO = "/usr/sbin/pkg_info"; my $PKG_ADD = "/usr/sbin/pkg_add"; my $TOUCH = "/usr/bin/touch"; my $SSH = "/usr/bin/ssh"; my $SCP = "/usr/bin/scp"; my $CP = "/bin/cp"; my $ENV = "/usr/bin/env"; my $MOUNT = "/sbin/mount"; my $SUDO = "/usr/local/bin/sudo"; my $SUIDPERL = "/usr/bin/suidperl"; my $MYSQL = "/usr/local/bin/mysql"; my $MYSQLADMIN = "/usr/local/bin/mysqladmin"; my $MYSQLSHOW = "/usr/local/bin/mysqlshow"; my $MYSQLDUMP = "/usr/local/bin/mysqldump"; my $MYSQLINSTALL = "/usr/local/bin/mysql_install_db"; my $MYSQLDBDIR = "/var/db/mysql"; my $GMAKE = "/usr/local/bin/gmake"; my $RNDC_CONFGEN = "/usr/local/sbin/rndc-confgen"; # # Some files we edit/create # my $CRONTAB = "/etc/crontab"; my $HOSTS = "/etc/hosts"; my $FSTAB = "/etc/fstab"; my $RCCONF = "/etc/rc.conf"; my $SYSLOG_CONF = "/etc/syslog.conf"; my $NEWSYSLOG_CONF = "/etc/newsyslog.conf"; my $INETD_CONF = "/etc/inetd.conf"; my $ROOT_PRIVKEY = "/root/.ssh/identity"; my $ROOT_PUBKEY = "$ROOT_PRIVKEY.pub"; my $ROOT_AUTHKEY = "/root/.ssh/authorized_keys"; my $APACHE_ETCDIR = "/usr/local/etc/apache"; my $SUDOERS = "/usr/local/etc/sudoers"; my $HTTPD_CONF = "$APACHE_ETCDIR/httpd.conf"; my $PHP_INI = "/usr/local/etc/php.ini"; my $DHCPD_CONF = "/usr/local/etc/dhcpd.conf"; my $DHCPD_TEMPLATE = "/usr/local/etc/dhcpd.conf.template"; my $DHCPD_LEASES = "/var/db/dhcpd.leases"; my $DHCPD_MAKECONF = "$PREFIX/sbin/dhcpd_makeconf"; my $BATCHEXP = "$PREFIX/bin/batchexp"; my $WAP = "$PREFIX/sbin/withadminprivs"; my $NAMED_SETUP = "$PREFIX/sbin/named_setup"; my $CRACKLIB_DICT = "/usr/local/lib/pw_dict.pwd"; my $STL_PATCH = "$TOP_SRCDIR/patches/g++.patch"; my $M2CRYPTO_PATCH = "$TOP_SRCDIR/patches/m2crypto.patch"; my $PHP4_PATCH = "$TOP_SRCDIR/patches/php4-Makefile.patch"; my $INIT_PRIVKEY = "$TOP_SRCDIR/install/identity"; my $SSH_CONFIG = "/etc/ssh/ssh_config"; my $RNDC_KEY = "/usr/local/etc/rndc.key"; my $LOADER_CONF = "/boot/loader.conf"; my $SYSCTL_CONF = "/etc/sysctl.conf"; my $EMULAB_PEM = "emulab.pem"; my $CLIENT_PEM = "client.pem"; my $CTRLNODE_PEM = "ctrlnode.pem"; my $ETC_EMULAB_DIR = "/etc/emulab"; # Apache certs on both boss and ops my $APACHE_CERTPEM = "apache_cert.pem"; my $APACHE_KEYPEM = "apache_key.pem"; my $APACHE_CERTFILE = "$APACHE_ETCDIR/ssl.crt/www.${OURDOMAIN}.crt"; my $APACHE_KEYFILE = "$APACHE_ETCDIR/ssl.key/www.${OURDOMAIN}.key"; my $APACHE_CERTPEM_OPS = "apache-ops_cert.pem"; my $APACHE_KEYPEM_OPS = "apache-ops_key.pem"; my $APACHE_CERTFILE_OPS = "$APACHE_ETCDIR/ssl.crt/${USERNODE}.crt"; my $APACHE_KEYFILE_OPS = "$APACHE_ETCDIR/ssl.key/${USERNODE}.key"; # # Some directories we care about # my $LOGDIR = "$PREFIX/log"; my $MYSQL_LOGDIR = "$LOGDIR/mysql"; my $RCDIR = "/usr/local/etc/rc.d"; my $USERSVAR_DIR = "$PREFIX/usersvar"; my $OPSDIR_DIR = "$PREFIX/opsdir"; my $PORTSDIR = "/usr/ports"; my $PORTSMISCDIR = "$PORTSDIR/misc"; my $MIBPATH = "/usr/local/share/snmp/mibs"; my $TFTP_DIR = "$PREFIX/tftpboot"; my $TFTP_PROJ_DIR = "$TFTP_DIR/proj"; my $VARRUN = "/var/run"; my $ETCSSH = "/etc/ssh"; # # URLs # my $CISCO_MIB_FTP = "ftp://ftp.cisco.com/pub/mibs/v2"; # # And some lists that we use # my @TESTBED_DIRS = ($PREFIX); my @MOUNTPOINTS = ("$PREFIX/usersvar", "$PREFIX/opsdir", "/users", "/proj", "/groups", "/share"); my @LOGFILES = ("$LOGDIR/bootinfo.log", "$LOGDIR/tmcd.log", "$LOGDIR/capture.log", "$LOGDIR/dhcpd.log", "$LOGDIR/capserver.log", "$LOGDIR/frisbeed.log", "$LOGDIR/proxydhcpd.log", "$LOGDIR/elvind.log", "$LOGDIR/stated.log", "$LOGDIR/osselect.log", "$LOGDIR/tftpd.log", "$LOGDIR/sdcollectd.log", "$LOGDIR/genlastlog.log", "$LOGDIR/sshxmlrpc.log", "$LOGDIR/plabgetfree.log", "$LOGDIR/xmlrpcbag.log", "$LOGDIR/plabrenew.log", "$LOGDIR/sslxmlrpc.log"); my @CISCO_MIBS = ("CISCO-SMI", "CISCO-TC", "CISCO-VTP-MIB", "CISCO-PAGP-MIB", "CISCO-PRIVATE-VLAN-MIB", "CISCO-STACK-MIB", "CISCO-VLAN-MEMBERSHIP-MIB", "CISCO-C2900-MIB"); # Initial experiments to create. Indexed by eid. my %EXPERIMENTS = ("hwdown" => {"pid" => "emulab-ops", "description" => "Node reported as down"}, "reloading" => {"pid" => "emulab-ops", "description" => "Nodes reloading images"}, "reloadpending" => {"pid" => "emulab-ops", "description" => "Nodes waiting for reload"}, "oldreserved" => {"pid" => "emulab-ops", "description" => "Nodes in limbo during swap modify"}, "nfree-leases" => {"pid" => "emulab-ops", "description" => "Nodes in limbo during nfree"}, "opsnodes" => {"pid" => "emulab-ops", "description" => "Nodes designated as Ops Nodes"}, ); # # List of names that goes into $HOSTS and which must resolve. # my @OPS_NAMES = ($USERNODE, "users", "ops"); if ($USERNODE eq $FSNODE) { push(@OPS_NAMES, "fs"); } # Version of FreeBSD. my $FBSD_VERSION = 4; if (`uname -r` =~ /^(\d)/) { $FBSD_VERSION = $1; } else { die("Could not determine what version of FreeBSD you are running!\n"); } # # The meta-ports (name and version) that drag in all the dependancies for # a boss node. These are OS dependent as we upgrade. # my $BOSS_PORT = (($FBSD_VERSION == 4) ? "emulab-boss-1.8" : "emulab-boss-2.0"); my $PHP4_PORT = "php4-extensions-1.0"; # # Figure out which directory we live in, so that some stages can do thing # relative to it. # my $OBJDIR = `/usr/bin/dirname $0`; chomp $OBJDIR; my $TOP_OBJDIR = "$OBJDIR/.."; my $ELVIND_CONF = "/usr/local/etc/elvind.conf"; my $BOSS_ELVIND_CONF = "$TOP_OBJDIR/event/etc/elvind-boss.conf"; # # Allow this to work if the library is left in the source directory # use lib '@srcdir@'; use English; use libinstall; use Getopt::Std; # # Handle command-line options # sub usage { print "Usage: boss-install [-p packagedir] [-P portname]\n"; exit(1); } my $packagedir = ""; my $batchmode = 0; my $password; my %opts; if (! getopts("P:p:bw:", \%opts)) { usage(); } if (defined($opts{p})) { $packagedir = $opts{p}; } if (defined($opts{b})) { $batchmode = 1; } if (defined($opts{w})) { $password = $opts{w}; } if (defined($opts{P})) { $BOSS_PORT = $opts{P}; } if (@ARGV) { usage(); } # # Make sure they know what they're getting into... # if (! $batchmode) { warn "***** Please run install-ops on ops, and reboot it, before running\n"; warn "this script!\n\n"; print STDERR "WARNING: This script is ONLY intended to be run on a machine\n"; print STDERR "that is being set up as a dedicated boss node. Continue? [y/N] "; my $response = <>; die "Installation aborted!\n" unless ($response =~ /^y/i); } if ($UID != 0) { die "This script must be run as root.\n"; } Phase "usersgroups", "Creating users and groups", sub { Phase "tbadmin", "Creating tbadmin group", sub { if (getgrnam("tbadmin")) { PhaseSkip("tbadmin group already exists"); } ExecQuietFatal("$PW groupadd tbadmin -g 101"); }; Phase "root", "Creating root group", sub { if (getgrnam("root")) { PhaseSkip("root group already exists"); } ExecQuietFatal("$PW groupadd root -g 103"); }; # Added next two cause the mysql package does not do this (port does). Phase "mysqlgroup", "Creating mysql group", sub { if (getgrnam("mysql")) { PhaseSkip("mysql group already exists"); } ExecQuietFatal("$PW groupadd mysql -g 88"); }; Phase "mysqluser", "Creating mysql user", sub { if (getpwnam("mysql")) { PhaseSkip("mysql user already exists"); } ExecQuietFatal("$PW useradd mysql -g 88 -g 88 -h - ". "-d $MYSQLDBDIR -s /sbin/nologin -c 'MySQL Daemon'"); }; ExecQuietFatal("$CHOWN mysql:mysql $MYSQLDBDIR") if (-e $MYSQLDBDIR); }; Phase "dirs", "Setting directory permissions", sub { foreach my $dir (@TESTBED_DIRS) { Phase $dir, $dir, sub { if (!-d $dir) { PhaseFail("Directory $dir does not exist"); } my ($mode,$group) = (stat($dir))[2,5]; # Fix up the mode (strip file type) $mode = $mode & 0777; if ($mode == 0775 && $group eq getgrnam("tbadmin")) { PhaseSkip("Already done"); } ExecQuietFatal("$CHGRP tbadmin $dir"); ExecQuietFatal("$CHMOD 0775 $dir"); }; } }; Phase "tftp", "Setting up directories for tftp", sub { Phase "tftpoff", "Disabling BSD tftpd", sub { PhaseSkip("no inetd.conf!?") unless (-e $INETD_CONF); PhaseSkip("already disabled") unless `grep '^tftp' $INETD_CONF`; ExecQuietFatal("sed -i .orig -e '/^tftp/s/^/#/' $INETD_CONF"); HUPDaemon("inetd"); }; Phase "tftpboot", "Creating $TFTP_DIR", sub { DoneIfExists($TFTP_DIR); mkdir $TFTP_DIR,0775 or PhaseFail("Unable to create $TFTP_DIR : $!"); ExecQuietFatal("$CHGRP tbadmin $TFTP_DIR"); }; Phase "tftpproj", "Creating $TFTP_PROJ_DIR", sub { DoneIfExists($TFTP_PROJ_DIR); mkdir $TFTP_PROJ_DIR,0775 or PhaseFail("Unable to create $TFTP_PROJ_DIR : $!"); ExecQuietFatal("$CHGRP tbadmin $TFTP_PROJ_DIR"); }; Phase "tftplink", "Linking /tftpboot", sub { DoneIfExists("/tftpboot"); ExecQuietFatal("ln -s $TFTP_DIR /tftpboot"); }; }; Phase "ports", "Installing ports", sub { if (!ExecQuiet("$PKG_INFO -e $BOSS_PORT") && (($FBSD_VERSION == 4) || !ExecQuiet("$PKG_INFO -e $PHP4_PORT"))) { PhaseSkip("Ports already installed"); } Phase "packages", "Installing packages", sub { if (!$packagedir) { PhaseSkip("No package directory provided"); } ExecQuietFatal("$ENV PKG_PATH=$packagedir $PKG_ADD $BOSS_PORT") if (!ExecQuiet("$PKG_INFO -e $BOSS_PORT")); ExecQuietFatal("$ENV PKG_PATH=$packagedir $PKG_ADD $PHP4_PORT") if ($FBSD_VERSION > 4 && !ExecQuiet("$PKG_INFO -e $PHP4_PORT")); }; if ($packagedir) { PhaseSkip("Packages installed okay"); } Phase "pcopy", "Copying ports into place", sub { DoneIfExists("$PORTSMISCDIR/emulab-boss"); ExecQuietFatal("$SH $SRCDIR/ports/ports-install"); }; if ($FBSD_VERSION == 4) { # Ick. The php4 port is broken with SSL, so we have to patch # it - hopefully it'll get fixed someday, and we remove this Phase "php4patch", "Patching php4 port", sub { if (!ExecQuiet("$PATCH -C -f -l -R -p0 -i $PHP4_PATCH")) { PhaseSkip("$PHP4_PATCH already applied"); } ExecQuietFatal("$PATCH -f -l -p0 -i $PHP4_PATCH"); }; } PhaseFail("Please install ports manually, since some\n of them are " . "interactive. Run: \n" . " cd $PORTSMISCDIR/emulab-boss && make install\n" . " cd $PORTSMISCDIR/emulab-php4 && make install\n" . "then re-run this script."); }; Phase "portfixup", "Fixing up packages", sub { Phase "rsync", "Looking for rsync", sub { if (!ExecQuiet("$PKG_INFO -x rsync")) { PhaseSkip("rsync already installed"); } PhaseFail("Must have a PKG_PATH variable set in your environment") if (!exists($ENV{"PKG_PATH"})); ExecQuietFatal("$PKG_ADD rsync-2.6.3_1"); }; if ($WINSUPPORT) { Phase "gcc30", "Looking for GCC 3.0 and installing", sub { if ($FBSD_VERSION > 4 || !ExecQuiet("$PKG_INFO -x gcc30")) { PhaseSkip("GCC 3.0 already installed"); } PhaseFail("Must have a PKG_PATH variable set in your environment") if (!exists($ENV{"PKG_PATH"})); ExecQuietFatal("$PKG_ADD gcc30-3.0.4_1"); }; } }; Phase "patches", "Applying patches", sub { if ($FBSD_VERSION == 4) { Phase "g++patch", "Patching g++'s STL", sub { if (!ExecQuiet("$PATCH -C -f -R -p0 -i $STL_PATCH")) { PhaseSkip("$STL_PATCH already applied"); } ExecQuietFatal("$PATCH -f -p0 -i $STL_PATCH"); }; } Phase "m2cryptopatch", "Patching m2crypto", sub { my $patchfile = $M2CRYPTO_PATCH; if (ExecQuiet("$PKG_INFO -I -x m2crypto | fgrep -q -s '0.13'") == 0) { $patchfile = "${patchfile}-0.13"; } if (!ExecQuiet("$PATCH -C -f -l -R -p0 -i $patchfile")) { PhaseSkip("$patchfile already applied"); } ExecQuietFatal("$PATCH -f -l -p0 -i $patchfile"); }; }; Phase "cracklib", "Installing cracklib", sub { DoneIfExists("$CRACKLIB_DICT"); my $pwd = `$PWD`; chomp $pwd; chdir "$TOP_SRCDIR/tbsetup/checkpass/cracklib,2.7" or PhaseFail "Unable to change to " . "$TOP_SRCDIR/tbsetup/checkpass/cracklib,2.7: $!"; ExecQuietFatal("make install clean"); chdir $pwd; }; Phase "apache", "Installing apache config file", sub { DoneIfEdited("$HTTPD_CONF"); # ICK!!! If we installed apache AFTER we unpacked the source tarball, # make will not properly install the new apache config file! So, we use # this shameful hack to force it to do so! ExecQuietFatal("$TOUCH -t 01010000 $HTTPD_CONF"); ExecQuietFatal("$GMAKE -C $TOP_OBJDIR/apache install"); }; Phase "rc.d", "Setting up rc.d scripts", sub { Phase "my-client", "Moving $RCDIR/mysql-client.sh", sub { DoneIfDoesntExist("$RCDIR/mysql-client.sh"); ExecQuietFatal("mv $RCDIR/mysql-client.sh $RCDIR/1.mysql-client.sh"); }; Phase "my-server", "Removing $RCDIR/mysql-server.sh", sub { DoneIfDoesntExist("$RCDIR/mysql-server.sh"); if (!unlink "$RCDIR/mysql-server.sh") { PhaseFail("Unable to remove $RCDIR/mysql-server.sh: $!"); } }; Phase "snmpd", "Removing snmpd startup script", sub { DoneIfDoesntExist("$RCDIR/snmpd.sh"); if (!unlink "$RCDIR/snmpd.sh") { PhaseFail("Unable to remove $RCDIR/snmpd.sh: $!"); } }; Phase "rsyncd", "Removing rsyncd startup script", sub { DoneIfDoesntExist("$RCDIR/rsyncd.sh"); if (!unlink "$RCDIR/rsyncd.sh") { PhaseFail("Unable to remove $RCDIR/rsyncd.sh: $!"); } }; Phase "dhcpd", "Removing isc-dhcpd startup script", sub { DoneIfDoesntExist("$RCDIR/isc-dhcpd.sh"); if (!unlink "$RCDIR/isc-dhcpd.sh") { PhaseFail("Unable to remove $RCDIR/isc-dhcpd.sh: $!"); } }; Phase "rc.testbed", "Installing testbed RC scripts", sub { Phase "elvind.sh", "Removing port version of elvind.sh", sub { DoneIfDoesntExist("$RCDIR/elvind.sh"); ExecQuietFatal("/bin/rm -f $RCDIR/elvind.sh"); }; DoneIfExists("$RCDIR/3.testbed.sh"); ExecQuietFatal("$GMAKE -C $TOP_OBJDIR/rc.d install"); }; }; Phase "syslog", "Setting up syslog", sub { Phase "sysconf", "Editing $SYSLOG_CONF", sub { DoneIfEdited($SYSLOG_CONF); # # Modify the /dev/console and /var/log/messages lines to exclude # testbed stuff # open(SC,"+<$SYSLOG_CONF") or PhaseFail("Unable to open $SYSLOG_CONF : $!"); my @sc = ; if (scalar(grep(/$LOGFACIL/, @sc)) != 0) { PhaseFail("Testbed chosen facility $LOGFACIL already in use in /etc/syslog.conf!"); } seek(SC,0,0); truncate(SC,0); foreach my $line (@sc) { my $cpat = q(\s+/dev/console); my $mpat = q(\s+/var/log/messages); if ($line =~ /^[^#].*$cpat/) { $line =~ s/($cpat)/\;$LOGFACIL.none$1/; } elsif ($line =~ /^[^#].*$mpat/) { $line =~ s/($mpat)/\;$LOGFACIL.none$1/; } print SC $line; } close(SC); AppendToFileFatal($SYSLOG_CONF, "!bootinfo", "*.*\t\t\t\t\t\t$LOGDIR/bootinfo.log", "!tmcd", "*.*\t\t\t\t\t\t$LOGDIR/tmcd.log", "!capture", "*.*\t\t\t\t\t\t$LOGDIR/capture.log", "!dhcpd", "*.*\t\t\t\t\t\t$LOGDIR/dhcpd.log", "!proxydhcpd","*.*\t\t\t\t\t\t$LOGDIR/proxydhcpd.log", "!tftpd", "*.*\t\t\t\t\t\t$LOGDIR/tftpd.log", "!capserver", "*.*\t\t\t\t\t\t$LOGDIR/capserver.log", "!frisbeed", "*.*\t\t\t\t\t\t$LOGDIR/frisbeed.log", "!elvind", "*.*\t\t\t\t\t\t$LOGDIR/elvind.log", "!stated", "*.*\t\t\t\t\t\t$LOGDIR/stated.log", "!osselect", "*.*\t\t\t\t\t\t$LOGDIR/osselect.log", "!genlastlog","*.*\t\t\t\t\t\t$LOGDIR/genlastlog.log", "!sdcollectd","*.*\t\t\t\t\t\t$LOGDIR/sdcollectd.log", "!plabgetfree","*.*\t\t\t\t\t\t$LOGDIR/plabgetfree.log", "!plabrenew", "*.*\t\t\t\t\t\t$LOGDIR/plabrenew.log", "!xmlrpcbag", "*.*\t\t\t\t\t\t$LOGDIR/xmlrpcbag.log", "!sshxmlrpc", "*.*\t\t\t\t\t\t$LOGDIR/sshxmlrpc.log", "!sslxmlrpc", "*.*\t\t\t\t\t\t$LOGDIR/sslxmlrpc.log"); }; Phase "logdir", "Creating log directory", sub { DoneIfExists($LOGDIR); mkdir $LOGDIR, 0775 or PhaseFail("Unable to create $LOGDIR : $!"); ExecQuietFatal("$CHGRP tbadmin $LOGDIR"); ExecQuietFatal("$CHMOD 775 $LOGDIR"); }; Phase "logdir", "Creating mysql log directory", sub { DoneIfExists($MYSQL_LOGDIR); mkdir $MYSQL_LOGDIR, 0775 or PhaseFail("Unable to create $MYSQL_LOGDIR : $!"); ExecQuietFatal("$CHOWN mysql:mysql $MYSQL_LOGDIR"); ExecQuietFatal("$CHMOD 775 $MYSQL_LOGDIR"); }; Phase "logfiles", "Creating log files", sub { foreach my $logfile (@LOGFILES) { Phase $logfile, $logfile, sub { DoneIfExists($logfile); CreateFileFatal($logfile); ExecQuietFatal("$CHGRP tbadmin $logfile"); ExecQuietFatal("$CHMOD 640 $logfile"); }; } }; Phase "newsyslog", "Setting up $NEWSYSLOG_CONF", sub { DoneIfEdited($NEWSYSLOG_CONF); AppendToFileFatal($NEWSYSLOG_CONF, "$LOGDIR/tmcd.log 640 9 1000 * Z", "$LOGDIR/stated.log 640 9 300 * Z", "$LOGDIR/osselect.log 640 9 300 * Z", "$LOGDIR/power.log 640 7 300 * Z", "$LOGDIR/frisbeed.log 640 7 300 * Z", "$LOGDIR/tftpd.log 640 7 200 * Z", "$LOGDIR/dhcpd.log 640 7 200 * Z", "$LOGDIR/bootinfo.log 640 7 200 * Z", "$LOGDIR/capserver.log 640 5 200 * Z", "$LOGDIR/elvind.log 640 5 1000 * Z", "$LOGDIR/suexec.log 640 3 200 * Z", "$LOGDIR/genlastlog.log 640 3 200 * Z", "$LOGDIR/genlastlog 640 3 200 * Z " . "/var/run/lastlog_daemon.pid", "$LOGDIR/plabmetrics.log 640 7 1000 * Z", "$LOGDIR/plablinkdata.log 640 7 1000 * Z", "$LOGDIR/xmlrpcbag.log 640 7 300 * Z", "$LOGDIR/sshxmlrpc.log 640 7 300 * Z", "$LOGDIR/sslxmlrpc.log 640 7 300 * Z"); }; }; Phase "database", "Setting up database", sub { Phase "initialize", "Initializing mysql", sub { PhaseSkip("mysqld already initialzed") if (-d "$MYSQLDBDIR/mysql"); ExecQuietFatal("$MYSQLINSTALL --ldata=${MYSQLDBDIR}"); ExecQuietFatal("$CHOWN -R mysql:mysql $MYSQLDBDIR"); }; Phase "mysql", "Starting mysqls", sub { if (!ExecQuiet("$MYSQLADMIN ping")) { PhaseSkip("mysqld already running"); } ExecQuietFatal("$RCDIR/2.mysql-server.sh start"); # Give mysqld some time to start, then make sure it did sleep 5; ExecQuietFatal("$MYSQLADMIN ping"); }; Phase "$DBNAME", "Creating $DBNAME", sub { if (!ExecQuiet("$MYSQLSHOW $DBNAME")) { PhaseSkip("tbdb already exists"); } ExecQuietFatal("$MYSQLADMIN create $DBNAME"); }; Phase "tables", "Creating tables in $DBNAME", sub { if (!ExecQuiet("$MYSQLDUMP -d $DBNAME users")) { PhaseSkip("Tables have already been created"); } ExecQuietFatal("$MYSQL $DBNAME < $TOP_SRCDIR/sql/database-create.sql"); }; Phase "dbdata", "Filling tables with initial data", sub { my ($exitval, @rows) = ExecQuiet("echo 'select * from " . "exported_tables' | $MYSQL -s $DBNAME"); if ($exitval) { PhaseFail("Error running query"); } if (scalar @rows) { PhaseSkip("Already done"); } ExecQuietFatal("$MYSQL $DBNAME < $TOP_SRCDIR/sql/database-fill.sql"); }; Phase "sdbdata", "Filling tables with supplemental data", sub { my ($exitval, @rows) = ExecQuiet("echo 'select * from " . "os_info' | $MYSQL -s $DBNAME"); if ($exitval) { PhaseFail("Error running query"); } if (scalar @rows) { PhaseSkip("Already done"); } ExecQuietFatal("$MYSQL $DBNAME < " . "$TOP_SRCDIR/sql/database-fill-supplemental.sql"); }; Phase "sitevars", "Setting sitevars to default values", sub { my ($exitval, @rows) = ExecQuiet("echo 'select * from " . "sitevariables' | $MYSQL -s $DBNAME"); if ($exitval) { PhaseFail("Error running query"); } if (scalar @rows) { PhaseSkip("Already done"); } ExecQuietFatal("$MYSQL $DBNAME < $TOP_SRCDIR/sql/sitevars-create.sql"); }; Phase "knowlbase", "Filling knowledge_base_entries table", sub { my ($exitval, @rows) = ExecQuiet("echo 'select * from " . "knowledge_base_entries' | $MYSQL -s $DBNAME"); if ($exitval) { PhaseFail("Error running query"); } if (scalar @rows) { PhaseSkip("Already done"); } ExecQuietFatal("$MYSQL $DBNAME < ". "$TOP_SRCDIR/sql/knowlbase-create.sql"); }; }; Phase "rc.conf", "Adding testbed content to $RCCONF", sub { DoneIfEdited($RCCONF); AppendToFileFatal($RCCONF, qq|rpcbind_enable="YES"|, qq|mountd_enable="YES"|, qq|nfs_server_enable="YES"|, qq|nfs_server_flags="-u -t -n 16"|, qq|nfs_client_enable="YES"|, qq|inetd_enable="YES"|, qq|inetd_flags="-wW -R 0"|, qq|named_enable="NO"|, qq|xntpd_enable="YES"|, qq|syslogd_flags=""|, qq|tftpd_flags="-lvvvv -C 40 -s /tftpboot"|, qq|apache_enable="YES"|); }; # # New version perl does not appear to require this anymore. # if ($FBSD_VERSION == 4) { Phase "suidperl", "Setting the suid bit on $SUIDPERL", sub { PhaseSkip("Already done") if (-u $SUIDPERL); ExecQuietFatal("$CHMOD u+s $SUIDPERL"); }; } Phase "hosts", "Adding boss/ops/fs IP addresses to $HOSTS", sub { DoneIfEdited($HOSTS); my $hstr = "${BOSSNODE_IP}\t${BOSSNODE} boss". "\n${USERNODE_IP}\t@OPS_NAMES"; if ($USERNODE ne $FSNODE) { $hstr .= "\n${FSNODE_IP}\t${FSNODE} fs"; } AppendToFileFatal($HOSTS, $hstr); }; Phase "resolve", "Checking to make sure names for boss/ops/fs resolve", sub { my @hnames = (@OPS_NAMES, ${BOSSNODE}, "boss"); if ($USERNODE ne $FSNODE) { push @hnames, ${FSNODE}, "fs"; } foreach my $name (@hnames) { Phase $name, $name, sub { if (gethostbyname($name)) { PhaseSucceed("$name resolves"); } else { PhaseFail("$name does not resolve - please see setup.txt\n" . "for further instructions!"); } }; } }; Phase "NFSmounts", "Setting up NFS mounts", sub { Phase "mountpoints", "Creating mountpoints", sub { foreach my $dir (@MOUNTPOINTS) { Phase $dir, $dir, sub { DoneIfExists($dir); mkdir $dir, 0777 or PhaseFail("Unable to create $dir : $!"); }; } }; Phase "fstab", "Adding NFS mounts to $FSTAB", sub { DoneIfEdited($FSTAB); AppendToFileFatal($FSTAB, "$FSNODE:/users\t\t/users\tnfs\trw,nodev,nosuid\t0\t0", "$FSNODE:/proj\t\t/proj\tnfs\trw,nodev,nosuid\t0\t0", "$FSNODE:/groups\t\t/groups\tnfs\trw,nodev,nosuid\t0\t0", "$FSNODE:/share\t\t/share\tnfs\trw,nodev,nosuid\t0\t0", "$USERNODE:/usr/testbed\t\t$OPSDIR_DIR\tnfs\trw,soft,". "-b,nodev,nosuid\t0\t0", "$USERNODE:/var\t\t$USERSVAR_DIR\tnfs\tro,soft,". "-b,nodev,nosuid\t0\t0"); }; Phase "mounts", "Mounting NFS filesystems", sub { foreach my $dir (@MOUNTPOINTS) { Phase $dir, $dir, sub { DoneIfMounted($dir); ExecQuietFatal("$MOUNT -o '-R 1' $dir"); }; } }; }; # # Not needed in an inner elab, and it takes a long time. # if (! $ELABINELAB) { Phase "mibs", "Fetching Cisco MIBs", sub { foreach my $mib (@CISCO_MIBS) { my $localfile = "$MIBPATH/$mib.txt"; my $mibURL = "$CISCO_MIB_FTP/$mib.my"; Phase "$mib", "Fetching $mib", sub { DoneIfExists($localfile); FetchFileFatal($mibURL,$localfile); }; } }; } Phase "cron", "Adding cron jobs", sub { Phase "crontab", "Editing $CRONTAB", sub { DoneIfEdited($CRONTAB); AppendToFileFatal($CRONTAB, "0 \t6\t*\t*\t*\troot\t$PREFIX/sbin/audit", "0 \t1\t*\t*\t*\troot\t$PREFIX/sbin/dbcheck", "45 \t1\t*\t*\t*\troot\t$PREFIX/sbin/backup", "*/5\t*\t*\t*\t*\troot\t$PREFIX/sbin/node_status", "*/5\t*\t*\t*\t*\troot\t$PREFIX/sbin/idlemail"); }; }; Phase "sudoers", "Editing $SUDOERS", sub { DoneIfEdited($SUDOERS); AppendToFileFatal($SUDOERS,"%wheel ALL=(ALL) NOPASSWD: ALL"); }; Phase "php.ini", "Creating php.ini file", sub { DoneIfExists($PHP_INI); CreateFileFatal($PHP_INI, "[PHP]","", ";", "; So that quotes are not escaped. Needed for netbuild application.", ";", "magic_quotes_gpc = Off","", ";", "; Our scripts depend on this!", ";", "register_globals = On"); }; Phase "ssh", "Setting up root ssh from boss to ops", sub { Phase "keygen", "Creating root private key", sub { DoneIfExists($ROOT_PRIVKEY); ExecQuietFatal("$SSH_KEYGEN -t rsa1 -P '' -f $ROOT_PRIVKEY"); }; # # Stick it into the DB. # WARNING: This sitevar (node/ssh_pubkey) is referenced in tmcd.c # Phase "sitevar", "Inserting pubkey into DB", sub { my $pubkey = `cat $ROOT_PUBKEY`; chomp $pubkey; ExecQuietFatal("echo \"update sitevariables set value='$pubkey' ". " where name='node/ssh_pubkey'\" | ". "$MYSQL $DBNAME"); }; Phase "ssh", "Editing ssh config file", sub { DoneIfEdited($SSH_CONFIG); AppendToFileFatal($SSH_CONFIG, "Host *", " StrictHostKeyChecking no", " Protocol 1,2"); }; Phase "keycopy", "Copy root ssh keys to ops and fs", sub { if (! ExecQuiet("$SSH -o 'BatchMode=yes' root\@${USERNODE} pwd")) { PhaseSkip("Key already copied"); } else { ExecQuietFatal("$SCP -i $INIT_PRIVKEY ". "$ROOT_PUBKEY ${USERNODE}:$ROOT_AUTHKEY"); # Copy hosts keys to ops. ExecQuietFatal("cat ${ETCSSH}/*.pub | $SSH ${USERNODE} ". " '(cat > ${ETCSSH}/ssh_known_hosts)'"); if (ExecQuiet("$SSH -o 'BatchMode=yes' root\@${USERNODE} pwd")) { PhaseFail("You need to manually copy boss's public SSH key\n". "over to $USERNODE so boss can get into it without\n". "a password. Run the following as root:\n" . "scp $ROOT_PUBKEY ${USERNODE}:$ROOT_AUTHKEY"); } } }; Phase "keycopy2", "Copy root ssh keys to fs", sub { if ($USERNODE eq $FSNODE) { PhaseSkip("FS node is ops node"); } if (! ExecQuiet("$SSH -o 'BatchMode=yes' root\@${FSNODE} pwd")) { PhaseSkip("Key already copied"); } else { ExecQuietFatal("$SCP -i $INIT_PRIVKEY ". "$ROOT_PUBKEY ${FSNODE}:$ROOT_AUTHKEY"); # Copy hosts keys to fs. ExecQuietFatal("cat ${ETCSSH}/*.pub | $SSH ${FSNODE} ". " '(cat > ${ETCSSH}/ssh_known_hosts)'"); if (ExecQuiet("$SSH -o 'BatchMode=yes' root\@${FSNODE} pwd")) { PhaseFail("You need to manually copy boss's public SSH key\n". "over to $FSNODE so boss can get into it without\n". "a password. Run the following as root:\n" . "scp $ROOT_PUBKEY ${FSNODE}:$ROOT_AUTHKEY"); } } }; }; Phase "rndc", "Setting up rndc for control of nameserver", sub { DoneIfExists($RNDC_KEY); ExecQuietFatal("$RNDC_CONFGEN -a -r /dev/urandom"); }; Phase "loader.conf", "Setting up $LOADER_CONF", sub { DoneIfEdited($LOADER_CONF); AppendToFileFatal($LOADER_CONF, "kern.hz=1000" ); }; Phase "sysctl.conf", "Setting up $SYSCTL_CONF", sub { DoneIfEdited($SYSCTL_CONF); AppendToFileFatal($SYSCTL_CONF, "net.local.dgram.maxdgram=65536", "net.local.dgram.recvspace=65536" ); }; Phase "sslcerts", "Setting up SSL certificates", sub { Phase "sslgen", "Generating SSL certificates", sub { DoneIfExists("$TOP_OBJDIR/ssl/$EMULAB_PEM"); ExecQuietFatal("$GMAKE -C $TOP_OBJDIR/ssl remote-site"); }; Phase "sslinstall", "Installing SSL certificates", sub { DoneIfExists("$PREFIX/etc/$EMULAB_PEM"); ExecQuietFatal("$GMAKE -C $TOP_OBJDIR/ssl remote-site-boss-install"); }; Phase "sslopscopy", "Copying SSL certificates to ops", sub { ExecQuietFatal("$SSH -o 'BatchMode=yes' root\@${USERNODE} ". " mkdir -p ${ETC_EMULAB_DIR} "); ExecQuietFatal("$SCP $PREFIX/etc/$EMULAB_PEM ". " ${USERNODE}:${ETC_EMULAB_DIR}"); ExecQuietFatal("$SCP $TOP_OBJDIR/ssl/$CTRLNODE_PEM". " ${USERNODE}:${ETC_EMULAB_DIR}/${CLIENT_PEM}"); ExecQuietFatal("$SSH -o 'BatchMode=yes' root\@${USERNODE} ". " 'chmod 640 ${ETC_EMULAB_DIR}/${CLIENT_PEM}; ". " chmod 640 ${ETC_EMULAB_DIR}/${EMULAB_PEM}' "); }; Phase "apache", "Setting up Apache on boss", sub { Phase "cert", "Installing Apache SSL certificate", sub { DoneIfExists("$APACHE_CERTFILE"); ExecQuietFatal("$CP $TOP_OBJDIR/ssl/$APACHE_CERTPEM ". " $APACHE_CERTFILE"); }; Phase "key", "Installing Apache SSL key", sub { DoneIfExists("$APACHE_KEYFILE"); ExecQuietFatal("$CP $TOP_OBJDIR/ssl/$APACHE_KEYPEM ". " $APACHE_KEYFILE"); }; Phase "rc.d", "Installing Apache startup file", sub { DoneIfExists("$RCDIR/apache.sh"); ExecQuietFatal("mv $RCDIR/apache.sh.sample $RCDIR/apache.sh"); }; Phase "starting", "Starting Apache server", sub { DoneIfExists("$VARRUN/httpd.pid"); ExecQuietFatal("$RCDIR/apache.sh start"); }; }; if ($FBSD_VERSION > 4) { Phase "apache", "Setting up Apache on ops", sub { Phase "cert", "Installing Apache SSL certificate", sub { ExecQuietFatal("$SCP $TOP_OBJDIR/ssl/$APACHE_CERTPEM_OPS ". " ${USERNODE}:$APACHE_CERTFILE_OPS"); }; Phase "key", "Installing Apache SSL key", sub { ExecQuietFatal("$SCP $TOP_OBJDIR/ssl/$APACHE_KEYPEM_OPS ". " ${USERNODE}:$APACHE_KEYFILE_OPS"); }; Phase "rc.d", "Installing Apache startup file", sub { ExecQuietFatal("$SSH -o 'BatchMode=yes' root\@${USERNODE} ". " cp -f $RCDIR/apache.sh.sample ". " $RCDIR/apache.sh"); }; }; } }; Phase "event", "Setting up event system", sub { Phase "elvinconf", "Installing elvind config file", sub { DoneIfIdentical($ELVIND_CONF,$BOSS_ELVIND_CONF); ExecQuietFatal("$CP $BOSS_ELVIND_CONF $ELVIND_CONF"); }; # For elabinelab, be sure to kill off running event system. Harmless. Phase "stopping", "Stopping event system", sub { if (ExecQuiet("killall -s elvind")) { PhaseSkip("elvind not running"); } ExecQuietFatal("$RCDIR/2.elvind.sh stop"); }; Phase "starting", "Starting event system", sub { ExecQuietFatal("$RCDIR/2.elvind.sh start"); }; }; # # Build and install software. Note that I am not looking to see if its # been done; I suppose we could touch a file to indicate that build has # been completed, but maybe that is not such a good idea. # Phase "Software", "Building and Installing Software", sub { Phase "building", "Building (please be patient)", sub { ExecQuietFatal("cd $TOP_OBJDIR; $GMAKE"); }; Phase "installing", "Installing (please be patient)", sub { ExecQuietFatal("cd $TOP_OBJDIR; $GMAKE boss-install"); }; Phase "postinstall", "Post Installing Testbed Software", sub { ExecQuietFatal("cd $TOP_OBJDIR; $GMAKE post-install"); }; }; # # The next few items must be after the software install since they use # testbed libraries and such. # Phase "dhcpd", "Setting up initial dhcpd configuration", sub { Phase "template", "Installing $DHCPD_TEMPLATE", sub { ExecQuietFatal("cd $TOP_OBJDIR/dhcpd; $GMAKE install"); }; Phase "config", "Creating $DHCPD_CONF from template", sub { ExecQuietFatal("$DHCPD_MAKECONF -i"); }; # How silly is this? Phase "leases", "Creating stub leases file", sub { DoneIfExists("$DHCPD_LEASES"); ExecQuietFatal("touch $DHCPD_LEASES"); }; }; Phase "named", "Setting up initial named configuration", sub { Phase "building", "Building named files and templates", sub { ExecQuietFatal("cd $TOP_OBJDIR/named; $GMAKE"); }; Phase "installing", "Installing named files and templates", sub { ExecQuietFatal("cd $TOP_OBJDIR/named; $GMAKE install-real"); }; Phase "generating", "Generating named zone files", sub { ExecQuietFatal("$NAMED_SETUP -norestart"); }; Phase "stopping", "Stopping named", sub { if (ExecQuiet("killall -s named")) { PhaseSkip("named not running"); } ExecQuietFatal("$RCDIR/1.named.sh stop"); }; Phase "starting", "Starting named", sub { ExecQuietFatal("$RCDIR/1.named.sh start"); }; Phase "copying", "Copying resolv.conf over to ops", sub { ExecQuietFatal("$SCP $TOP_OBJDIR/named/resolv.conf.ops ". " ${USERNODE}:/etc/resolv.conf"); }; Phase "copying2", "Copying resolv.conf over to fs", sub { if ($USERNODE eq $FSNODE) { PhaseSkip("FS node is ops node"); } ExecQuietFatal("$SCP $TOP_OBJDIR/named/resolv.conf.ops ". " ${FSNODE}:/etc/resolv.conf"); }; }; Phase "firstuser", "Setting up initial user (elabman)", sub { PhaseSkip("elabman already created") if (-d "/users/elabman"); ExecQuietFatal("perl $TOP_OBJDIR/utils/firstuser -b ". (defined($password) ? " -p $password" : "")); }; Phase "chkupuser", "Setting up checkup user (elabckup)", sub { PhaseSkip("elabckup already created") if (-d "/users/elabckup"); ExecQuietFatal("perl $TOP_OBJDIR/utils/firstuser -b ". (defined($password) ? " -p $password" : ""). " -u elabckup -n 'Emulab Checkup User' ". "-e '@TBTESTSUITEEMAIL@'"); }; Phase "experiments", "Setting up system experiments", sub { foreach my $eid (keys(%EXPERIMENTS)) { my $pid = $EXPERIMENTS{$eid}->{"pid"}; my $desc = $EXPERIMENTS{$eid}->{"description"}; Phase "$pid/$eid", "$pid/$eid", sub { PhaseSkip("Experiment Created") if (-d "/proj/$pid/exp/$eid"); ExecQuietFatal("$SUDO -u elabman $WAP $BATCHEXP ". " -q -i -w -f -n -S 'System Experiment' ". " -L 'System Experiment' ". " -E '$desc - DO NOT DELETE' ". " -p $pid -e $eid"); }; } }; print "----------------------------------------------------------------------\n"; print "Installation completed succesfully!\n"; print "Please reboot this machine before proceeding with boss setup\n"; exit 0;