diff --git a/configure b/configure index 0ffd55bf62ed2e2c098f4e59183a19e08b91e8dd..4c0c3e40415ae3a061f28e812774f7dc076291b4 100755 --- a/configure +++ b/configure @@ -1404,7 +1404,7 @@ outfiles="$outfiles Makeconf GNUmakefile \ utils/GNUmakefile utils/vlandiff utils/vlansync utils/delay_config \ utils/sshtb utils/create_image utils/node_admin utils/webcreateimage \ utils/firstuser utils/export_tables utils/eventping \ - utils/cvsupd.pl \ + utils/cvsupd.pl utils/newnode \ www/GNUmakefile www/defs.php3 www/dbdefs.php3 \ vis/GNUmakefile vis/webvistopology \ vis/dbvistopology \ diff --git a/configure.in b/configure.in index 42e632fbf72bdaacdec6b906c7289cdbe59bbfd3..1c4aecdf1a6e885955356cef675b0d12f30e9575 100755 --- a/configure.in +++ b/configure.in @@ -447,7 +447,7 @@ outfiles="$outfiles Makeconf GNUmakefile \ utils/GNUmakefile utils/vlandiff utils/vlansync utils/delay_config \ utils/sshtb utils/create_image utils/node_admin utils/webcreateimage \ utils/firstuser utils/export_tables utils/eventping \ - utils/cvsupd.pl \ + utils/cvsupd.pl utils/newnode \ www/GNUmakefile www/defs.php3 www/dbdefs.php3 \ vis/GNUmakefile vis/webvistopology \ vis/dbvistopology \ diff --git a/utils/newnode.in b/utils/newnode.in new file mode 100644 index 0000000000000000000000000000000000000000..f392b0fd6af03d0594f6461f89990847f29767eb --- /dev/null +++ b/utils/newnode.in @@ -0,0 +1,242 @@ +#!/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. +# + +use lib '/usr/testbed/devel/ricci/lib'; + +use libdb; + +use strict; + +my $TB = "/usr/testbed/devel/ricci"; + +my $switchmac = "$TB/sbin/switchmac"; +my $sched_reload = "$TB/sbin/sched_reload"; +my $nalloc = "$TB/bin/nalloc"; +my $nfree = "$TB/bin/nfree"; +my $dhcpd_makeconf = "$TB/sbin/dhcpd_makeconf"; + +my $dhcpd_conf = "/usr/local/etc/dhpcd.conf"; +my $dhcpd_template = "/usr/local/etc/dhcpd.conf.template"; +my $dhcpd_rc = "/usr/local/etc/rc.d/2.dhcpd.sh"; + +if (!TBAdmin()) { + die "Sorry, only testbed administrators can run this script!\n"; +} + +if (@ARGV < 1) { + die "Usage: $0 <node_id> ...\n"; +} + +my @node_ids = @ARGV; + +# +# Start this party by getting MAC addresses from the switches +# +print "Getting MAC addresses from the switches (this could take a while!)\n"; +open(MAC,"$switchmac |") or die "Unable to fork: $!\n"; +my %wires; +while (my $line = pop @switchmac) { + chomp $line; + my ($MAC,$switchport,$vlan,$iface) = split /,/, $line; + if ($switchport !~ /^([\w-]+)\/(\d+)\.(\d+)$/) { + die "Bad line from $switchmac: $line\n"; + } + my ($switch, $card, $port) = ($1,$2,$3); + $wires{$MAC} = [$switch, $card, $port]; + print "filling wires{$MAC}\n"; +} + +# +# Now, loop through the nodes given, and add each one +# +my @succeeded_nodes; +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 + # + $query_result = DBQueryFatal("SELECT type, IP FROM new_nodes " . + "WHERE node_id='$node_id'"); + if (!$query_result->num_rows()) { + warn "Node $node_id failed: No pending node with that name exists!\n"; + next NODE; + } + my ($type, $IP) = $query_result->fetchrow(); + + # + # 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 + # + $query_result = DBQueryFatal("SELECT iface, MAC, interface_type " . + "FROM new_interfaces WHERE node_id='$node_id'"); + if (!$query_result->num_rows()) { + warn "Node $node_id failed: Must have at least one interface!\n"; + next NODE; + } + + my %interfaces; + while (my ($iface, $MAC, $iface_type) = $query_result->fetchrow()) { + # + # 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()) { + warn "Node $node_id failed: Interface $iface is of unknown type " . + "$iface_type"; + next NODE; + } + + my ($max_speed, $full_duplex) = $iface_query->fetchrow(); + + # + # Stash it away... + # + $interfaces{$iface} = [$MAC, $iface_type, $max_speed, $full_duplex]; + + # + # Check to see if we have wires for all of the interfaces - we can ignore + # the control net, it's OK if we don't have that one. + # + if ($iface eq "eth$control_net") { + next NODE; + } + + if (!$wires{$MAC}) { + print "Node $node_id failed: Could not find switch port for ". + "$MAC ($iface)\n"; + next NODE; + } + } + + # + # Make up a priority (just used for sorting) + # + $node_id =~ /(\d+)$/; + my $priority; + if ($1) { + $priority = $1; + } else { + $priority = 1; + } + + # + # Okay, time to actually add the node! + # + + DBQueryFatal("INSERT INTO nodes SET node_id='$node_id', type='$type', " . + "phys_nodeid='$node_id', role='testnode', priority=$priority"); + + while (my ($iface, $aref) = each %interfaces) { + my ($MAC, $iface_type, $speed, $duplex) = @$aref; + $iface =~ /(\d+)$/; + my $card = $1; + 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"); + + if ($card != $control_net) { + my ($switch, $card2, $port2) = @{$wires{$MAC}}; + DBQueryFatal("INSERT INTO wires SET type='$wire_type', " . + "node_id1='$node_id', card1=$card, port1=1, node_id2='$switch', " . + "card2=$card2, port2=$port2"); + } + } + + # + # TODO - Do we need to get the nodes into a good state for the event system? + # + + # + # Put the node into hwdown and schedule a reload for it + # + system "$nalloc emulab-ops hwdown $node_id"; + system "$sched_reload $node_id"; + + # + # Remove the node from the new_ tables + # + DBQueryFatal("DELETE FROM new_nodes WHERE node_id='$node_id"); + DBQueryFatal("DELETE FROM new_interfaces WHERE node_id='$node_id'"); + + print "$node_id succesfully added!"; + + push @succeeded_nodes, $node_id; +} + +# +# 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 +# +if (! -f $dhcpd_template) { + warn "Warning: $dhcpd_template does not exist\n"; + warn "You'll need to re-run dhcpd_makeconf manually, then free the new\n"; + warn "nodes from emulab-ops/hwdown\n"; +} else { + 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\n"; + system "$dhcpd_rc stop"; + system "$dhcpd_rc start"; + + # + # Now, free all the nodes we just made from hwdown, so that they can reload + # + system "$nfree emulab-ops hwdown " . join(" ",@succeeded_nodes); + +} + +# +# TODO - add nodes to named? +#