#!/usr/bin/perl -wT # # Copyright (c) 2003-2009 University of Utah and the Flux Group. # # {{{EMULAB-LICENSE # # This file is part of the Emulab network testbed software. # # This file is free software: you can redistribute it and/or modify it # under the terms of the GNU Affero General Public License as published by # the Free Software Foundation, either version 3 of the License, or (at # your option) any later version. # # This file is distributed in the hope that it will be useful, but WITHOUT # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or # FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public # License for more details. # # You should have received a copy of the GNU Affero General Public License # along with this file. If not, see . # # }}} # use strict; use English; use Getopt::Std; # # Notes on the dongle boot: # # * Check /boot/loader.conf for comconsole or vga. # * 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 ] \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", 0); } } 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", 0); } if ($privkey =~ /^([\w]+)$/) { $privkey = $1; } else { fatal($WASTATUS_INVALIDARGS, "Bad data in private key", 1); } # # 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", 0); } 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", 1); } my ($node_id, $known_IP, $machine_type) = $query_result->fetchrow_array(); $query_result = DBQueryWarn("SELECT n.node_id FROM nodes AS n LEFT ". "JOIN widearea_nodeinfo AS w ON n.node_id=w.node_id ". "WHERE w.node_id='$node_id'"); if (! $query_result->numrows) { my $experiment = Experiment->Lookup($PID_HWDOWN, $EID_HWDOWN); # # This is the first time we have heard from a partially-initialised 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", 0); } # 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", 0); } 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", 0); } 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", 0); } 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", 0); } 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: XXXX\n\n". "$message", "$TBOPS"); } # Return Error code and send email; sub fatal($$$) { my ($status, $message, $reportkey) = @_; 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: ". ($reportkey ? $privkey : "XXXX"). "\n\n". "$message", "$TBOPS"); exit($status); }