deletenode.in 7.73 KB
Newer Older
1 2
#!/usr/bin/perl -w
#
3
# Copyright (c) 2004-2018 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
# deletenode - a script for deleting a node from the database
#
26 27

#
28
# Configure variables
29
#
30 31
my $TB         = "@prefix@";
my $ELABINELAB = @ELABINELAB@;
32
my $MAINSITE   = @TBMAINSITE@;
33
my $KILL       = "/bin/kill";
34 35 36 37

use lib '@prefix@/lib';
use libdb;

38 39
use Getopt::Std;

40 41 42 43 44 45 46 47
# Turn off line buffering on output
$| = 1;

#
# A list of all the tables we have to delete this node from, and the name of
# the column(s) in that table that contains a node name
#
my %clean_tables = TBDB_PHYSICAL_NODE_TABLES;
48
my %history_tables = TBDB_PHYSICAL_NODE_HISTORY_TABLES;
49 50 51 52 53

if (!TBAdmin()) {
    die "Sorry, only testbed administrators can run this script!\n";
}

54 55 56 57
#
# Handle command-line arguments
#
sub usage {
58
    print STDERR "Usage: $0 [-f | -F] [-q] [-n] [-s] [-S] [-H] <node>\n";
59 60
    print STDERR "Options:\n";
    print STDERR " -n       - Dry run mode\n";
61
    print STDERR " -v       - Be more chatty\n";
62
    print STDERR " -S       - Save off DB state before deletion\n";
63
    print STDERR " -s       - Only perform DB deletions; do not restart daemons\n";
64 65
    print STDERR " -f       - Force mode; deletenode even if not in hwdown\n";
    print STDERR " -F       - FORCE MODE; deletenode even if not in hwdown\n";
66
    print STDERR " -H       - Remove node history entries as well\n";
67
    exit(1);
68 69 70
}

my %options = ();
71
if (!getopts("bFfHvnSs",\%options)) {
72
    usage();
73
}
74 75 76
usage()
    if (!@ARGV);
my $node = shift(@ARGV);
77 78 79 80 81 82 83 84 85 86 87 88

#
# Start off with some serious sanity checks
#

#
# First, make sure the node exists, and only let them delete experimental nodes
#
my $result = DBQueryFatal("select role from nodes where node_id='$node'");
if ($result->num_rows() != 1) {
    die "Node $node does not exist!\n";
}
89
my ($role) = $result->fetchrow_array();
90 91 92
if ($role ne "testnode" &&
    ! (defined($options{"f"}) || defined($options{"F"}))) {
    die "Node $node is not a testnode! Use the -f option.\n";
93 94 95
}

#
96 97
# Don't let 'em delete a node that is allocated, except to hwdown. Override
# with force option though (for ElabInElab).
98 99 100 101
#
my ($pid, $eid);
my $allocated = NodeidToExp($node,\$pid,\$eid);
if ($allocated && (($pid ne NODEDEAD_PID) || ($eid ne NODEDEAD_EID))) {
102
    print "Node is not free or in the " .
Mike Hibler's avatar
Mike Hibler committed
103
	NODEDEAD_PID . "/" . NODEDEAD_EID . " experiment!\n";
104 105
    if (defined($options{"F"}) ||
	(defined($options{"f"}) && ($ELABINELAB || $MAINSITE))) {
106 107 108 109 110
	print "WARNING: Continuing anyway!\n";
    }
    else {
	exit(-1);
    }
111 112
}

113 114 115 116 117 118 119 120
#
# Find any subnodes of the node and delete them as well (if that is really,
# really what they want to do).
#
my @subnodes;
$result = DBQueryFatal("select node_id from nodes where ".
		       "node_id!=phys_nodeid and phys_nodeid='$node' ".
		       "and role='testnode'");
121
while (my ($subnode) = $result->fetchrow_array()) {
122 123 124 125 126 127 128 129 130 131 132 133 134 135
    push(@subnodes, $subnode);
}
if (@subnodes > 0) {
    unless ($options{b}) {
	print "*** WARNING: $node has subnodes: ", join(", ", @subnodes), "\n";
	print "This procedure will remove those as well, continue? ";
	my $answer = <>;
	if ($answer !~ /^y/i) {
	    print "Okay, aborting...\n";
	    exit(1);
	}
    }
}

136 137 138
#
# Make sure they know what they are getting themselves into
#
139 140 141 142 143 144 145 146 147 148 149 150
# Note: the -b (batch) option is intentionally undocumented, it should only be
# used from other scripts that have already asked for confirmation
#
unless ($options{b}) {
    print "*** WARNING:\n";
    print "This will erase all evidence that $node ever existed in the testbed!\n";
    print "Are you SURE you want to continue? ";
    my $answer = <>;
    if ($answer !~ /^y/i) {
	print "Okay, aborting...\n";
	exit(1);
    }
151 152
}

153 154
push(@subnodes, $node);

155
#
156
# Save off DB info we are about to delete
157
#
158 159 160 161 162 163 164 165
if ($options{"S"}) {
    foreach $node (@subnodes) {
	my $savefile = "/var/tmp/$node-delete.log";
	print "Saving table data for $node to $savefile\n";
	if (-e "$savefile") {
	    print STDERR "*** Savefile already exists, aborting\n";
	    exit(1);
	}
166
	
167
	my $dumpopts = "--compact --no-create-info --add-locks";
168
	$dumpopts .= " --set-gtid-purged=OFF";
169 170 171 172 173 174 175 176
	while (my ($table, $clist) = each %clean_tables) {
	    foreach my $column (@$clist) {
		if (system("mysqldump $dumpopts -w \"$column='$node'\" tbdb $table >> $savefile")) {
		    print STDERR "*** Failed to save $table info for $node, aborting\n";
		    exit(1);
		}
	    }
	}
177 178 179 180 181 182 183 184 185
	# We always save the history tables content regardless of -H
	while (my ($table, $clist) = each %history_tables) {
	    foreach my $column (@$clist) {
		if (system("mysqldump $dumpopts -w \"$column='$node'\" tbdb $table >> $savefile")) {
		    print STDERR "*** Failed to save $table info for $node, aborting\n";
		    exit(1);
		}
	    }
	}
186 187 188 189 190 191 192 193 194 195 196
    }
}

foreach $node (@subnodes) {
    my $pre = $options{"n"} ? "NOT " : "";
    print "${pre}Removing $node...\n";

    #
    # Okay, let's clean out them tables
    #
    while (my ($table, $clist) = each %clean_tables) {
197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213
	#
	# Handle some special cases:
	# - some blockstore state is indexed by just bsidx
	#
	if ($table eq "blockstores") {
	    print "${pre}Removing from table blockstore_attributes, column bsidx\n"
		if (defined($options{"v"}));
	    DBQueryFatal("DELETE FROM blockstore_attributes WHERE bsidx IN ".
		"(SELECT bsidx FROM blockstores WHERE node_id='$node')")
		if (!defined($options{"n"}));

	    print "${pre}Removing from table blockstore_trees, column bsidx\n"
		if (defined($options{"v"}));
	    DBQueryFatal("DELETE FROM blockstore_trees WHERE bsidx IN ".
		"(SELECT bsidx FROM blockstores WHERE node_id='$node')")
		if (!defined($options{"n"}));
	}
214 215
	foreach my $column (@$clist) {
	    print "${pre}Removing from table $table, column $column\n"
216
		if (defined($options{"v"}));
217 218 219
	    DBQueryFatal("DELETE FROM $table WHERE $column='$node';")
		if (!defined($options{"n"}));
	}
220
    }
221 222 223 224 225 226 227 228 229 230 231 232 233 234

    #
    # Clear out history related tables if requested.
    #
    if ($options{"H"}) {
	while (my ($table, $clist) = each %history_tables) {
	    foreach my $column (@$clist) {
		print "${pre}Removing from table $table, column $column\n"
		    if (defined($options{"v"}));
		DBQueryFatal("DELETE FROM $table WHERE $column='$node';")
		    if (!defined($options{"n"}));
	    }
	}
    }
235 236
}

237 238 239
#
# Need to run a bunch of stuff to really kill off the node.
#
240
if (! (defined($options{"n"}) || defined($options{"s"}))) {
241
    print "Regenerating exports file and restarting daemon.\n"
242
	if (defined($options{"v"}));
243 244 245 246
    if (system("$TB/sbin/exports_setup")) {
	print STDERR "*** Failed to reset mountpoints.\n";
    }
    print "Regenerating named maps and restarting daemon.\n"
247
	if (defined($options{"v"}));
248 249 250 251
    if (system("$TB/sbin/named_setup")) {
	print STDERR "*** Failed to reset named maps.\n";
    }
    print "Regenerating DHCPD config file and restarting daemon.\n"
252
	if (defined($options{"v"}));
253 254 255
    if (system("$TB/sbin/dhcpd_makeconf -i -r")) {
	print STDERR "*** Failed to reset DHCPD config file.\n";
    }
256 257 258 259 260 261
    if (-e "/var/run/stated.pid") {
	print "Hupping stated so that it will reload its tables\n"
	    if (defined($options{"v"}));
	if (system("sudo $KILL -HUP `cat /var/run/stated.pid`")) {
	    print STDERR "*** Failed to HUP stated.\n";
	}
262
    }
263
}
264
exit 0;