Commit 3c1a5bad authored by Timothy Stack's avatar Timothy Stack

Robot related stuff: power via e-mail, client-install fixups, checking
coords against camera boundaries.

	* configure, configure.in: Add tbsetup/power_mail.pm to the list
	of template files.

	* doc/cross-compiling.txt: More stargate notes.

	* event/sched/rpc.cc: Updates for the addition of the cameras
	table.

	* robots/GNUmakefile.in, robots/emc/GNUmakefile.in,
	robots/mtp/GNUmakefile.in, robots/rmcd/GNUmakefile.in,
	robots/tbsetdest/GNUmakefile.in, robots/vmcd/GNUmakefile.in:
	client-install fixups.

	* tbsetup/GNUmakefile.in: Add power_mail.pm.

	* tbsetup/os_setup.in: Don't skip reboot of robots anymore.

	* tbsetup/power.in: Add special case for a power_id of "mail",
	which calls into the power_mail.pm backend.

	* tbsetup/power_mail.pm.in: E-mail backend for power, it sends an
	e-mail to tbops and waits for the outlets.last_power value to be
	updated from the power.php3 web page.

	* tbsetup/ns2ir/parse-ns.in: Add the contents of the cameras table
	to the TBCOMPAT namespace.

	* tbsetup/ns2ir/sim.tcl.in: More checking of "setdest" inputs.

	* tbsetup/ns2ir/topography.tcl: Update the checkdest method to
	check destination points against the camera list.

	* www/powertime.php3: Webpage used to update the last power time
	for nodes.

	* www/shownode.php3: Add "Update Power Time" menu button.
parent 254e99e8
......@@ -1916,6 +1916,7 @@ outfiles="$outfiles Makeconf GNUmakefile \
tbsetup/GNUmakefile tbsetup/console_setup tbsetup/spewlogfile \
tbsetup/spewrpmtar tbsetup/gentopofile tbsetup/power_sgmote.pm \
tbsetup/console_reset tbsetup/bwconfig tbsetup/power_rpc27.pm \
tbsetup/power_mail.pm \
tbsetup/os_load tbsetup/os_setup tbsetup/os_select tbsetup/power \
tbsetup/node_reboot tbsetup/webnscheck tbsetup/nscheck \
tbsetup/resetvlans tbsetup/rmuser tbsetup/rmproj \
......
......@@ -623,6 +623,7 @@ outfiles="$outfiles Makeconf GNUmakefile \
tbsetup/GNUmakefile tbsetup/console_setup tbsetup/spewlogfile \
tbsetup/spewrpmtar tbsetup/gentopofile tbsetup/power_sgmote.pm \
tbsetup/console_reset tbsetup/bwconfig tbsetup/power_rpc27.pm \
tbsetup/power_mail.pm \
tbsetup/os_load tbsetup/os_setup tbsetup/os_select tbsetup/power \
tbsetup/node_reboot tbsetup/webnscheck tbsetup/nscheck \
tbsetup/resetvlans tbsetup/rmuser tbsetup/rmproj \
......
......@@ -95,3 +95,15 @@ made a fake /sbin/consoletype
copied /etc/sysconfig/network from a pc
added /var/tmp, /var/db to /etc/rcS.d/S05mountall.sh for install-tarfile
rsync flags: -avzpog --exclude-from=garcia.exclude
contents of garcia.exclude:
/proj/*
/share/*
/var/*
/proc/*
/users/*
/dev/*
/tmp/*
/mnt/*
......@@ -323,17 +323,17 @@ RPC_cameralist(FILE *emcd_config, char *area)
ulxr::RpcString tmp;
ulxr::Struct attr;
int port;
attr = cameras.getItem(lpc);
tmp = attr.getMember("host");
tmp = attr.getMember("hostname");
port = ((ulxr::Integer)attr.getMember("port")).getInteger();
fprintf(emcd_config,
"camera %s %s %d %f %f %f %f\n",
area,
tmp.getString().c_str(),
port,
((ulxr::Double)attr.getMember("x")).getDouble(),
((ulxr::Double)attr.getMember("y")).getDouble(),
((ulxr::Double)attr.getMember("loc_x")).getDouble(),
((ulxr::Double)attr.getMember("loc_y")).getDouble(),
((ulxr::Double)attr.getMember("width")).getDouble(),
((ulxr::Double)attr.getMember("height")).getDouble());
}
......
......@@ -27,6 +27,7 @@ SUBDIRS = tbsetdest $(MEZZDIR) mtp $(EMCDIR) vmcd primotion rmcd
all: all-subdirs
client: client-subdirs
client-install: client-install-subdirs
check: check-subdirs
install: install-subdirs
......
......@@ -15,7 +15,7 @@ PROGS = emcd loclistener
TESTS = test_emcd.sh
all: $(PROGS)
client:
client client-install:
include $(TESTBED_SRCDIR)/GNUmakerules
......@@ -23,11 +23,11 @@ OBJS = emcd.o robot_list.o
CFLAGS += -O -g -Wall -I${OBJDIR} -I/usr/local/include
CFLAGS += -I${SRCDIR}/../mtp -I../mtp -I${SRCDIR}/../../event/lib
CFLAGS += -I${SRCDIR}/../../lib/libtb
CFLAGS += `elvin-config --cflags vin4c`
CFLAGS += `$(ELVIN_CONFIG) --cflags vin4c`
LDFLAGS = -L../mtp -L${OBJDIR}/lib/libtb -L${OBJDIR}/event/lib
LIBS += -levent -lcrypto -lmtp -ltb
LIBS += `elvin-config --libs vin4c`
LIBS += `$(ELVIN_CONFIG) --libs vin4c`
test_emcd.sh: emcd
......
......@@ -22,6 +22,7 @@ JARS = $(TESTBED_SRCDIR)/www/mtp.jar
endif
all client: $(MTPLIBS) $(MTPPROGS) $(JARS)
client-install:
include $(TESTBED_SRCDIR)/GNUmakerules
......
......@@ -30,7 +30,7 @@ client-install: client
include $(TESTBED_SRCDIR)/GNUmakerules
CXXFLAGS += -O -g -Wall -I${OBJDIR} -I/usr/local/include
CXXFLAGS += -I${SRCDIR}/../mtp -I../mtp
CXXFLAGS += -I${SRCDIR}/../mtp -I../mtp -pthread -D_REENTRANT
LDFLAGS = -L../mtp -Ldgrobot
......@@ -68,7 +68,7 @@ LDFLAGS += \
-laStem \
-laIO \
-laUI \
-lpthread \
-pthread \
-lm
endif
......
......@@ -12,7 +12,9 @@
#include "config.h"
#include <errno.h>
#include <stdio.h>
#include <fcntl.h>
#include <assert.h>
#include <signal.h>
#include <string.h>
......@@ -24,6 +26,9 @@
#include <sys/socket.h>
#include <arpa/inet.h>
#include <list>
#include <algorithm>
#include "acpGarcia.h"
#include "acpValue.h"
......@@ -37,6 +42,8 @@
*/
#define PILOT_PORT 2531
static const char *DEFAULT_LOG_PATH = "/tmp/garcia-pilot.log";
/**
* Default path to the battery log.
*/
......@@ -49,6 +56,9 @@ static const char *BATTERY_LOG_PATH = "/var/log/battery.log";
*/
static volatile int looping = 1;
extern char **environ;
static char **reexec_argv;
/**
* Signal handler for SIGINT, SIGTERM, and SIGQUIT signals. Sets looping to
* zero and aborts() if it is called more than three times in case the program
......@@ -89,13 +99,19 @@ static void sigdebug(int sig)
* Signal handler for SIGSEGV and SIGBUS, just prints out the signal number and
* aborts.
*/
static void sigpanic(int sig)
static void sigpanic(int signum)
{
fprintf(stderr,
"panic: signal=%d\n",
sig);
char msg[128];
snprintf(msg, sizeof(msg), "panic: %d; reexec'ing\n", signum);
write(1, msg, strlen(msg));
execve(reexec_argv[0], reexec_argv, environ);
abort();
snprintf(msg, sizeof(msg), "reexec failed %s\n", strerror(errno));
write(1, msg, strlen(msg));
exit(1);
}
/**
......@@ -119,15 +135,21 @@ static void usage(void)
int main(int argc, char *argv[])
{
int c, port = PILOT_PORT, serv_sock, on_off = 1;
const char *logfile = NULL, *pidfile = NULL;
int lpc, c, port = PILOT_PORT, serv_sock, on_off = 1;
const char *logfile = DEFAULT_LOG_PATH, *pidfile = NULL;
const char *batteryfile = BATTERY_LOG_PATH;
int retval = EXIT_SUCCESS;
std::list<int> fd_list;
unsigned long now;
FILE *batterylog;
aIOLib ioRef;
sigset_t ss;
aErr err;
reexec_argv = argv;
sigfillset(&ss);
sigprocmask(SIG_UNBLOCK, &ss, NULL);
while ((c = getopt(argc, argv, "hdp:l:i:b:")) != -1) {
switch (c) {
case 'h':
......@@ -167,7 +189,7 @@ int main(int argc, char *argv[])
if (!debug) {
/* Become a daemon */
daemon(0, 0);
daemon(1, 0);
if (logfile) {
int logfd;
......@@ -187,7 +209,6 @@ int main(int argc, char *argv[])
FILE *fp;
if ((fp = fopen(pidfile, "w")) != NULL) {
fprintf(fp, "%d\n", getpid());
(void) fclose(fp);
}
}
......@@ -198,16 +219,24 @@ int main(int argc, char *argv[])
batteryfile);
}
for (lpc = 3; lpc < 32; lpc++) {
struct stat st;
if (fstat(lpc, &st) == 0) {
fd_list.push_back(lpc);
}
}
acpGarcia garcia;
signal(SIGUSR1, sigdebug);
signal(SIGHUP, sigdebug);
signal(SIGQUIT, sigquit);
signal(SIGTERM, sigquit);
signal(SIGINT, sigquit);
signal(SIGSEGV, (void (*)(int))sigpanic);
signal(SIGBUS, (void (*)(int))sigpanic);
signal(SIGSEGV, sigpanic);
signal(SIGBUS, sigpanic);
signal(SIGPIPE, SIG_IGN);
......@@ -271,12 +300,61 @@ int main(int argc, char *argv[])
FD_ZERO(&readfds);
FD_ZERO(&writefds);
FD_SET(serv_sock, &readfds);
for (lpc = 3; lpc < 32; lpc++) {
struct stat st;
if ((fstat(lpc, &st) == 0) &&
(std::find(fd_list.begin(),
fd_list.end(),
lpc) == fd_list.end())) {
fcntl(lpc, F_SETFD, 1);
}
}
for (lpc = 3; lpc < 128; lpc++) {
struct sockaddr_in sin;
socklen_t sin_len;
sin_len = sizeof(sin);
if ((lpc != serv_sock) &&
(getpeername(lpc,
(struct sockaddr *)&sin,
&sin_len) == 0)) {
struct mtp_packet ump;
if (debug) {
printf("recovered client: %s:%d\n",
inet_ntoa(sin.sin_addr),
ntohs(sin.sin_port));
}
pilotClient *pc = new pilotClient(lpc, wm, db);
pc->setFD(&readfds);
pc->setFD(&writefds);
clients.push_back(pc);
mtp_init_packet(&ump,
MA_Opcode, MTP_UPDATE_POSITION,
MA_Role, MTP_ROLE_ROBOT,
MA_Status, MTP_POSITION_STATUS_IDLE,
MA_CommandID, 0,
MA_TAG_DONE);
mtp_send_packet(pc->getHandle(), &ump);
}
}
wm.setDashboard(&db);
do {
fd_set rreadyfds = readfds, wreadyfds = writefds;
struct timeval tv_zero = { 0, 0 };
int rc;
if (debug == 3) {
int *i = 0;
*i = 1;
}
/* Poll the file descriptors, don't block */
rc = select(FD_SETSIZE,
......@@ -381,8 +459,10 @@ int main(int argc, char *argv[])
aIO_ReleaseLibRef(ioRef, &err);
fclose(batterylog);
batterylog = NULL;
if (batterylog) {
fclose(batterylog);
batterylog = NULL;
}
return retval;
}
......@@ -16,6 +16,8 @@
#include "pilotClient.hh"
extern int debug;
/**
* Callback passed to the wheelManager when performing movements.
*/
......@@ -52,7 +54,7 @@ void pilotMoveCallback::call(int status)
pilotClient::iterator i;
mtp_packet_t mp;
mtp_status_t ms;
switch (status) {
case aGARCIA_ERRFLAG_NORMAL:
ms = MTP_POSITION_STATUS_COMPLETE;
......
......@@ -19,6 +19,8 @@
#include "dashboard.hh"
#include "wheelManager.hh"
extern int debug;
/**
* An "execute" callback for a garcia behavior.
*/
......@@ -301,8 +303,16 @@ void wheelManager::stop(void)
void wheelManager::motionStarted(void)
{
if (debug) {
fprintf(stderr, "debug: motion started\n");
}
if ((this->wm_last_status != aGARCIA_ERRFLAG_NORMAL) &&
(this->wm_last_status != aGARCIA_ERRFLAG_ABORT)) {
if (debug) {
fprintf(stderr, "debug: clear error LED\n");
}
this->wm_dashboard->remUserLEDClient(&this->wm_error_notice);
this->wm_last_status = 0;
}
......@@ -315,9 +325,17 @@ void wheelManager::motionFinished(int status, wmCallback *callback)
{
this->wm_last_status = status;
if (debug) {
fprintf(stderr, "debug: motion finished -- %d\n", status);
}
if (status != aGARCIA_ERRFLAG_WONTEXECUTE) {
if ((this->wm_last_status != aGARCIA_ERRFLAG_NORMAL) &&
(this->wm_last_status != aGARCIA_ERRFLAG_ABORT)) {
if (debug) {
fprintf(stderr, "debug: set error LED\n");
}
this->wm_dashboard->addUserLEDClient(&this->wm_error_notice);
}
......
......@@ -15,7 +15,7 @@ PROGS = rmcd
TESTS = test_rmcd.sh test_rmcd2.sh
all: $(PROGS)
client:
client client-install:
include $(TESTBED_SRCDIR)/GNUmakerules
......
......@@ -14,7 +14,7 @@ include $(OBJDIR)/Makeconf
PROGS = tbsetdest
all: $(PROGS)
client:
client client-install:
include $(TESTBED_SRCDIR)/GNUmakerules
......
......@@ -26,7 +26,7 @@ TESTS += test_vmcd.sh test_vmcd2.sh test_vmcd3.sh test_vmcd4.sh
endif
all: $(PROGS)
client:
client client-install:
include $(TESTBED_SRCDIR)/GNUmakerules
......
......@@ -48,7 +48,8 @@ LIB_STUFF = libtbsetup.pm exitonwarn.pm libtestbed.pm snmpit_intel.pm \
snmpit_cisco.pm snmpit_lib.pm snmpit_apc.pm power_rpc27.pm \
snmpit_cisco_stack.pm snmpit_intel_stack.pm power_sgmote.pm \
snmpit_foundry.pm snmpit_stack.pm snmpit_remote.pm \
libaudit.pm libreboot.pm libosload.pm libtestbed.py
libaudit.pm libreboot.pm libosload.pm libtestbed.py \
power_mail.pm
#
# Force dependencies on the scripts so that they will be rerun through
......
......@@ -449,6 +449,18 @@ sub GenDefsFile($)
}
print TCL "\n";
print TCL "# Cameras\n";
$query_result = DBQueryFatal(
"select name,building,loc_x,loc_y,width,height from cameras");
while (my ($name,$building,$loc_x,$loc_y,$width,$height) =
$query_result->fetchrow_array()) {
print TCL "set cameras($name,$building,x) $loc_x\n";
print TCL "set cameras($name,$building,y) $loc_y\n";
print TCL "set cameras($name,$building,width) $width\n";
print TCL "set cameras($name,$building,height) $height\n";
}
print TCL "\n";
if (defined($pid)) {
print TCL "# OSIDs\n";
$query_result =
......
......@@ -1161,15 +1161,14 @@ Simulator instproc make_event {outer event} {
}
set x [lindex $evargs 0]
set y [lindex $evargs 1]
if {$x < 0 || $x >= [$topo set width]} {
perror "$x is out of bounds for node $obj"
if {! [$topo checkdest $self $x $y]} {
return
}
if {$y < 0 || $y >= [$topo set height]} {
perror "$x is out of bounds for node $obj"
set speed [lindex $evargs 2]
if {$speed != 0.0 && $speed != 0.1} {
perror "Speed is currently locked at 0.0 or 0.1"
return
}
set speed [lindex $evargs 2]
::GLOBALS::named-args [lrange $evargs 3 end] {
-orientation 0
}
......
......@@ -63,6 +63,7 @@ Topography instproc initialized {} {
Topography instproc checkdest {obj x y} {
var_import ::TBCOMPAT::obstacles
var_import ::TBCOMPAT::cameras
$self instvar area_name
$self instvar width
$self instvar height
......@@ -82,13 +83,33 @@ Topography instproc checkdest {obj x y} {
set id [lindex [split $key ,] 0]
if {($x >= $obstacles($id,$area_name,x1)) &&
($x < $obstacles($id,$area_name,x2)) &&
($x <= $obstacles($id,$area_name,x2)) &&
($y >= $obstacles($id,$area_name,y1)) &&
($y < $obstacles($id,$area_name,y2))} {
($y <= $obstacles($id,$area_name,y2))} {
perror "Destination $x,$y puts $obj in obstacle $value."
return 0
}
}
set camlist [array get cameras *,$area_name,x]
set in_cam ""
foreach {key value} $camlist {
set id [lindex [split $key ,] 0]
if {($x >= $cameras($id,$area_name,x)) &&
($x < [expr $cameras($id,$area_name,x) + \
$cameras($id,$area_name,width)]) &&
($y >= $cameras($id,$area_name,y)) &&
($y < [expr $cameras($id,$area_name,y) + \
$cameras($id,$area_name,height)])} {
set in_cam $id
}
}
if {$in_cam == ""} {
perror "Destination $x,$y is out of view of the tracking cameras";
return 0
}
}
return 1
}
......@@ -223,7 +223,8 @@ while (my %row = $db_result->fetchhash()) {
elsif ($class eq "robot") {
print "Skipping os_setup for $node cause its a robot.\n";
TBSetNodeAllocState($node, TBDB_ALLOCSTATE_RES_READY);
SetNodeBootStatus($node, NODEBOOTSTATUS_OKAY);
$nodeAllocStates{$node} = TBDB_ALLOCSTATE_RES_READY;
$reboots{$node} = 1;
next;
}
elsif ($subnode) {
......
......@@ -2,7 +2,7 @@
#
# EMULAB-COPYRIGHT
# Copyright (c) 2000-2004 University of Utah and the Flux Group.
# Copyright (c) 2000-2005 University of Utah and the Flux Group.
# All rights reserved.
#
......@@ -28,6 +28,7 @@ use libdb;
use libxmlrpc;
use power_rpc27;
use power_sgmote;
use power_mail;
use snmpit_apc;
use libtestbed;
use strict;
......@@ -210,7 +211,8 @@ foreach my $node (@machines) {
if (! ($time_ok || ($UID == 0)) ) {
warn "$node was power cycled recently. Skipping...\n";
next;
} else {
} elsif ( $power_id ne "mail" ) {
print "updating last_power\n";
DBQueryFatal("update outlets set last_power=CURRENT_TIMESTAMP " .
"where node_id = '$node'");
}
......@@ -240,21 +242,32 @@ foreach my $power_id (keys %outlets) {
}
my $nodestr = join(",",@nodes);
#
# Find out some information about this power controller
#
my $result = DBQueryFatal("select n.type, i.IP, t.class ".
"from nodes as n " .
"left join interfaces as i on n.node_id=i.node_id " .
"left join node_types as t on n.type=t.type " .
"where n.node_id='$power_id'");
if ($result->num_rows() == 0) {
warn "No entry found for power controller $power_id. Skipping " .
"$nodestr\n";
$exitval++;
next;
my $type;
my $IP;
my $class;
if ($power_id eq "mail") {
$type = "mail";
$IP = "";
$class = "";
}
else {
#
# Find out some information about this power controller
#
my $result = DBQueryFatal("select n.type, i.IP, t.class ".
"from nodes as n " .
"left join interfaces as i on n.node_id=i.node_id " .
"left join node_types as t on n.type=t.type " .
"where n.node_id='$power_id'");
if ($result->num_rows() == 0) {
warn "No entry found for power controller $power_id. Skipping " .
"$nodestr\n";
$exitval++;
next;
}
($type, $IP, $class) = $result->fetchrow();
}
my ($type, $IP, $class) = $result->fetchrow();
#
# Finally, we look at the controller type and construct the proper type
......@@ -285,6 +298,11 @@ foreach my $power_id (keys %outlets) {
print "Control of $nodestr failed.\n"; $exitval++;
$errors++;
}
} elsif ($type eq "mail") {
if (mailctrl($op,@nodes)) {
print "Control of $nodestr failed.\n"; $exitval++;
$errors++;
}
} else {
print "power: Unknown power type '$type'\n";
$errors++;
......
#!/usr/bin/perl -wT
#
# EMULAB-COPYRIGHT
# Copyright (c) 2005 University of Utah and the Flux Group.
# All rights reserved.
#
# A perl module to power cycle nodes using email to the operators.
package power_mail;
use Exporter;
@ISA = ("Exporter");
@EXPORT = qw( mailctrl );
use lib "@prefix@/lib";
use libdb;
use libtestbed;
my $WWW = "@WWW@";
my $TBOPS = "@TBOPSEMAIL@";
my $default_tries = 10;
# Turn off line buffering on output
$| = 1;
# usage: mailctrl(cmd, nodes)
# cmd = { "cycle" | "on" | "off" }
# nodes = list of one or more physcial node names
#
# Returns 0 on success. Non-zero on failure.
#
sub mailctrl($@) {
my ($cmd, @nodes) = @_;
my %actual = ();
# Check to see if we have to send mail first.
foreach my $node (@nodes) {
my $dbres = DBQueryFatal(
"select (CURRENT_TIMESTAMP - last_power) < 500 " .
"from outlets where node_id='$node'");
if ($dbres->num_rows() == 0) {
print "Unknown node $node";
next;
}
($ok) = $dbres->fetchrow();
if (!$ok) {
$actual{$node} = 1;
}
}
if (scalar(keys %actual)) {
print "Sending mail to the operators\n";
SENDMAIL($TBOPS,
"Power $cmd nodes: " . join(" ",@nodes),
"Someone needs to power $cmd the following nodes:\n" .
"\t" . join(" ",@nodes) . "\n" .
"\nAnd update power time through this web page:\n" .
"\n https://$WWW/powertime.php3?node_id=" . join(",",@nodes),
$TBOPS);
foreach my $node (keys %actual) {
my $tries = $default_tries;
my $ok = 0;
print "Waiting for node $node\n";
while (!$ok) {
my $dbres = DBQueryFatal(
"select (CURRENT_TIMESTAMP - last_power) < 500 " .
"from outlets where node_id='$node'");
if ($dbres->num_rows() == 0) {
print "Unknown node $node";
next;
}
($ok) = $dbres->fetchrow();
if ($tries == 0) {
print "No more tries left for $node...";
return 1;
}
elsif (!$ok) {
$tries -= 1;
print "Sleeping for 30 seconds.\n";
sleep(30);
}
}
}
}
return 0;
}
1;
<?php
#
# EMULAB-COPYRIGHT
# Copyright (c) 2005 University of Utah and the Flux Group.
# All rights reserved.
#
include("defs.php3");
#
# Only known and logged in admins can update last_power times.
#
$uid = GETLOGIN();
LOGGEDINORDIE($uid);
$isadmin = ISADMIN($uid);
if (!$isadmin) {
USERERROR("Not enough permission.", 1);
}
#
# Verify page arguments.
#
if ((!isset($node_id) || strcmp($node_id, "") == 0) && !isset($nodes)) {
USERERROR("You must provide a node ID.", 1);
}
$body_str = "<center>";
if ($confirmed) {
$body_str .= "Updated power time for:<br><br>";
foreach ($nodes as $ni) {
if (!TBvalid_node_id($ni)) {
USERERROR("Invalid node ID: $ni", 1);
}
if (!TBValidNodeName($ni)) {
USERERROR("Invalid node ID: $ni", 1);
}
DBQueryFatal("update outlets " .