rc.storage 9.15 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
#!/usr/bin/perl -w
#
# Copyright (c) 2004-2013 University of Utah and the Flux Group.
# 
# {{{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/>.
# 
# }}}
#
use English;
use Getopt::Std;
use Socket qw(inet_ntoa);
27
use Storable;
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44

sub usage()
{
    print "Usage: " .
	scriptname() . " [-j vnodeid] boot|shutdown|reconfig|reset\n";
    exit(1);
}
my $optlist = "j:";
my $action  = "boot";

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

# Drag in path stuff so we can find emulab stuff.
BEGIN { require "/etc/emulab/paths.pm"; import emulabpaths; }

# Only root.
45
if ($EUID != 0) {
46 47 48 49 50 51
    die("*** $0:\n".
	"    Must be root to run this script!\n");
}

# Script specific goo
my $IQN_PREFIX = "iqn.2000-10.net.emulab";
52
my $OLDCONFIG = "$VARDIR/db/storage.conf";
53
my $STORAGEMAP = "$BOOTDIR/storagemap";
54 55 56 57 58 59 60

#
# Load the OS independent support library. It will load the OS dependent
# library and initialize itself. 
# 
use libsetup;
use liblocsetup;
61
use liblocstorage;
62 63 64 65 66 67 68 69 70 71 72 73 74
use libtmcc;
use librc;

#
# Not all clients support this.
#
exit(0)
    if (MFS() || (REMOTE() && !(REMOTEDED() || JAILED() || PLAB())));

# Protos.
sub doboot();
sub doshutdown();
sub doreconfig();
75
sub docleanup($);
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104

# Parse command line.
if (! getopts($optlist, \%options)) {
    usage();
}
if (defined($options{'j'})) {
    my $vnodeid = $options{'j'};
    libsetup_setvnodeid($vnodeid);
}
# Allow default above.
if (@ARGV) {
    $action = $ARGV[0];
}

# Execute the action.
SWITCH: for ($action) {
    /^boot$/i && do {
	doboot();
	last SWITCH;
    };
    /^shutdown$/i && do {
	doshutdown();
	last SWITCH;
    };
    /^reconfig$/i && do {
	doreconfig();
	last SWITCH;
    };
    /^reset$/i && do {
105
	docleanup(1);
106 107 108 109 110 111 112 113 114 115 116 117 118 119 120
	last SWITCH;
    };
    fatal("Invalid action: $action\n");
}
exit(0);

#
# Boot Action.
#
sub doboot()
{
    my $bossip;

    print STDOUT "Checking Testbed storage configuration ... \n";

121
    # XXX uncomment this for tmp testing with alternate tmcd
122
    #configtmcc("portnum",7778);
123

124 125 126
    my @cmds;
    if (getstorageconfig(\@cmds) != 0) {
	fatal("Error grabbing storage config!");
127 128
    }

129
    if (!@cmds) {
130
	#warn("*** No storageconfig output - nothing to do");
131
	return;
132 133
    }

134
    #
135 136
    # Process each command in turn.  Already sorted by
    # getstorageconfig().
137
    #
138 139 140 141
    my $so = os_init_storage(\@cmds);
    if (!$so) {
	fatal("Could not initialize storage subsystem!");
    }
142
    foreach my $cmd (@cmds) {
143
	if (!process($so, $cmd, 1)) {
144
	    fatal("Could not process storage commands!");
145 146 147
	}
    }

148 149 150
    #
    # Save config
    #
151 152 153 154 155 156
    my $ret = eval { Storable::store(\@cmds, $OLDCONFIG); };
    if ($@) {
	fatal("$@");
    }
    if (!$ret) {
	fatal("Error stashing away storage config!");
157
    }
158 159 160 161 162 163 164 165 166 167 168 169 170 171 172

    #
    # Stash mapping of block stores to local names for the convenience
    # of the user.
    #
    if (open(MAP, ">$STORAGEMAP")) {
	foreach my $cmd (@cmds) {
	    if (exists($cmd->{'LNAME'})) {
		print MAP $cmd->{'VOLNAME'} . " /dev/" . $cmd->{'LNAME'} . "\n";
	    }
	}
	close(MAP);
    } else {
	warn("*** Could not create storage map: $STORAGEMAP\n");
    }
173 174 175 176 177 178 179
}

#
# Shutdown Action.
#
sub doshutdown()
{
180
    docleanup(0);
181 182 183 184
}

#
# Node Reconfig Action (without rebooting).
185
# XXX don't do anything til we figure out what would be correct.
186 187 188 189 190 191 192 193
#
sub doreconfig()
{
}

#
# Node cleanup action (node is reset to clean state, as if just allocated).
#
194
sub docleanup($)
195
{
196
    my ($doteardown) = @_;
197
    my $cmdref = [];
198 199

    if (-r "$OLDCONFIG") {
200 201 202 203 204 205 206 207
	$cmdref = eval { Storable::retrieve($OLDCONFIG); };
	if ($@) {
	    fatal("$@");
	}
	if (!$cmdref) {
	    fatal("Could not retrieve stashed storage config!");
	}

208 209 210 211
	if ($doteardown) {
	    unlink($OLDCONFIG);
	}
    }
212
    if (!@$cmdref) {
213 214 215 216
	return;
    }

    #
217
    # Process each command in turn.  Already sorted.
218
    #
219 220 221 222
    my $so = os_init_storage($cmdref);
    if (!$so) {
	fatal("Could not initialize storage subsystem!");
    }
223
    foreach my $cmd (@$cmdref) {
224
	if (!process($so, $cmd, 0, $doteardown)) {
225
	    fatal("Could not process storage commands!");
226 227
	}
    }
228 229

    unlink($STORAGEMAP);
230 231 232 233 234 235 236
}

#
# XXX Currently it isn't clear how much we can do here in a generic fashion.
# This may wind up just being a call to the os-specific setup. But for now
# we do further validation of params based on what we currently implement.
#
237
sub process($$$;$)
238
{
239
    my ($so,$href,$dosetup,$doteardown) = @_;
240 241
    my $class = $href->{'CLASS'};

242 243 244 245 246 247 248 249
    #
    # XXX get rid of any trailing slashes on the mountpoint so it
    # doesn't cause grief for the OS-dependent backend.
    #
    if (exists($href->{'MOUNTPOINT'})) {
	$href->{'MOUNTPOINT'} =~ s#/+$##;
    }

250 251 252 253 254 255 256 257 258 259
    if ($href->{'CMD'} eq "ELEMENT") {
	# look up the host name and convert to IP
	if (exists($href->{'HOSTID'})) {
	    my $hostip = gethostbyname($href->{'HOSTID'});
	    if (!defined($hostip)) {
		warn("*** Cannot resolve hostname '" . $href->{'HOSTID'} . "'\n");
		return 0;
	    }
	    $href->{'HOSTIP'} = inet_ntoa($hostip);
	}
260

261 262 263 264 265 266 267 268 269 270 271 272 273 274 275
	if ($class eq "SAN") {
	    if ($href->{'PROTO'} ne "iSCSI") {
		warn("*** SAN protocol '" .
		     $href->{'PROTO'} . "' not implemented\n");
		return 0;
	    }
	    if (!exists($href->{'HOSTID'})) {
		warn("*** No iSCSI target portal specified\n");
		return 0;
	    }
	    if ($href->{'UUID_TYPE'} ne "iqn" ||
		$href->{'UUID'} !~ /^$IQN_PREFIX/) {
		warn("*** Invalid iSCSI target name '".$href->{'UUID'}."'\n");
		return 0;
	    }
276

277 278 279 280 281 282 283 284 285
	}
	elsif ($class eq "local") {
	    if ($href->{'HOSTID'} ne "localhost" ||
		$href->{'UUID_TYPE'} ne "serial") {
		warn("*** Unexpected parameters for local storage\n");
		return 0;
	    }
	} else {
	    warn("*** Unknown storage element class '$class'\n");
286
	    return 0;
287
	}
288 289 290
    } elsif ($href->{'CMD'} eq "SLICE") {
	if ($class ne "local") {
	    warn("*** Unknown storage slice class '$class'\n");
291
	    return 0;
292
	}
293
	if ($href->{'BSID'} !~ /^(ANY|SYSVOL|NONSYSVOL)$/) {
294
	    warn("*** Unknown storage slice bsid '".$href->{'BSID'}."'\n");
295
	    return 0;
296 297
	}
    } else {
298
	warn("*** Unrecognized storage command '".$href->{'CMD'}."'\n");
299
	return 0;
300 301
    }

302
    my $exists = os_check_storage($so, $href);
303 304 305 306 307 308 309 310 311 312 313 314 315 316 317

    #
    # Infrastructure failure or storage unit was partially configured.
    # Be conservative for both setup and teardown: just punt.
    #
    if ($exists < 0) {
	warn("*** Storage device '" . $href->{'VOLNAME'} .
	     "' incorrectly configured, doing nothing\n");
	return 0;
    }

    #
    # Storage unit is already properly configured.
    # If setting up, we are done. Otherwise, tear it down.
    #
318
    if ($exists > 0) {
319
	if ($dosetup) {
320 321 322 323 324 325 326 327 328 329 330 331 332 333 334
	    if ($href->{'CMD'} eq "ELEMENT") {
		my $msg;

		if ($href->{'CLASS'} eq "SAN" && $href->{'PROTO'} eq "iSCSI") {
		    $msg = "iSCSI node";
		} elsif ($href->{'CLASS'} eq "local") {
		    $msg = "local disk";
		}
		print "  " . $href->{'VOLNAME'} . ": $msg at /dev/" .
		    $href->{'LNAME'};
	    } elsif ($href->{'CMD'} eq "SLICE") {
		if ($href->{'CLASS'} eq "local") {
		    print "  " . $href->{'VOLNAME'} . ": /dev/" .
			$href->{'LNAME'};
		}
335
	    }
336 337 338 339
	    if ($href->{'MOUNTPOINT'}) {
		print " mounted on " . $href->{'MOUNTPOINT'};
	    }
	    print "\n";
340
	} else {
341
	    print "  Deconfiguring '" . $href->{'VOLNAME'} . "'...\n";
342
	    if (!os_remove_storage($so, $href, $doteardown)) {
343 344 345 346
		warn("*** Could not remove storage device '" .
		     $href->{'VOLNAME'} . "'\n");
		return 0;
	    }
347
	    if ($href->{'CLASS'} eq "SAN" && $href->{'PROTO'} eq "iSCSI") {
348 349 350
		print "  " . $href->{'VOLNAME'} .
		    ": iSCSI node detached from /dev/" .
		    $href->{'LNAME'} . "\n";
351 352 353 354 355 356 357 358 359 360
	    } elsif ($href->{'CMD'} eq "SLICE") {
		print "  " . $href->{'VOLNAME'} . ": ";
		if ($href->{'MOUNTPOINT'}) {
		    print "unmounted " . $href->{'MOUNTPOINT'} .
			($doteardown ? " and " : " ");
		}
		if ($doteardown) {
		    print "destroyed /dev/" . $href->{'LNAME'};
		}
		print "\n";
361
	    }
362 363 364 365
	}
	return 1;
    }

366 367 368 369 370
    #
    # Storage unit not setup.
    # If setting up, do it. Otherwise there is nothing to do.
    #
    if ($dosetup) {
371
	print "  Configuring '" . $href->{'VOLNAME'} . "'...\n";
372
	if (!os_create_storage($so, $href)) {
373 374 375 376
	    warn("*** Could not create storage device '" .
		 $href->{'VOLNAME'} . "'\n");
	    return 0;
	}
377

378 379 380 381 382 383 384 385 386 387 388 389 390 391 392
	if ($href->{'CMD'} eq "ELEMENT") {
	    if ($href->{'CLASS'} eq "SAN" && $href->{'PROTO'} eq "iSCSI") {
		print "  " . $href->{'VOLNAME'} .
		    ": iSCSI node attached as /dev/" .
		    $href->{'LNAME'};
	    }
	}
	elsif ($href->{'CMD'} eq "SLICE") {
	    if ($href->{'CLASS'} eq "local") {
		print "  " . $href->{'VOLNAME'} . ": /dev/" .
		    $href->{'LNAME'};
	    }
	}
	if ($href->{'MOUNTPOINT'}) {
	    print " mounted on " . $href->{'MOUNTPOINT'};
393
	}
394
	print "\n";
395 396 397
    } else {
	print "  " . $href->{'VOLNAME'} . ": not configured\n";
    }
398 399
    return 1;
}