newnode.in 9 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12
#!/usr/bin/perl -w

#
# EMULAB-COPYRIGHT
# Copyright (c) 2003 University of Utah and the Flux Group.
# All rights reserved.
#

#
# newnode - a script for moving nodes from the new_ tables into production.
#

13
use lib '@prefix@/lib';
14 15

use libdb;
Robert Ricci's avatar
Robert Ricci committed
16
use English;
17 18 19

use strict;

20
my $TB = "@prefix@";
21 22

my $switchmac = "$TB/sbin/switchmac";
23 24 25 26
my $os_load = "$TB/bin/os_load";
my $os_select = "$TB/bin/os_select";
my $newnode_reboot = "$TB/sbin/newnode_reboot";
my $named_setup = "$TB/sbin/named_setup";
27 28 29
my $nalloc = "$TB/bin/nalloc";
my $nfree = "$TB/bin/nfree";
my $dhcpd_makeconf = "$TB/sbin/dhcpd_makeconf";
30
my $exports_setup = "$TB/sbin/exports_setup";
31

32
my $dhcpd_conf = "/usr/local/etc/dhcpd.conf";
33 34 35
my $dhcpd_template = "/usr/local/etc/dhcpd.conf.template";
my $dhcpd_rc = "/usr/local/etc/rc.d/2.dhcpd.sh";

36
my $sudo = "/usr/local/bin/sudo -S";
37 38 39 40

#
# Initial event system state to put the nodes into
#
41 42 43 44 45 46
my $INITIAL_STATE = TBDB_NODESTATE_SHUTDOWN;
my $INITIAL_OPMODE = TBDB_NODEOPMODE_RELOAD;

#
# MFS to boot the nodes into initially
#
47
my $INITIAL_MFS = TB_OSID_FREEBSD_MFS();
48 49 50 51 52 53 54 55

#
# Number of vnodes to create for each physical node
#
my $NUM_VNODES = 50;

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

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

61 62 63 64 65 66
#
# We seem to have to do this so that ssh gets proper root permissions to read
# the key file. Argh.
#
$UID = $EUID = 0;

67 68 69 70
if (@ARGV < 1) {
    die "Usage: $0 <node_id> ...\n";
}

71 72 73
#
# The user has to be able to run sudo, so they can restart dhcpd.
#
74
if (system "$sudo /bin/pwd < /dev/null") {
75 76 77
    die "You must be able to sudo to root to use this script\n";
}

78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
#
# Make sure that the dhcpd template file exists, and that the real version
# is writable
#
if (!-e $dhcpd_template) {
    die "In order to use this script, $dhcpd_template must exist!\n";
}
if (!-w $dhcpd_conf) {
    die "In order to use this script, you must be able to write $dhcpd_conf\n";
}

#
# For vnodes - figure out the jail IP base
#
my $IPBASE;
if (TBDB_JAILIPBASE =~ /^(\d+).(\d+).(\d+).(\d+)/) {
    $IPBASE = "$1.$2";
} else {
    die "Problem with JAILIPBASE\n"
}

99 100 101 102 103
my @node_ids = @ARGV;

#
# Now, loop through the nodes given, and add each one
#
104
my (@succeeded_nodes, @succeeded_IPs);
105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120
NODE: foreach my $node_id (@node_ids) {
    my $query_result;

    #
    # Check to make sure said node does not already exist!
    #
    $query_result = DBQueryFatal("SELECT node_id FROM nodes WHERE " .
	"node_id='$node_id'");
    if ($query_result->num_rows()) {
	warn "Node $node_id failed: a node with that name already exists!\n";
	next NODE;
    }

    #
    # Grab information about the node from the new_nodes table
    #
121
    $query_result = DBQueryFatal("SELECT new_node_id, type, IP, temporary_IP " .
122
	"FROM new_nodes WHERE node_id='$node_id'");
123 124 125 126
    if (!$query_result->num_rows()) {
	warn "Node $node_id failed: No pending node with that name exists!\n";
	next NODE;
    }
127
    my ($new_node_id, $type, $IP, $tempIP) = $query_result->fetchrow();
128 129 130 131 132

    #
    # Make sure that the new node is of a valid type, and grab a few other
    # things to fill in as initial values
    #
133
    $query_result = DBQueryFatal("SELECT control_iface FROM node_types " .
134 135 136 137 138
	"WHERE type='$type'");
    if (!$query_result->num_rows()) {
	warn "Node $node_id failed: Type $type does not exist!\n";
	next NODE;
    }
139
    my ($control_iface) = $query_result->fetchrow();
140 141 142 143

    #
    # Grab the node's MACs from the new_interfaces table
    #
144
    $query_result = DBQueryFatal("SELECT card, MAC, interface_type, " .
145
	"switch_id, switch_card, switch_port " .
146
	"FROM new_interfaces WHERE new_node_id='$new_node_id'");
147 148 149 150 151 152
    if (!$query_result->num_rows()) {
	warn "Node $node_id failed: Must have at least one interface!\n";
	next NODE;
    }

    my %interfaces;
153
    while (my ($card, $MAC, $iface_type, $switch_id, $switch_card,
154
	    $switch_port) = $query_result->fetchrow()) {
155 156 157 158 159 160
	#
	# Get some more information about this interface type
	#
	my $iface_query = DBQueryFatal("SELECT max_speed, full_duplex " .
	    "FROM interface_types WHERE type='$iface_type'");
	if (!$iface_query->num_rows()) {
161
	    warn "Node $node_id failed: Interface $card is of unknown type " .
162 163 164 165 166 167 168 169 170
	    	"$iface_type";
	    next NODE;
	}

	my ($max_speed, $full_duplex) = $iface_query->fetchrow();

	#
	# Stash it away...
	#
171
	$interfaces{$card} = [$MAC, $iface_type, $max_speed, $full_duplex,
172
		$switch_id, $switch_card, $switch_port];
173 174 175 176 177 178

    }

    #
    # Make up a priority (just used for sorting)
    #
179 180
    $node_id =~ /^(.*\D)(\d+)$/;
    my ($prefix,$nodenum) = ($1, $2);
181
    my $priority;
182 183
    if (defined $nodenum) {
	$priority = $nodenum;
184 185 186 187 188 189 190 191 192
    } else {
	$priority = 1;
    }

    #
    # Okay, time to actually add the node!
    #
    
    DBQueryFatal("INSERT INTO nodes SET node_id='$node_id', type='$type', " .
193
	"phys_nodeid='$node_id', role='testnode', priority=$priority, " .
194
    	"eventstate='$INITIAL_STATE', op_mode='$INITIAL_OPMODE'");
195

196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221
    #
    # Add some vnodes
    #
    if (defined $nodenum) {
	my $vtype = $type;
	if (!($vtype =~ s/pc/pcvm/)) {
	    $vtype = "$vtype-vm";
	}
	for (my $i = 1; $i <= $NUM_VNODES; $i++) {
	    my $vpriority = 10000000 + ($nodenum * 1000) + $i;
	    my $nodename = $node_id;
	    if (!($nodename =~ s/pc/pcvm/)) {
		$nodename = "$nodename-vm";
	    }
	    $nodename .= "-$i";
	    my $jailip = "${IPBASE}.${nodenum}.${i}";

	    DBQueryFatal("INSERT INTO nodes SET node_id='$nodename', " .
		"type='$vtype', phys_nodeid='$node_id', role='virtnode', " .
		"priority='$vpriority', op_mode='PCVM', " .
		"eventstate='SHUTDOWN', " .
		"def_boot_osid='emulab-ops-FBSD-JAIL', " .
		"update_accounts=1, jailflag=1, jailip='$jailip'");
	}
    }

222
    while (my ($card, $aref) = each %interfaces) {
223 224
	my ($MAC, $iface_type, $speed, $duplex, $switch_id, $switch_card,
	    $switch_port) = @$aref;
225
	my $iface = "eth$card";
226 227
	my $iface_IP = "";
	my $wire_type = "Node";
228
	my $iface_role = TBDB_IFACEROLE_EXPERIMENT();
229
	if ($iface eq $control_iface) {
230 231
	    $iface_IP = $IP;
	    $wire_type = "Control";
232
	    $iface_role = TBDB_IFACEROLE_CONTROL();
233 234 235 236
	}
	DBQueryFatal("INSERT INTO interfaces SET node_id='$node_id', " .
	    "card=$card, port=1, mac='$MAC', IP='$iface_IP', " .
	    "interface_type='$iface_type', iface='$iface', " .
237
	    "current_speed='$speed', duplex=$duplex, role='$iface_role'");
238

239 240 241 242
	if (!$switch_id) {
	    print "No switch found for ${node_id}:$iface - skipping\n";
	    next;
	}
243 244 245 246
	DBQueryFatal("INSERT INTO wires SET type='$wire_type', " .
	    "node_id1='$node_id', card1=$card, port1=1, " .
	    "node_id2='$switch_id', card2='$switch_card', " .
	    "port2='$switch_port'");
247 248 249
    }

    #
250 251
    # Put it into hwdown for now - I would put them in reloading, but I'm
    # afriad the reload_daemon might do the wrong thing to them
252 253 254 255 256 257
    #
    system "$nalloc emulab-ops hwdown $node_id";

    #
    # Remove the node from the new_ tables
    #
258
    DBQueryFatal("DELETE FROM new_nodes WHERE new_node_id=$new_node_id");
259
    DBQueryFatal("DELETE FROM new_interfaces WHERE new_node_id=$new_node_id");
260

261
    print "$node_id succesfully added!\n";
262 263

    push @succeeded_nodes, $node_id;
264
    push @succeeded_IPs, $tempIP;
265 266 267 268 269 270 271 272 273 274 275 276
}

#
# No point in restarting dhcpd, etc. if there are no nodes that succeeded
#
if (!@succeeded_nodes) {
    die "No nodes succeeded, exiting early\n";
}

#
# Re-generate dhcpd.conf
#
277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296
print "Re-generating dhcpd.conf\n";
open(CONF,"$dhcpd_makeconf $dhcpd_template|") or die "Unable to fork: $!\n";
my @conf = <CONF>;
close CONF or die "Error reading from dhcpd_makeconf: $!\n";

open(CONF,">$dhcpd_conf") or die "Unable to open $dhcpd_conf for writing\n";
print CONF @conf;
close CONF;

print "Restarting dhcpd: $sudo $dhcpd_rc stop\n";
my $sudo_rv = system "$sudo $dhcpd_rc stop";
if ($sudo_rv) {
    die "Error stopping dhcpd - return value was $sudo_rv\n";
}
sleep 2;
print "Restarting dhcpd: $sudo $dhcpd_rc start\n";
$sudo_rv = system "$sudo $dhcpd_rc start";
if ($sudo_rv) {
    die "Error starting dhcpd - return value was $sudo_rv\n";
}
297

298 299 300 301 302
print "Setting up nameserver\n";
my $named_rv = system "$named_setup";
if ($named_rv) {
    die "Error running named_setup - return value was $named_rv\n";
}
303

304 305 306 307 308 309 310 311 312
#
# Before we boot nodes into the MFS, we have to make sure they can mount
# NFS filesystems
#
print "Running exports_setup\n";
my $exports_rv = system "$exports_setup";
if ($exports_rv) {
    warn "WARNING - exports_setup returned $exports_rv";
}
313

314 315 316 317 318 319
#
# Tell the nodes to boot into the FreeBSD MFS, so that we have them in a state
# which we can control - this also puts them into a state that the event system
# likes better
#
print "Instructing nodes to boot into the FreeBSD MFS\n";
320
my $select_rv = system "$os_select $INITIAL_MFS " .
321 322 323 324
    join(" ",@succeeded_nodes);
if ($select_rv) {
    warn "WARNING - failed to select FreeBSD MFS for nodes";
}
325

326 327 328 329 330 331 332
#
# Start rebooting nodes
#
print "Rebooting nodes...\n";
foreach my $IP (@succeeded_IPs) {
    print "Rebooting $IP\n";
    system "$newnode_reboot $IP\n";
333 334
}

335 336 337 338 339
print "\n\n";
print "Finished - when you are satisifed that the nodes are working\n";
print "correctly, use nfree on boss to free them from the emulab-ops/hwdown\n";
print "experiment.\n";

340
#
341 342 343
# TODO -
#    disable interfaces
#    console setup
344
#