nfsmfs_setup.proxy.in 5.33 KB
Newer Older
1 2 3
#!/usr/bin/perl -wT

#
4
# Copyright (c) 2000-2017 University of Utah and the Flux Group.
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
# 
# {{{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 Errno;
use Fcntl ':flock';
use Getopt::Std;

#
# Create a per-node NFS filesystem to act as the admin MFS.
# Currently we can only do this on ZFS by cloning a filesystem snapshot.
#
# usage: nfsmfs_setup [-Ddn] node_id ...
#

my %opts = ();
getopts('Ddfn', \%opts);
my $destroy = 0;
my $debug = 0;
my $doit = 1;
my $force = 0;

#
# Configure variables
#
my $TBOPS       = "@TBOPSEMAIL@";
my $ZFSROOT	= "@ZFS_ROOT@";
50
my $ZFSNOEXPORT	= "@ZFS_NOEXPORT@";
51

52
my $MOUNTPOINT  = "@NFSMFS_ROOT@";
53
my $ZFS         = "/sbin/zfs";
54
my $MOUNT	= "/sbin/mount";
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92

# XXX this should be constructed per node type based on info passed in
my $GOLDEN      = "$ZFSROOT$MOUNTPOINT/m400\@current";

my $etcdir;
my $exports;
my $exportsnew;
my $exportsback;
my $exportshead;
my $exportstail;
my $pidfile;
my $daemon;

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

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

#
# Testbed Support libraries
# 
use lib "@prefix@/lib";
use libtestbed;

#
# Can only be done by root. We don't want users seting up or destroying
# these filesystems.
#
if ($UID != 0) {
    die("Must be root!");
}

if (! -x "$ZFS") {
    fatal("Can only be used with ZFS right now!");
}

93 94 95 96
if ($MOUNTPOINT eq "") {
    fatal("Must set NFSMFS_ROOT in defs file!");
}

97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
if (defined($opts{'D'})) {
    $destroy = 1;
}
if (defined($opts{'d'})) {
    $debug = 1;
}
if (defined($opts{'f'})) {
    $force = 1;
}
if (defined($opts{'n'})) {
    $doit = 0;
}
if (@ARGV < 1) {
    print STDERR "usage: nfsmfs_setup [-D] node_id ...\n";
    exit(1);
}

#
# Make sure the golden filesystem exists
#
117
if (!$destroy && system("$ZFS list -H $GOLDEN >/dev/null 2>&1")) {
118 119 120 121 122 123
    fatal("ZFS snapshot '$GOLDEN' does not exist");
}

#
# Get a list of existing ZFS-based mount points.
#
124 125 126 127 128 129
# XXX "zfs list" can be super-slow (i.e., 10s of seconds) when you have
# 1000+ filesystems. So we use "mount" instead. This makes a big difference
# when we are called from nfree to destroy the MFS for all nodes in an
# experiment that would use the NFS MFS, even if they not are actually in
# the MFS at the time.
#
130
my %mfs = ();
131
my @mounts = `$MOUNT | fgrep ' on $MOUNTPOINT'`;
132
if ($?) {
133
    fatal("$MOUNTPOINT does not exist!?");
134 135
}
foreach my $line (@mounts) {
136 137
    if ($line =~ /^(\S+) on $MOUNTPOINT\/(\S+)/) {
	$mfs{$2} = $1;
138 139 140 141 142 143 144
    }
}

#
# For each node, see if we need to do something and do it!
#
my @failed = ();
145
my $changed = 0;
146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168
foreach my $nodeid (@ARGV) {
    my $cmd;

    #
    # Untaint.
    #
    if ($nodeid =~ /^([-\w]+)$/) {
	$nodeid = $1;
    } else {
	next;
    }

    #
    # First, do cleanup.
    # Get rid of any old version of the MFS for this node.
    #
    my $onodeid = "$nodeid-DEAD";
    if (exists($mfs{$onodeid})) {
	$cmd = "$ZFS destroy $ZFSROOT$MOUNTPOINT/$onodeid";
	if (mysystem($cmd)) {
	    print STDERR "$nodeid: WARNING: could not remove old FS\n";
	}
	delete $mfs{$onodeid};
169
	$changed++;
170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189
    }

    if ($destroy) {
	if (!exists($mfs{$nodeid})) {
	    next;
	}

	#
	# If forcing, really destroy it. Otherwise just rename it
	# and we will get rid of it next time through here.
	#
	if ($force) {
	    $cmd = "$ZFS destroy $ZFSROOT$MOUNTPOINT/$nodeid";
	} else {
	    $cmd = "$ZFS rename $ZFSROOT$MOUNTPOINT/$nodeid".
		" $ZFSROOT$MOUNTPOINT/$onodeid";
	}
	if (mysystem($cmd)) {
	    push(@failed, $nodeid);
	}
190
	$changed++;
191 192 193 194 195 196 197 198 199
    } else {
	# If forcing, first get rid of current MFS
	if ($force && exists($mfs{$nodeid})) {
	    $cmd = "$ZFS destroy $ZFSROOT$MOUNTPOINT/$nodeid";
	    if (mysystem($cmd)) {
		push(@failed, $nodeid);
		next;
	    }
	    delete $mfs{$nodeid};
200
	    $changed++;
201 202 203 204
	}

	# Create the MFS
	if (!exists($mfs{$nodeid})) {
205 206 207 208
	    # For ZFS_NOEXPORT, we rely on exports_setup to expose the FS
	    my $opt = $ZFSNOEXPORT ?
		"" : "-o sharenfs='$nodeid -maproot=root'";
	    $cmd = "$ZFS clone $opt $GOLDEN $ZFSROOT$MOUNTPOINT/$nodeid";
209 210 211
	    if (mysystem($cmd)) {
		push(@failed, $nodeid);
	    }
212
	    $changed++;
213 214 215 216
	}
    }
}

217
#
218
# Odd...sometimes the (un)export doesn't happen.
219 220 221 222
#
# XXX again, this can be a brutally expensive command, so only do it if
# we have to.
#
223
if (!$ZFSNOEXPORT && $changed) {
224 225
    mysystem("$ZFS share -a");
}
226

227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252
if (@failed > 0) {
    my $action = $destroy ? "destroy" : "create";
    fatal("$action failed on: " . join(' ', @failed));
}

exit(0);

sub mysystem {
    my $cmd = shift;

    if (!$doit) {
	print STDERR "Would do: $cmd\n";
	return 0;
    }

    return system($cmd);
}

sub fatal {
    local($msg) = $_[0];

    SENDMAIL($TBOPS, "NFS MFS setup: ", $msg)
	if (!$debug);
    die($msg);
}