Commit 6ad7d688 authored by Robert Ricci's avatar Robert Ricci

Web interface for autoconfiguring nodes. This consisits of two pages,

one that lists and allows operations on a number of nodes at once, and
another that allows editing of an individual node.

Split much of the functionality of newnodecheckin.php into a new
library, so it can get re-called from newnodes_list.php3 later.

Move where we look for MACs on the switches - instead of doing in in
newnode, call it from newnodecheckin.php - this provides the admins
more feedback as to what's going on, and allows them to call it again
later or put the information in by hand.
parent 92a0956a
......@@ -38,23 +38,6 @@ if (@ARGV < 1) {
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;
my @lines = ('000000000000,intel1/1.17,1,unknown');
while (my $line = <MAC>) {
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];
}
#
# Now, loop through the nodes given, and add each one
#
......@@ -98,7 +81,8 @@ NODE: foreach my $node_id (@node_ids) {
#
# Grab the node's MACs from the new_interfaces table
#
$query_result = DBQueryFatal("SELECT iface, MAC, interface_type " .
$query_result = DBQueryFatal("SELECT iface, MAC, interface_type, " .
"switch_id, switch_card, switch_port " .
"FROM new_interfaces WHERE new_node_id='$new_node_id'");
if (!$query_result->num_rows()) {
warn "Node $node_id failed: Must have at least one interface!\n";
......@@ -106,7 +90,8 @@ NODE: foreach my $node_id (@node_ids) {
}
my %interfaces;
while (my ($iface, $MAC, $iface_type) = $query_result->fetchrow()) {
while (my ($iface, $MAC, $iface_type, $switch_id, $switch_card,
$switch_port) = $query_result->fetchrow()) {
#
# Get some more information about this interface type
#
......@@ -123,21 +108,9 @@ NODE: foreach my $node_id (@node_ids) {
#
# Stash it away...
#
$interfaces{$iface} = [$MAC, $iface_type, $max_speed, $full_duplex];
$interfaces{$iface} = [$MAC, $iface_type, $max_speed, $full_duplex,
$switch_id, $switch_card, $switch_port];
#
# 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;
}
}
#
......@@ -159,7 +132,8 @@ NODE: foreach my $node_id (@node_ids) {
"phys_nodeid='$node_id', role='testnode', priority=$priority");
while (my ($iface, $aref) = each %interfaces) {
my ($MAC, $iface_type, $speed, $duplex) = @$aref;
my ($MAC, $iface_type, $speed, $duplex, $switch_id, $switch_card,
$switch_port) = @$aref;
$iface =~ /(\d+)$/;
my $card = $1;
my $iface_IP = "";
......@@ -173,12 +147,10 @@ NODE: foreach my $node_id (@node_ids) {
"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");
}
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'");
}
#
......@@ -186,7 +158,7 @@ NODE: foreach my $node_id (@node_ids) {
#
#
# Put the node into hwdown and schedule a reload for it
# Schedule a reload for it
#
system "$nalloc emulab-ops hwdown $node_id";
system "$sched_reload $node_id";
......@@ -195,7 +167,7 @@ NODE: foreach my $node_id (@node_ids) {
# Remove the node from the new_ tables
#
DBQueryFatal("DELETE FROM new_nodes WHERE new_node_id=$new_node_id");
DBQueryFatal("DELETE FROM new_interfaces WHERE node_id=$new_node_id");
DBQueryFatal("DELETE FROM new_interfaces WHERE new_node_id=$new_node_id");
print "$node_id succesfully added!";
......@@ -238,5 +210,8 @@ if (! -f $dhcpd_template) {
}
#
# TODO - add nodes to named?
# TODO -
# add nodes to named?
# disable interfaces
# console setup
#
<?PHP
#
# EMULAB-COPYRIGHT
# Copyright (c) 2003 University of Utah and the Flux Group.
# All rights reserved.
#
#
# This is a simple library for use by the new-node pages, with functions that
# need to be called both by the initial checkin page and the adminstrator
# node-adding page.
#
function find_switch_macs($mac_list) {
global $uid, $gid, $TBSUEXEC_PATH;
#
# Avoid SIGPROF in child.
#
set_time_limit(0);
$macs = popen("$TBSUEXEC_PATH $uid $gid switchmac 2>&1","r");
#
# XXX - error checking
#
while (!feof($macs)) {
$line = fgets($macs);
$exploded = explode(",",$line);
$MAC = $exploded[0];
$switchport = $exploded[1];
$vlan = $exploded[2];
$iface = $exploded[3];
if (!preg_match("/^([\w-]+)\/(\d+)\.(\d+)$/",$switchport,$matches)) {
echo "<h3>Bad line from switchmac: $line\n";
return 0;
}
$switch = $matches[0];
$card = $matches[1];
$port = $matches[2];
if (in_array($MAC,$mac_list)) {
$mac_list[$MAC]["switch"] = $switch;
$mac_list[$MAC]["card"] = $card;
$mac_list[$MAC]["port"] = $port;
}
}
pclose($macs);
return 1;
}
function guess_node_type($proc,$disk) {
#
# Allow the reported speed to differ from the one in the database
#
$fudge_factor = .05;
#
# Convert disk size from megabytes to gigabtypes
#
$disk /= 1000.0;
#
# Go through node types and try to find a single one that matches
# this node's processor speed. This is a totally bogus way to do this,
# but it's the best we got for now.
#
$node_type = "";
$query_result = DBQueryFatal("select type, speed, HD from node_types " .
"where !isvirtnode and !isremotenode");
while ($row = mysql_fetch_array($query_result)) {
$speed = $row["speed"];
$type = $row["type"];
$HD = $row["HD"];
echo "Checking $speed vs $proc, $HD vs $disk\n";
if (($proc > ($speed * (1.0 - $fudge_factor))) &&
($proc < ($speed * (1.0 + $fudge_factor))) &&
($disk > ($HD * (1.0 - $fudge_factor))) &&
($disk < ($HD * (1.0 + $fudge_factor)))) {
if ($node_type != "") {
# We found two potential matches, choose neither
echo "Found a second match ($type), bailing\n";
return "";
} else {
echo "Found a first match ($type)\n";
$node_type = $type;
}
}
}
echo "Returning $node_type\n";
return $node_type;
}
function guess_IP ($prefix, $number) {
$hostname = $prefix . $number;
#
# First, let's see if they've already added to to DNS - the PHP
# gethostbyname has a really dumb way to return failure - it just
# gives you the hostname back.
#
$IP = gethostbyname($hostname);
if (strcmp($IP,$hostname)) {
return $IP;
}
#
# Okay, no such luck. We'll go backwards through the host list until
# we find the previous node with an established IP, then add our offset
# onto that
#
$i = $number - 1;
$IP = "";
while ($i > 0) {
$query_result = DBQueryFatal("select IP from interfaces as i " .
"left join nodes as n on i.node_id = n.node_id left join " .
"node_types as nt on n.type = nt.type " .
"where n.node_id='$prefix$i' and i.card = nt.control_net");
if (mysql_num_rows($query_result)) {
$row = mysql_fetch_array($query_result);
$IP = $row[IP];
break;
}
# Try new_nodes too
$query_result = DBQueryFatal("select IP from new_nodes " .
"where node_id='$prefix$i'");
if (mysql_num_rows($query_result)) {
$row = mysql_fetch_array($query_result);
$IP = $row[IP];
break;
}
$i--;
}
if ($i <= 0) {
return 0;
}
#
# Parse the IP we got into the last octet and everything else. Note that we
# don't really do the IP address calcuation correctly - just an
# approximation of the right thing.
#
list($oct1,$oct2,$oct3,$oct4) = explode(".",$IP);
$oct4 += $number - $i;
# We don't yet wrap - it may not be OK to do
if ($oct4 > 254) {
return 0;
}
return "$oct1.$oct2.$oct3.$oct4";
}
?>
<?PHP
#
# EMULAB-COPYRIGHT
# Copyright (c) 2003 University of Utah and the Flux Group.
# All rights reserved.
#
require("defs.php3");
#
# List the nodes that have checked in and are awaint being added the the real
# testbed
#
#
# Standard Testbed Header
#
PAGEHEADER("New Testbed Node");
#
# Only admins can see this page
#
$uid = GETLOGIN();
LOGGEDINORDIE($uid);
$isadmin = ISADMIN($uid);
if (! $isadmin) {
USERERROR("You do not have admin privileges!", 1);
}
if (!$id) {
USERERROR("Must specify a node ID!",1);
}
#
# If we had any update information passed to us, do the update now
#
if ($node_id) {
DBQueryFatal("UPDATE new_nodes SET node_id='$node_id', type='$type', " .
"IP='$IP' WHERE new_node_id='$id'");
}
#
# Same for interface update information
#
foreach ($HTTP_GET_VARS as $key => $value) {
if (preg_match("/iface(\w+)_mac/",$key,$matches)) {
$iface = $matches[1];
$mac = $HTTP_GET_VARS["iface${iface}_mac"];
$type = $HTTP_GET_VARS["iface${iface}_type"];
$switch_id = $HTTP_GET_VARS["iface${iface}_switch_id"];
$switch_card = $HTTP_GET_VARS["iface${iface}_switch_card"];
$switch_port = $HTTP_GET_VARS["iface${iface}_switch_port"];
DBQueryFatal("UPDATE new_interfaces SET mac='$mac', " .
"interface_type='$type', switch_id='$switch_id', " .
"switch_card='$switch_card', switch_port='$switch_port' " .
"WHERE new_node_id=$id AND iface='$iface'");
}
}
#
# Get the information about the node they asked for
#
$query_result = DBQueryFatal("SELECT new_node_id, node_id, type, IP, " .
"DATE_FORMAT(created,'%M %e %H:%i:%s') as created, dmesg " .
"FROM new_nodes WHERE new_node_id='$id'");
if (mysql_num_rows($query_result) != 1) {
USERERROR("Error getting information for node ID $id",1);
}
$row = mysql_fetch_array($query_result)
?>
<h4><a href="newnodes_list.php3">Back to the new node list</a></h4>
<form action="newnode_edit.php3" method="get">
<input type="hidden" name="id" value="<?=$id?>">
<h3 align="center">Node</h3>
<table align="center">
<tr>
<th>ID</th>
<td><?= $row['new_node_id'] ?></td>
</tr>
<tr>
<th>Node ID</th>
<td>
<input type="text" width=10 name="node_id" value="<?=$row['node_id']?>">
</td>
</tr>
<tr>
<th>Type</th>
<td>
<input type="text" width=10 name="type" value="<?=$row['type']?>">
</td>
</tr>
<tr>
<th>IP</th>
<td>
<input type="text" width=10 name="IP" value="<?=$row['IP']?>">
</td>
</tr>
<tr>
<th>Created</th>
<td><?= $row['created'] ?></td>
</tr>
<tr>
<th>dmesg Output</th>
<td><?= $row['dmesg'] ?></td>
</tr>
</table>
<h3 align="center">Interfaces</h3>
<table align="center">
<tr>
<th>Interface</th>
<th>MAC</th>
<th>Type</th>
<th>Switch</th>
<th>Card</th>
<th>Port</th>
</tr>
<?
$query_result = DBQueryFatal("SELECT iface, mac, interface_type, switch_id, " .
"switch_card, switch_port FROM new_interfaces where new_node_id=$id");
while ($row = mysql_fetch_array($query_result)) {
$iface = $row['iface'];
$mac = $row['mac'];
$type = $row['interface_type'];
$switch_id = $row['switch_id'];
$switch_card = $row['switch_card'];
$switch_port = $row['switch_port'];
echo "<tr>\n";
echo "<td>$iface</td>\n";
echo "<td><input type='text' name='iface${iface}_mac' size=12 " .
"value='$mac'></td>\n";
echo "<td><input type='text' name='iface${iface}_type' size=5 " .
"value='$type'></td>\n";
echo "<td><input type='text' name='iface${iface}_switch_id' size=10 " .
"value='$switch_id'></td>\n";
echo "<td><input type='text' name='iface${iface}_switch_card' size=3 " .
"value='$switch_card'></td>\n";
echo "<td><input type='text' name='iface${iface}_switch_port' size=3 " .
"value='$switch_port'></td>\n";
echo "</tr>\n";
}
?>
</table>
<br>
<center>
<input type="submit" name="submit" value="Update node">
</center>
<?
#
# Standard Testbed Footer
#
PAGEFOOTER();
?>
......@@ -5,6 +5,7 @@
# All rights reserved.
#
require("defs.php3");
require("newnode-defs.php3");
#
# Note - this script is not meant to be called by humans! It returns no useful
......@@ -18,6 +19,7 @@ require("defs.php3");
#
# Find all interfaces
#
$interfaces = array();
foreach ($HTTP_GET_VARS as $key => $value) {
if (preg_match("/iface(name|mac)(\d+)/",$key,$matches)) {
$vartype = $matches[1];
......@@ -49,6 +51,15 @@ $IP = guess_IP("pc",$node_num);
#
$type = guess_node_type($cpuspeed,$disksize);
#
# Try to figure out which switch ports it's plugged into
#
$mac_list = array();
foreach ($interfaces as $interface) {
$mac_list[$interface["type"]] = "";
}
find_switch_macs($mac_list);
#
# Stash this information in the database
#
......@@ -63,8 +74,18 @@ foreach ($interfaces as $interface) {
$iface = $interface["iface"];
$mac = $interface["mac"];
$type = $interface["type"];
DBQueryFatal("insert into new_interfaces set new_node_id=$new_node_id, "
. "iface='$iface', mac='$mac', interface_type='$type'");
if ($mac_list[$mac]["switch"]) {
DBQueryFatal("insert into new_interfaces set " .
"new_node_id=$new_node_id, iface='$iface', mac='$mac', " .
"interface_type='$type', ".
"switch_id='$mac_list[$mac][switch]', " .
"switch_card='$mac_list[$mac][card]', " .
"switch_port='$mac_list[$mac][port]'");
} else {
DBQueryFatal("insert into new_interfaces set " .
"new_node_id=$new_node_id, iface='$iface', mac='$mac', " .
"interface_type='$type'");
}
}
#
......@@ -106,103 +127,4 @@ function find_free_id($prefix) {
}
function guess_IP ($prefix, $number) {
$hostname = $prefix . $number;
#
# First, let's see if they've already added to to DNS - the PHP
# gethostbyname has a really dumb way to return failure - it just
# gives you the hostname back.
#
$IP = gethostbyname($hostname);
if (strcmp($IP,$hostname)) {
return $IP;
}
#
# Okay, no such luck. We'll go backwards through the host list until
# we find the previous node with an established IP, then add our offset
# onto that
#
$i = $number - 1;
$IP = "";
while ($i > 0) {
$query_result = DBQueryFatal("select IP from interfaces as i " .
"left join nodes as n on i.node_id = n.node_id left join " .
"node_types as nt on n.type = nt.type " .
"where n.node_id='$prefix$i' and i.card = nt.control_net");
if (mysql_num_rows($query_result)) {
$row = mysql_fetch_array($query_result);
$IP = $row[IP];
break;
}
$i--;
}
if ($i <= 0) {
return 0;
}
#
# Parse the IP we got into the last octet and everything else. Note that we
# don't really do the IP address calcuation correctly - just an
# approximation of the right thing.
#
list($oct1,$oct2,$oct3,$oct4) = explode(".",$IP);
$oct4 += $number - $i;
# We don't yet wrap - it may not be OK to do
if ($oct4 > 254) {
return 0;
}
return "$oct1.$oct2.$oct3.$oct4";
}
function guess_node_type($proc,$disk) {
#
# Allow the reported speed to differ from the one in the database
#
$fudge_factor = .05;
#
# Convert disk size from megabytes to gigabtypes
#
$disk /= 1000.0;
#
# Go through node types and try to find a single one that matches
# this node's processor speed. This is a totally bogus way to do this,
# but it's the best we got for now.
#
$node_type = "";
$query_result = DBQueryFatal("select type, speed, HD from node_types " .
"where !isvirtnode and !isremotenode");
while ($row = mysql_fetch_array($query_result)) {
$speed = $row["speed"];
$type = $row["type"];
$HD = $row["HD"];
echo "Checking $speed vs $proc, $HD vs $disk\n";
if (($proc > ($speed * (1.0 - $fudge_factor))) &&
($proc < ($speed * (1.0 + $fudge_factor))) &&
($disk > ($HD * (1.0 - $fudge_factor))) &&
($disk < ($HD * (1.0 + $fudge_factor)))) {
if ($node_type != "") {
# We found two potential matches, choose neither
echo "Found a second match ($type), bailing\n";
return "";
} else {
echo "Found a first match ($type)\n";
$node_type = $type;
}
}
}
echo "Returning $node_type\n";
return $node_type;
}
?>
<?PHP
#
# EMULAB-COPYRIGHT
# Copyright (c) 2003 University of Utah and the Flux Group.
# All rights reserved.
#
require("defs.php3");
require("newnode-defs.php3");
#
# List the nodes that have checked in and are awaint being added the the real
# testbed
#
#
# Standard Testbed Header
#
PAGEHEADER("New Testbed Nodes");
#
# Only admins can see this page
#
$uid = GETLOGIN();
LOGGEDINORDIE($uid);
$isadmin = ISADMIN($uid);
if (! $isadmin) {
USERERROR("You do not have admin privileges!", 1);
}
#
# XXX - a hack
#
$gid = "nobody";
#
# Start out by performing any operations they may have asked for
#
if ($selected) {
$selected_nodes = $selected;
} else {
$selected_nodes = array();
}
#
# Build up a handy little clause for these nodes to be used in WHEREs
#
if (count($selected_nodes)) {
$equal_clauses = array();
foreach ($selected_nodes as $node) {
$equal_clauses[] = "new_node_id=$node";
$equal_clauses_qualified[] = "n.new_node_id=$node";
}
$whereclause = implode(" OR ",$equal_clauses);
$whereclause_qualified = implode(" OR ",$equal_clauses_qualified);
}
#
# Delete nodes
#
if ($delete) {
DBQueryFatal("DELETE FROM new_nodes WHERE $whereclause");
DBQueryFatal("DELETE FROM new_interfaces WHERE $whereclause");
}
#
# Recalculate IPs
#
if ($calc) {
$query_result = DBQueryFatal("SELECT new_node_id, node_id FROM new_nodes " .
"WHERE $whereclause ORDER BY new_node_id");
while ($row = mysql_fetch_array($query_result)) {
$id = $row["new_node_id"];
$name = $row["node_id"];
if (preg_match("/^(.*\D)(\d+)$/",$name,$matches)) {
$prefix = $matches[1];
$number = $matches[2];
$newIP = guess_IP($prefix,$number);
if (!$newIP) {
echo "<h4>Failed to guess IP for $name!</h4>";
} else {
DBQueryFatal("UPDATE new_nodes SET IP='$newIP' WHERE " .
"new_node_id='$id'");
}
} else {
echo "Unable to understand the node name $name";
}
}
}
#
# Create the nodes in the real database
#
if ($create) {
$nodenames = array();
$query_result = DBQueryFatal("SELECT node_id FROM new_nodes " .
"WHERE $whereclause");
while ($row = mysql_fetch_array($query_result)) {
$nodenames[] = $row["node_id"];
}
$nodelist = implode(" ",$nodenames);
echo "<h3>Creating nodes - this could take a while, please wait</h3>\n";
echo "<hr>\n";
echo "<pre>\n";
passthru("$TBSUEXEC_PATH $uid $gid newnode $nodelist 2>&1");
echo "</pre>\n";
echo "<hr>\n";
}
#
# Look for the nodes on the switch again
#
if ($research) {
#
# Get the MACs we're supposed to be looking for
#
$query_result = DBQueryFatal("SELECT i.mac, i.new_node_id, n.node_id, " .
"i.iface FROM new_interfaces as i LEFT JOIN new_nodes as n " .
"ON i.new_node_id = n.new_node_id WHERE $whereclause_qualified");
$mac_list = array();
while ($row = mysql_fetch_array($query_result)) {
$mac_list[$row["mac"]] = array( "new_node_id" => $row["new_node_id"],
"node_id" => $row["node_id"], "iface" => $row["iface"]);
}
echo "<h3>Looking for MACs, this could take a while...</h3>";
find_switch_macs($mac_list);
foreach ($mac_list as $mac => $switchport) {
if (in_array("switch",$switchport)) {
DBQueryFatal("UPDATE new_interfaces SET " .
"switch_id='$switchport[switch]', " .
"switch_card='$switchport[card]', " .
"switch_port='$switchport[port]', ".
"WHERE new_node_id='$switchport[new_node_id]' " .
"AND iface='$switchport[iface]'");
} else {
echo "<h4>Unable to find $switchport[node_id]:$switchport[iface] " .
"on switches, not updating</h4>\n";
}
}
}