Commit f3f0fa98 authored by Leigh Stoller's avatar Leigh Stoller

New widearea node checkin stuff for CMU. This stuff is quite a bit

different then the original widearea code. Simpler, less dynamic.

First off, the wanodecreate script creates a new widearea_nodeinfo
entry.  These are nodes that will later checkin and be created as a
real node.  The input is a little xml file that you can use to specify
the stuff in the table entry (city, state, zip, etc). You can also
provide a privkey (no more then 64 chars), or one will be generated
for you.  For each one of these, create a Dongle Boot and stash the
privkey as /etc/emulab/emulab-privkey on the dongle. You do not assign
the IP address; the node will tell us that when it checks in.

A node checks in like this:

	https://$bossname/wanodecheckin.php?IP=$IP&privkey=$privkey
               &hostname=$hostname

The web page is simply a stub that makes sure the arguments don't have
any illegal characters, and then passes off to the backend.

The backend script checks the privkey and finds the widearea_nodeinfo.
The first time the node checks in, the node is created (db/Node.pm)
(nodes table, interfaces table, etc), and the node is moved to hwdown.
Subsequent checkins watch for changes to the IP or hostname, and issue
named_setup calls as needed.
parent 0f1f37ee
......@@ -22,9 +22,11 @@ SBIN_SCRIPTS = vlandiff vlansync withadminprivs export_tables cvsupd.pl \
eventping grantnodetype import_commitlog daemon_wrapper \
opsreboot deletenode node_statewait grabwebcams \
grabswitchconfig backupswitches cvsinit checkquota \
spewconlog opsdb_control newnode suchown archive_list
spewconlog opsdb_control newnode suchown archive_list \
wanodecheckin wanodecreate
WEB_SBIN_SCRIPTS= webnewnode webdeletenode webspewconlog webarchive_list
WEB_SBIN_SCRIPTS= webnewnode webdeletenode webspewconlog webarchive_list \
webwanodecheckin
WEB_BIN_SCRIPTS = webcreate_image websetdest weblinkmon_ctl webspewevents \
webdelay_config
LIBEXEC_SCRIPTS = spewleds webcopy spewsource webcvsweb xlogin webviewvc \
......
......@@ -2,7 +2,7 @@
#
# EMULAB-COPYRIGHT
# Copyright (c) 2000-2002, 2004, 2005 University of Utah and the Flux Group.
# Copyright (c) 2000-2002, 2004, 2005, 2007 University of Utah and the Flux Group.
# All rights reserved.
#
......@@ -32,6 +32,7 @@ my $UNIFIED = "@UNIFIED_BOSS_AND_OPS@";
my $BOSSNODE = "@BOSSNODE@";
my $USERNODE = "@USERNODE@";
my $FSNODE = "@FSNODE@";
my $MAINSITE = @TBMAINSITE@;
# Locals
my $debug = 0;
......@@ -97,7 +98,7 @@ else {
TBPhysNodeID($hostname, \$physhost);
$hostname = $physhost;
}
elsif (TBIsNodeRemote($hostname)) {
elsif (TBIsNodeRemote($hostname) && $MAINSITE) {
$user = "emulabman";
}
}
......
#!/usr/bin/perl -wT
#
# EMULAB-COPYRIGHT
# Copyright (c) 2003-2007 University of Utah and the Flux Group.
# All rights reserved.
#
use strict;
use English;
use Getopt::Std;
#
# Notes on the dongle boot:
#
# * Need SSL version of tmcc, emulab.pem and pcwa.pem file.
# * Root public ssh key from boss.
# * New version of libtmcc with bootwhat def.
# * Create /etc/emulab/emulab-privkey to match what is in widearea_nodeinfo
# for each node (per node dongles).
# * Created /etc/emulab/bossnode pointing to boss.
# * touch /etc/emulab/isrem
#
#
# Widearea Node checkin.
#
# Note that the exit code from this script is propogated via the web
# interface to the remote node.
#
sub usage()
{
print(STDERR "Usage: wanodecheckin [-h <hostname>] <privkey> <IP>\n");
exit(-1);
}
my $optlist = "dh:";
my $debug = 0;
my $hostname;
#
# Configure variables
#
my $TB = "@prefix@";
my $TBOPS = "@TBOPSEMAIL@";
my $TBLOGS = "@TBLOGSEMAIL@";
my $TBBASE = "@TBBASE@";
my $NAMED = "$TB/sbin/named_setup";
my $NALLOC = "$TB/bin/nalloc";
# un-taint path
$ENV{'PATH'} = '/bin:/usr/bin:/usr/local/bin';
delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};
# Turn off line buffering on output
$| = 1;
#
# Testbed Support libraries
#
use lib "@prefix@/lib";
use libdb;
use libtestbed;
use Experiment;
use Node;
# Protos
sub notify($);
sub fatal($$);
# These error codes must match whats in the client, but of course.
my $WASTATUS_OKAY = 0;
my $WASTATUS_MISSINGARGS = 100;
my $WASTATUS_INVALIDARGS = 101;
my $WASTATUS_BADPRIVKEY = 102;
my $WASTATUS_BADIPADDR = 103;
my $WASTATUS_BADREMOTEIP = 104;
my $WASTATUS_IPADDRINUSE = 105;
my $WASTATUS_MUSTUSESSL = 106;
my $WASTATUS_OTHER = 199;
# Default initial experiment unless overridden.
my $PID_HWDOWN = NODEDEAD_PID();
my $EID_HWDOWN = NODEDEAD_EID();
# Parse options.
my %options = ();
if (! getopts($optlist, \%options)) {
usage();
}
if (defined($options{"d"})) {
$debug = 1;
}
if (defined($options{"h"})) {
$hostname = $options{"h"};
if ($hostname =~ /^([-\w]+)$/) {
$hostname = $1;
}
else {
fatal($WASTATUS_INVALIDARGS, "Bad data in hostname");
}
}
usage
if (@ARGV != 2);
my $privkey = $ARGV[0];
my $IP = $ARGV[1];
# untaint args.
if ($IP =~ /^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/) {
$IP = $1;
}
else {
fatal($WASTATUS_INVALIDARGS, "Bad data in IP address");
}
if ($privkey =~ /^([\w]+)$/) {
$privkey = $1;
}
else {
fatal($WASTATUS_INVALIDARGS, "Bad data in private key");
}
#
# Lookup and see if privkey is valid and matches the IP. If so, all is
# good.
#
my $query_result = DBQueryWarn("select node_id,IP,machine_type ".
" from widearea_nodeinfo ".
"where privkey='$privkey'");
if (!$query_result) {
fatal($WASTATUS_OTHER, "DB Error getting widearea_nodeinfo table");
}
if (!$query_result->numrows) {
#
# We are not going to do dynamic widearea nodes yet. If the privkey
# is not in the table, it is not a node we know/care about.
#
fatal($WASTATUS_BADPRIVKEY, "Unknown private key");
}
my ($node_id, $known_IP, $machine_type) = $query_result->fetchrow_array();
if (!defined($known_IP)) {
my $experiment = Experiment->Lookup($PID_HWDOWN, $EID_HWDOWN);
#
# If the known_IP is not defined, this is the first time we have
# heard from the node. Create it.
#
my $node = Node->Create($node_id, $experiment,
{'type' => $machine_type,
'role' => $Node::NODEROLE_TESTNODE});
if (!defined($node)) {
fatal($WASTATUS_OTHER, "Could not create new node: $node_id");
}
# Initialize the control interface.
my $control_iface = $node->control_iface();
if (!defined($control_iface)) {
$control_iface = "eth0";
}
if (! DBQueryWarn("replace into interfaces set ".
" node_id='$node_id', ".
" card=0, port=1, ".
" interface_type='fxp', ".
" iface='$control_iface', ".
" role='" . TBDB_IFACEROLE_CONTROL() . "', ".
" IP='$IP'")) {
fatal($WASTATUS_OTHER,
"Failed to insert new WA node into interfaces table");
}
if (! DBQueryWarn("update widearea_nodeinfo set IP='$IP' ".
(defined($hostname) ? ",hostname='$hostname' " : "") .
"where node_id='$node_id'")) {
fatal($WASTATUS_OTHER, "Failed to update IP in widearea_nodeinfo");
}
notify("Widearea node $node_id has checked in for the firstime ".
"with IP $IP");
}
elsif ($known_IP eq $IP) {
#
# If the IP has not changed, nothing to do unless the hostname changed.
#
if (defined($hostname)) {
DBQueryWarn("update widearea_nodeinfo set hostname='$hostname' ".
"where node_id='$node_id'");
}
exit($WASTATUS_OKAY);
}
else {
#
# IP has changed, probably cause of dynamic DHCP. We have to
# change various tables and then regen the nameserver config.
#
if (! (DBQueryWarn("update interfaces set IP='$IP' ".
"where node_id='$node_id' and card=0 and port=1") &&
DBQueryWarn("update widearea_nodeinfo set IP='$IP' ".
(defined($hostname) ? ",hostname='$hostname' " : "") .
"where node_id='$node_id'"))) {
fatal($WASTATUS_OTHER, "Failed to update IP in DB");
}
notify("Widearea node $node_id has changed its IP address from $known_IP ".
"to $IP");
}
# Now regen nameserver.
if (system($NAMED) != 0) {
fatal($WASTATUS_OTHER, "Failed to regenerate named files and restart");
}
exit($WASTATUS_OKAY);
# Notify TBOPS
sub notify($)
{
my ($message) = @_;
print "$message\n";
SENDMAIL($TBOPS,
"Widearea Node Checkin",
"\n".
"Widearea Node Checkin.\n".
"IP: $IP, Privkey: $privkey\n\n".
"$message",
"$TBOPS");
}
# Return Error code and send email;
sub fatal($$)
{
my ($status, $message) = @_;
print STDERR "*** $0:\n".
" $message\n";
SENDMAIL($TBOPS,
"Failure in Widearea Node Checkin",
"\n".
"Failure in Widearea Node Checkin. Exited with $status.\n".
"IP: $IP, Privkey: $privkey\n\n".
"$message",
"$TBOPS");
exit($status);
}
#!/usr/bin/perl -w
#
# EMULAB-COPYRIGHT
# Copyright (c) 2003-2007 University of Utah and the Flux Group.
# All rights reserved.
#
use strict;
use English;
use Getopt::Std;
use XML::Simple;
use Data::Dumper;
#
# Simple script to create new widearea nodes. The XML file should look
# like this:
#
# <opt>
# <site value='NYC' />
# <city value='FEE' />
# <machine_type value='pc850' />
# <state />
# </opt>
#
sub usage()
{
print(STDERR "Usage: wanodecreate <node_id> <xmlfile>\n");
exit(-1);
}
my $optlist = "d";
my $debug = 0;
# Default values for the table entry. Gotta have the undef entries.
my %defaults = ('node_id' => undef,
'machine_type' => undef,
'contact_uid' => 'elabman',
'contact_idx' => undef,
'connect_type' => 'unknown',
'city' => "",
'state' => "",
'zip' => "",
'country' => "USA",
'external_node_id' => "",
'hostname' => "NULL",
'site' => "NULL",
'latitude' => "NULL",
'longitude' => "NULL",
'bwlimit' => "NULL",
'privkey' => undef);
#
# Configure variables
#
my $TB = "@prefix@";
my $TBOPS = "@TBOPSEMAIL@";
my $TBLOGS = "@TBLOGSEMAIL@";
# un-taint path
$ENV{'PATH'} = '/bin:/usr/bin:/usr/local/bin';
delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};
# Turn off line buffering on output
$| = 1;
#
# Testbed Support libraries
#
use lib "@prefix@/lib";
use libdb;
use libtestbed;
use User;
use Node;
use NodeType;
# Protos
sub fatal($);
# Parse options.
my %options = ();
if (! getopts($optlist, \%options)) {
usage();
}
if (defined($options{"d"})) {
$debug = 1;
}
usage
if (@ARGV != 2);
my $node_id = $ARGV[0];
my $xmlfile = $ARGV[1];
#
# Verify user is an admin.
#
my $this_user = User->ThisUser();
if (! defined($this_user)) {
fatal("You ($UID) do not exist!");
}
if (! $this_user->IsAdmin()) {
fatal("You do not have permission to create new widearea nodes");
}
if (!-e $xmlfile) {
fatal("$xmlfile does not exist");
}
my $node = Node->Lookup($node_id);
if (defined($node)) {
fatal("$node_id already exists in the nodes table!");
}
my $query_result =
DBQueryFatal("select node_id from widearea_nodeinfo ".
"where node_id='$node_id'");
if ($query_result->numrows) {
fatal("$node_id already exists in the widearea_nodeinfo table!");
}
my $parse = XMLin($xmlfile, ValueAttr => [ 'value' ],
SuppressEmpty => undef);
# Pick up values from XML file.
foreach my $key (keys(%defaults)) {
my $default = $defaults{$key};
if (exists($parse->{$key}) && defined($parse->{$key})) {
$defaults{$key} = $parse->{$key};
}
}
# Node_id from command line
$defaults{'node_id'} = $node_id;
# Make sure we got a privkey, or generate one if not.
if (!defined($defaults{'privkey'})) {
$defaults{'privkey'} = TBGenSecretKey();
}
# Map the contact_uid to a real user.
if (!defined($defaults{'contact_uid'})) {
$defaults{'contact_uid'} = $this_user->uid();
}
my $contact_user = User->Lookup($defaults{'contact_uid'});
if (!defined($contact_user)) {
fatal("Contact uid does not exist in the Emulab DB!");
}
$defaults{'contact_idx'} = $contact_user->uid_idx();
# Make sure the machine_type is real.
if (!defined($defaults{'machine_type'}) || $defaults{'machine_type'} eq "") {
fatal("Invalid node type given!");
}
my $nodetype = NodeType->Lookup($defaults{'machine_type'});
if (!defined($nodetype)) {
fatal("Invalid node type given!");
}
# Make everything is defined.
foreach my $key (keys(%defaults)) {
my $default = $defaults{$key};
if (!defined($default)) {
fatal("Must provided a defintion for $key!");
}
}
DBQueryFatal("insert into widearea_nodeinfo set ".
join(",", map("$_='" . $defaults{$_} . "'", keys(%defaults))));
print "Inserted new node $node_id with key: ".
$defaults{'privkey'} . "\n";
exit(0);
sub fatal($)
{
my ($message) = @_;
die("*** $0:\n".
" $message\n");
}
<?php
#
# EMULAB-COPYRIGHT
# Copyright (c) 2007 University of Utah and the Flux Group.
# All rights reserved.
#
require("defs.php3");
# These error codes must match whats in the client, but of course.
define("WASTATUS_OKAY" , 0);
define("WASTATUS_MISSINGARGS", 100);
define("WASTATUS_INVALIDARGS", 101);
define("WASTATUS_BADPRIVKEY", 102);
define("WASTATUS_BADIPADDR", 103);
define("WASTATUS_BADREMOTEIP", 104);
define("WASTATUS_IPADDRINUSE", 105);
define("WASTATUS_MUSTUSESSL", 106);
define("WASTATUS_OTHER", 199);
#
# Spit back a text message we can display to the user on the console
# of the node running the checkin. We could return an http error, but
# that would be of no help to the user on the other side.
#
function SPITSTATUS($status)
{
header("Content-Type: text/plain");
echo "emulab_status=$status\n";
}
# Required arguments
$reqargs = RequiredPageArguments("IP", PAGEARG_STRING,
"privkey", PAGEARG_STRING);
$optargs = OptionalPageArguments("hostname", PAGEARG_STRING);
# Must use https,
if (!isset($_SERVER["SSL_PROTOCOL"])) {
SPITSTATUS(WASTATUS_MUSTUSESSL);
return;
}
if (!preg_match('/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/', $IP) ||
!preg_match('/^[\w]+$/', $privkey) ||
(isset($hostname) &&
!preg_match('/^[-\w]+$/', $hostname))) {
SPITSTATUS(WASTATUS_INVALIDARGS);
return;
}
#
# Make sure this is a valid privkey before we invoke the backend.
#
$query_result =
DBQueryFatal("select IP from widearea_nodeinfo where privkey='$privkey'");
if (! mysql_num_rows($query_result)) {
SPITSTATUS(WASTATUS_PRIVKEY);
return;
}
#
# Invoke the backend and return the status. We send the IP since cause we
# have to deal with nodes with dynamic IP addresses.
#
$retval = SUEXEC("nobody", $TBADMINGROUP,
"webwanodecheckin " .
(isset($hostname) ? "-h $hostname " : "") . "$privkey $IP",
SUEXEC_ACTION_IGNORE);
if ($retval) {
SUEXECERROR(SUEXEC_ACTION_CONTINUE);
}
SPITSTATUS($retval);
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment