elabinelab_bossinit.in 9.76 KB
Newer Older
1
#!/usr/bin/perl -wT
2
#
3
# Copyright (c) 2000-2011 University of Utah and the Flux Group.
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
# 
# {{{EMULAB-LICENSE
# 
# This file is part of the Emulab network testbed software.
# 
# This file is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or (at
# your option) any later version.
# 
# This file is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public
# License for more details.
# 
# You should have received a copy of the GNU Affero General Public License
# along with this file.  If not, see <http://www.gnu.org/licenses/>.
# 
# }}}
23 24 25 26 27 28 29 30
#
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).
#
31 32 33 34 35 36
# 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.
#
37 38 39 40 41 42 43
sub usage()
{
    print STDERR "Usage: $0 [-d] <pid>\n";
    exit(1);
}
my $optlist = "d";
my $debug   = 0;
44
sub mysystem($);
45 46 47 48 49 50 51

#
# Configure variables
#
my $TB		= "@prefix@";
my $TBOPS       = "@TBOPSEMAIL@";
my $ELABINELAB  = @ELABINELAB@;
52
my $SAVEUID     = $UID;
53 54 55 56 57 58 59 60 61 62 63 64

# 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 Stoller's avatar
Leigh Stoller committed
65 66 67
# Defined in libdb ...
my $TBOPSPID    = TBOPSPID();

68 69 70 71 72 73 74 75 76
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");
}
77 78 79 80 81 82 83
#
# 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");
}
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101

#
# 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();

#
102
# Untaint the arguments.
103
#
104 105 106 107 108 109
if ($pid =~ /^([-\w]+)$/) {
    $pid = $1;
}
else {
    die("Tainted argument $pid!\n");
}
110

Mike Hibler's avatar
Mike Hibler committed
111 112 113 114 115 116 117 118 119 120 121
#
# XXX Fixup the pid/gid indicies for emulab-ops in various tables.
# At this point they have the values of the outer Emulab so they need
# to be reset to reflect the newly minted version here.
#
my @PIDGIDTABLES = ("group_membership", "images");
my @PIDONLYTABLES = ("os_info");

my $q = DBQueryFatal("select pid_idx from projects ".
		     "where pid='$TBOPSPID'");
my ($npid) = $q->fetchrow_array();
122 123
$q = DBQueryFatal("select gid_idx from groups ".
		  "where pid='$TBOPSPID' and gid=pid");
Mike Hibler's avatar
Mike Hibler committed
124 125 126 127 128 129 130 131 132
my ($ngid) = $q->fetchrow_array();

foreach my $table (@PIDGIDTABLES) {
    DBQueryFatal("update $table set pid_idx=$npid,gid_idx=$ngid ".
		 "where pid='$TBOPSPID' and pid=gid");
}
foreach my $table (@PIDONLYTABLES) {
    DBQueryFatal("update $table set pid_idx=$npid where pid='$TBOPSPID'");
}
133

134 135 136 137
# Do not want to share the UUIDs with outer Emulab.
DBQueryFatal("update users set uid_uuid=UUID()");
DBQueryFatal("update nodes set uuid=UUID()");

138
#
139
# Shift to real user for these scripts.
140
#
141 142 143 144 145
$EUID = $UID;

#
# Build the project.
#
146
mysystem("$TB/sbin/mkproj -s $pid");
147 148 149

#
# Get the list of users and admin status. Admin users get a real shell
Mike Hibler's avatar
Mike Hibler committed
150
# on boss. Create the users, and note that we have to do this before the
151
# groups are created (tbacct add does not do a setgroups).
152
#
153
my $users_result =
154
    DBQueryFatal("select distinct u.uid,u.uid_idx,u.admin,u.status ".
155 156
		 "   from group_membership as m ".
		 "left join users as u on u.uid_idx=m.uid_idx ");
157 158 159 160

# Need to do this we want to seek around inside the results.
$users_result = $users_result->WrapForSeek();

161
while (my ($uid,$uid_idx,$admin,$status) = $users_result->fetchrow_array()) {
162 163 164
    next
	if ($uid eq "elabman");
    
165 166
    if ($admin) {
	# Add admin users to group wheel for convenience.
167
	DBQueryFatal("replace into unixgroup_membership ".
168
		     "values ('$uid','$uid_idx','wheel')");
169
    }
170 171 172 173 174
    next
	if ($status ne USERSTATUS_ACTIVE());
    
    mysystem("$TB/sbin/tbacct -b add $uid");
    
175
    if ($admin) {
176 177
	# Flip back to root for pw command.
	$EUID = 0;
178
	mysystem("pw usermod -n $uid -s /bin/tcsh");
179
	$EUID = $UID;
180 181 182
    }
}

183 184 185 186
#
# Get the list of subgroups in the project and create those groups.
#
my $query_result =
187 188
    DBQueryFatal("select gid_idx from groups where pid='$pid' and pid!=gid");
while (my ($gid_idx) = $query_result->fetchrow_array()) {
189
    mysystem("$TB/sbin/mkgroup -s $gid_idx");
190 191 192 193 194 195
}

#
# Now do a setgroups.
#
$users_result->dataseek(0);
Mike Hibler's avatar
Mike Hibler committed
196
while (my ($uid,$uid_idx,$admin,$status) = $users_result->fetchrow_array()) {
197 198
    next
	if ($uid eq "elabman");
199 200
    next
	if ($status ne USERSTATUS_ACTIVE());
201 202 203 204
    
    mysystem("$TB/sbin/setgroups $uid");
}

205 206 207 208 209 210 211
#
# 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");

212 213 214 215 216 217 218 219 220 221 222 223
#
# Set some sitevars that are different for inner elabs
#
if (TBSiteVarExists("images/frisbee/maxrate_std")) {
    # XXX traditional value for elabinelab
    TBSetSiteVar("images/frisbee/maxrate_std", "54000000");
}
if (TBSiteVarExists("images/frisbee/maxrate_usr")) {
    # XXX traditional value for elabinelab
    TBSetSiteVar("images/frisbee/maxrate_usr", "54000000");
}

224 225 226 227 228 229 230 231
#
# 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
232 233 234 235 236 237 238 239 240 241 242 243 244 245
# 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.
246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266
#
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
267 268 269 270 271 272 273 274 275 276 277 278 279 280 281
    # 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;
    }

282 283 284 285 286 287 288 289 290 291 292 293
    # 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
294
    # fixup any nodes table entries with non-standard pxe_boot_path's
295
    # and clear any one-shot values
Mike Hibler's avatar
Mike Hibler committed
296 297
    DBQueryFatal("update nodes set pxe_boot_path=NULL ".
		 "  where pxe_boot_path is not NULL");
298 299
    DBQueryFatal("update nodes set next_pxe_boot_path=NULL ".
		 "  where next_pxe_boot_path is not NULL");
Mike Hibler's avatar
Mike Hibler committed
300 301 302 303 304

    #
    # Now symlink all the alternate boots to pxeboot.emu
    # XXX we assume everything is at the top level of /tftpboot right now.
    #
305
    $EUID = 0;
Mike Hibler's avatar
Mike Hibler committed
306 307 308 309 310 311 312 313 314 315 316 317
    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";
		}
	    }
	}
    }
318
    $EUID = $UID;
Mike Hibler's avatar
Mike Hibler committed
319

320 321
    #
    # Remake the dhcpd.conf file to reflect any pxeboot change.
322
    # XXX dhcpd is not running yet so don't need this.
323
    #
324
    #mysystem("$TB/sbin/dhcpd_makeconf -ir");
325 326
}

327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343
#
# Run a command string.
#
sub mysystem($)
{
    my ($command) = @_;

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

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