newnode.in 8.74 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 16 17 18

use libdb;

use strict;

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

my $switchmac = "$TB/sbin/switchmac";
22 23 24 25
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";
26 27 28
my $nalloc = "$TB/bin/nalloc";
my $nfree = "$TB/bin/nfree";
my $dhcpd_makeconf = "$TB/sbin/dhcpd_makeconf";
29
my $exports_setup = "$TB/sbin/exports_setup";
30

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

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

#
# Initial event system state to put the nodes into
#
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
my $INITIAL_STATE = TBDB_NODESTATE_SHUTDOWN;
my $INITIAL_OPMODE = TBDB_NODEOPMODE_RELOAD;

#
# MFS to boot the nodes into initially
#
my $INITIAL_MFS = "PXEFBSD";

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

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

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

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

64 65 66
#
# The user has to be able to run sudo, so they can restart dhcpd.
#
67
if (system "$sudo /bin/pwd < /dev/null") {
68 69 70
    die "You must be able to sudo to root to use this script\n";
}

71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
#
# 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"
}

92 93 94 95 96
my @node_ids = @ARGV;

#
# Now, loop through the nodes given, and add each one
#
97
my (@succeeded_nodes, @succeeded_IPs);
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
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
    #
114
    $query_result = DBQueryFatal("SELECT new_node_id, type, IP, temporary_IP " .
115
	"FROM new_nodes WHERE node_id='$node_id'");
116 117 118 119
    if (!$query_result->num_rows()) {
	warn "Node $node_id failed: No pending node with that name exists!\n";
	next NODE;
    }
120
    my ($new_node_id, $type, $IP, $tempIP) = $query_result->fetchrow();
121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136

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

    #
    # Grab the node's MACs from the new_interfaces table
    #
137
    $query_result = DBQueryFatal("SELECT card, MAC, interface_type, " .
138
	"switch_id, switch_card, switch_port " .
139
	"FROM new_interfaces WHERE new_node_id='$new_node_id'");
140 141 142 143 144 145
    if (!$query_result->num_rows()) {
	warn "Node $node_id failed: Must have at least one interface!\n";
	next NODE;
    }

    my %interfaces;
146
    while (my ($card, $MAC, $iface_type, $switch_id, $switch_card,
147
	    $switch_port) = $query_result->fetchrow()) {
148 149 150 151 152 153
	#
	# 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()) {
154
	    warn "Node $node_id failed: Interface $card is of unknown type " .
155 156 157 158 159 160 161 162 163
	    	"$iface_type";
	    next NODE;
	}

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

	#
	# Stash it away...
	#
164
	$interfaces{$card} = [$MAC, $iface_type, $max_speed, $full_duplex,
165
		$switch_id, $switch_card, $switch_port];
166 167 168 169 170 171

    }

    #
    # Make up a priority (just used for sorting)
    #
172 173
    $node_id =~ /^(.*\D)(\d+)$/;
    my ($prefix,$nodenum) = ($1, $2);
174
    my $priority;
175 176
    if (defined $nodenum) {
	$priority = $nodenum;
177 178 179 180 181 182 183 184 185
    } else {
	$priority = 1;
    }

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

189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214
    #
    # 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'");
	}
    }

215
    while (my ($card, $aref) = each %interfaces) {
216 217
	my ($MAC, $iface_type, $speed, $duplex, $switch_id, $switch_card,
	    $switch_port) = @$aref;
218
	my $iface = "eth$card";
219 220 221 222 223 224 225 226 227 228 229
	my $iface_IP = "";
	my $wire_type = "Node";
	if ($card == $control_net) {
	    $iface_IP = $IP;
	    $wire_type = "Control";
	}
	DBQueryFatal("INSERT INTO interfaces SET node_id='$node_id', " .
	    "card=$card, port=1, mac='$MAC', IP='$iface_IP', " .
	    "interface_type='$iface_type', iface='$iface', " .
	    "current_speed='$speed', duplex=$duplex");

230 231 232 233
	if (!$switch_id) {
	    print "No switch found for ${node_id}:$iface - skipping\n";
	    next;
	}
234 235 236 237
	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'");
238 239 240
    }

    #
241 242
    # 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
243 244 245 246 247 248
    #
    system "$nalloc emulab-ops hwdown $node_id";

    #
    # Remove the node from the new_ tables
    #
249
    DBQueryFatal("DELETE FROM new_nodes WHERE new_node_id=$new_node_id");
250
    DBQueryFatal("DELETE FROM new_interfaces WHERE new_node_id=$new_node_id");
251

252
    print "$node_id succesfully added!\n";
253 254

    push @succeeded_nodes, $node_id;
255
    push @succeeded_IPs, $tempIP;
256 257 258 259 260 261 262 263 264 265 266 267
}

#
# 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
#
268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287
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";
}
288

289 290 291 292 293
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";
}
294

295 296 297 298 299 300 301 302 303
#
# 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";
}
304

305 306 307 308 309 310 311 312 313 314 315
#
# 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";
my $select_rv = system "$os_select -m $INITIAL_MFS " .
    join(" ",@succeeded_nodes);
if ($select_rv) {
    warn "WARNING - failed to select FreeBSD MFS for nodes";
}
316

317 318 319 320 321 322 323
#
# Start rebooting nodes
#
print "Rebooting nodes...\n";
foreach my $IP (@succeeded_IPs) {
    print "Rebooting $IP\n";
    system "$newnode_reboot $IP\n";
324 325
}

326 327 328 329 330
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";

331
#
332 333 334
# TODO -
#    disable interfaces
#    console setup
335
#