elabinelab_bossinit.in 7.8 KB
Newer Older
1
#!/usr/bin/perl -wT
2 3
#
# EMULAB-COPYRIGHT
4
# Copyright (c) 2000-2010 University of Utah and the Flux Group.
5 6 7 8 9 10 11 12 13
# All rights reserved.
#
use English;
use Getopt::Std;

#
# ElabInElab: This is run on the inner boss to construct a bunch stuff
# from the db (groups, projects, users, etc).
#
14 15 16 17 18 19
# We also use this opportunity to munge node-related bootstrap state.
# Right now we only load the standard BSD-based pxeboot and MFSes into
# an elabinelab, but many of our node types now use the Linux-based tools
# instead.  So for the moment, we tweak the DB to rewrite everything to use
# the BSD tools.
#
20 21 22 23 24 25 26
sub usage()
{
    print STDERR "Usage: $0 [-d] <pid>\n";
    exit(1);
}
my $optlist = "d";
my $debug   = 0;
27
sub mysystem($);
28 29 30 31 32 33 34

#
# Configure variables
#
my $TB		= "@prefix@";
my $TBOPS       = "@TBOPSEMAIL@";
my $ELABINELAB  = @ELABINELAB@;
35
my $SAVEUID     = $UID;
36 37 38 39 40 41 42 43 44 45 46 47

# un-taint path
$ENV{'PATH'} = '/bin:/usr/bin:/usr/sbin:/usr/local/bin';
delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};

# Turn off line buffering on output
$| = 1;

use lib "@prefix@/lib";
use libdb;
use libtestbed;

Leigh B. Stoller's avatar
Leigh B. Stoller committed
48 49 50
# Defined in libdb ...
my $TBOPSPID    = TBOPSPID();

51 52 53 54 55 56 57 58 59
if (!$ELABINELAB) {
    die("*** $0:\n".
	"    This script can only run on an inner Emulab!\n");
}
# Only admin types!
if (!TBAdmin($UID)) {
    die("*** $0:\n".
	"    Only TB administrators can run this script!\n");
}
60 61 62 63 64 65 66
#
# We don't want to run this script unless its the real version.
#
if ($EUID != 0) {
    die("*** $0:\n".
	"    Must be root! Maybe its a development version?\n");
}
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84

#
# Parse command arguments. Once we return from getopts, all that should
# left are the required arguments.
#
%options = ();
if (! getopts($optlist, \%options)) {
    usage();
}
if (defined($options{"d"})) {
    $debug = 1;
}

usage()
    if (scalar(@ARGV) != 1);
my $pid = shift();

#
85
# Untaint the arguments.
86
#
87 88 89 90 91 92
if ($pid =~ /^([-\w]+)$/) {
    $pid = $1;
}
else {
    die("Tainted argument $pid!\n");
}
93

94
# Temporary ... See utils/firstuser ... 
95 96 97
DBQueryFatal("update group_membership set pid_idx=1,gid_idx=1 ".
	     "where pid='$TBOPSPID' and pid=gid");

98 99 100 101
# Do not want to share the UUIDs with outer Emulab.
DBQueryFatal("update users set uid_uuid=UUID()");
DBQueryFatal("update nodes set uuid=UUID()");

102
#
103
# Shift to real user for these scripts.
104
#
105 106 107 108 109
$EUID = $UID;

#
# Build the project.
#
110
mysystem("$TB/sbin/mkproj -s $pid");
111 112 113

#
# Get the list of users and admin status. Admin users get a real shell
Mike Hibler's avatar
Mike Hibler committed
114
# on boss. Create the users, and note that we have to do this before the
115
# groups are created (tbacct add does not do a setgroups).
116
#
117
my $users_result =
118
    DBQueryFatal("select distinct u.uid,u.uid_idx,u.admin,u.status ".
119 120
		 "   from group_membership as m ".
		 "left join users as u on u.uid_idx=m.uid_idx ");
121
while (my ($uid,$uid_idx,$admin,$status) = $users_result->fetchrow_array()) {
122 123 124
    next
	if ($uid eq "elabman");
    
125 126
    if ($admin) {
	# Add admin users to group wheel for convenience.
127
	DBQueryFatal("replace into unixgroup_membership ".
128
		     "values ('$uid','$uid_idx','wheel')");
129
    }
130 131 132 133 134
    next
	if ($status ne USERSTATUS_ACTIVE());
    
    mysystem("$TB/sbin/tbacct -b add $uid");
    
135
    if ($admin) {
136 137
	# Flip back to root for pw command.
	$EUID = 0;
138
	mysystem("pw usermod -n $uid -s /bin/tcsh");
139
	$EUID = $UID;
140 141 142
    }
}

143 144 145 146
#
# Get the list of subgroups in the project and create those groups.
#
my $query_result =
147 148
    DBQueryFatal("select gid_idx from groups where pid='$pid' and pid!=gid");
while (my ($gid_idx) = $query_result->fetchrow_array()) {
149
    mysystem("$TB/sbin/mkgroup -s $gid_idx");
150 151 152 153 154 155
}

#
# Now do a setgroups.
#
$users_result->dataseek(0);
Mike Hibler's avatar
Mike Hibler committed
156
while (my ($uid,$uid_idx,$admin,$status) = $users_result->fetchrow_array()) {
157 158
    next
	if ($uid eq "elabman");
159 160
    next
	if ($status ne USERSTATUS_ACTIVE());
161 162 163 164
    
    mysystem("$TB/sbin/setgroups $uid");
}

165 166 167 168 169 170 171
#
# Do the exports setup and the genelists all at once now that all the above
# stuff happened.
#
mysystem("$TB/sbin/genelists -a");
mysystem("$TB/sbin/exports_setup");

172 173 174 175 176 177 178 179
#
# Fixup pxeboot and MFS info.
#
# Right now we only load the standard BSD-based pxeboot and MFSes into
# an elabinelab, but many of our node types use the Linux-based tools instead.
# So for the moment, we tweak the DB to rewrite everything to use the BSD
# tools.
#
Mike Hibler's avatar
Mike Hibler committed
180 181 182 183 184 185 186 187 188 189 190 191 192 193
# Note that re-writing the pxe_boot_path doesn't have any effect for elabs
# NOT using the SINGLE_CONTROLNET setting.  This is because as long as
# outer (real) boss responds first, the filename it returns is what gets
# used.  We could rewrite pxe_boot_path in the real boss DB for nodes
# that are in an elabinelab, but then we could lose custom per-node settings
# for that field.  To fix that, we could introduce a temporary field for
# holding any custom value, but I don't want to go there...
#
# Anyway, the way we work around the non-SINGLE_CONTROLNET problem is to
# find all the custom pxe_boot_path values (in nodes or node_type_attributes)
# and "create" them on the inner boss by symlinking them to the standard
# pxeboot.  SWEET!
#
# XXX this should go away if/when we settle on a single set of tools.
194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214
#
if (1) {
    # first find the OSIDs for the "standard" MFSes
    my $qr = DBQueryFatal("select osname,osid from os_info where osname in ".
			  "  ('FREEBSD-MFS','FRISBEE-MFS','NEWNODE-MFS') ".
			  " and pid='$TBOPSPID'");
    my ($amfs,$dmfs,$nmfs,$nmfspath);
    while (my ($name, $osid) = $qr->fetchrow_array()) {
	if ($name eq "FREEBSD-MFS") {
	    $amfs = $osid;
	} elsif ($name eq "FRISBEE-MFS") {
	    $dmfs = $osid;
	} else {
	    $nmfs = $osid;
	    $nmfspath = "/tftpboot/freebsd.newnode"; # XXX hardwired
	}
    }

    # make sure newnode MFS points to the correct place
    DBQueryFatal("update os_info set path='$nmfspath' where osid=$nmfs");

Mike Hibler's avatar
Mike Hibler committed
215 216 217 218 219 220 221 222 223 224 225 226 227 228 229
    # collect up non-standard PXE boot paths, first from node_type_attributes..
    my @bogoboots = ();
    $qr = DBQueryFatal("select attrvalue from node_type_attributes ".
		       "where attrkey='pxe_boot_path' and attrvalue!='' ".
		       "group by attrvalue");
    while (my ($path) = $qr->fetchrow_array()) {
	push @bogoboots, $path;
    }
    # ..and then from nodes
    $qr = DBQueryFatal("select pxe_boot_path from nodes ".
		       "where pxe_boot_path is not NULL and role='testnode'");
    while (my ($path) = $qr->fetchrow_array()) {
	push @bogoboots, $path;
    }

230 231 232 233 234 235 236 237 238 239 240 241
    # and find all the node types and update their attributes.
    $qr = DBQueryFatal("select type from node_types where class='pc'");
    while (my ($ntype) = $qr->fetchrow_array()) {
	# XXX assumes that BSD version is the default in dhcpd.conf.template
	DBQueryFatal("delete from node_type_attributes ".
		     "  where attrkey='pxe_boot_path' and type='$ntype'");
	DBQueryFatal("update node_type_attributes set attrvalue='$amfs' ".
		     "  where attrkey='adminmfs_osid' and type='$ntype'");
	DBQueryFatal("update node_type_attributes set attrvalue='$dmfs' ".
		     "  where attrkey='diskloadmfs_osid' and type='$ntype'");
    }

Mike Hibler's avatar
Mike Hibler committed
242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262
    # fixup any nodes table entries with non-standard pxe_boot_path's
    DBQueryFatal("update nodes set pxe_boot_path=NULL ".
		 "  where pxe_boot_path is not NULL");

    #
    # Now symlink all the alternate boots to pxeboot.emu
    # XXX we assume everything is at the top level of /tftpboot right now.
    #
    foreach my $boot (@bogoboots) {
	if ($boot =~ /^\/tftpboot\/([^\/]+)$/) {
	    $boot = $1;
	    if (! -e "/tftpboot/$boot") {
		if (system("ln -s pxeboot.emu /tftpboot/$boot")) {
		    print STDERR
			"*** could not symlink non-standard boot '$boot';",
			" some inner nodes will not boot properly!\n";
		}
	    }
	}
    }

263 264
    #
    # Remake the dhcpd.conf file to reflect any pxeboot change.
265
    # XXX dhcpd is not running yet so don't need this.
266
    #
267
    #mysystem("$TB/sbin/dhcpd_makeconf -ir");
268 269
}

270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286
#
# Run a command string.
#
sub mysystem($)
{
    my ($command) = @_;

    if ($debug) {
	print "Command: '$command\'\n";
    }

    system($command);
    if ($?) {
	die("*** $0:\n".
	    "    Command failed: $? - $command\n");
    }
}