Commit bf489797 authored by Timothy Stack's avatar Timothy Stack
Browse files

More robot integration and some event system updates.

	* configure, configure.in: Detect rsync for loghole and add
	utils/loghole to the list of template files.

	* db/libdb.pm.in, db/xmlconvert.in: Add virt_node_startloc to the
	list of virtual tables.

	* event/lib/event.h, event/lib/event.c, event/lib/tbevent.py.tail:
	Add event_stop_main function to break out of the event_main()
	loop.  Add timeline to the address tuple.

	* event/sched/GNUmakefile.in, event/sched/error-record.h,
	event/sched/error-record.c, event/sched/event-sched.8,
	event/sched/event-sched.h, event/sched/event-sched.c,
	event/sched/group-agent.h, event/sched/group-agent.c,
	event/sched/listNode.h, event/sched/listNode.c,
	event/sched/local-agent.h, event/sched/local-agent.c,
	event/sched/node-agent.h, event/sched/node-agent.cc,
	event/sched/queue.c, event/sched/rpc.h, event/sched/rpc.cc,
	event/sched/simulator-agent.h, event/sched/simulator-agent.c,
	event/sched/timeline-agent.h, event/sched/timeline-agent.c:
	Updated event schedu...
parent 0d031e0b
......@@ -1839,6 +1839,45 @@ case "$INSTALL" in
;;
esac
# Extract the first word of "rsync", so it can be a program name with args.
set dummy rsync; ac_word=$2
echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
echo "configure:1811: checking for $ac_word" >&5
if eval "test \"`echo '$''{'ac_cv_path_RSYNC'+set}'`\" = set"; then
echo $ac_n "(cached) $ac_c" 1>&6
else
case "$RSYNC" in
/*)
ac_cv_path_RSYNC="$RSYNC" # Let the user override the test with a path.
;;
?:/*)
ac_cv_path_RSYNC="$RSYNC" # Let the user override the test with a dos path.
;;
*)
IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":"
ac_dummy="${PATH}"
for ac_dir in $ac_dummy; do
test -z "$ac_dir" && ac_dir=.
if test -f $ac_dir/$ac_word; then
ac_cv_path_RSYNC="$ac_dir/$ac_word"
break
fi
done
IFS="$ac_save_ifs"
test -z "$ac_cv_path_RSYNC" && ac_cv_path_RSYNC=""""
;;
esac
fi
RSYNC="$ac_cv_path_RSYNC"
if test -n "$RSYNC"; then
echo "$ac_t""$RSYNC" 1>&6
else
echo "$ac_t""no" 1>&6
fi
if test x"$RSYNC" = x"" || test ! -x "$RSYNC"; then
{ echo "configure: error: Rsync is required to use this software, see www.rsync.org." 1>&2; exit 1; }
fi
outfiles="$outfiles Makeconf GNUmakefile \
assign/GNUmakefile \
......@@ -1943,7 +1982,7 @@ outfiles="$outfiles Makeconf GNUmakefile \
utils/nsgen/GNUmakefile utils/nsgen/webnsgen \
utils/link_config utils/import_commitlog utils/dhcpd_wrapper \
utils/opsreboot utils/deletenode utils/webdeletenode utils/spewleds \
utils/grabwebcams \
utils/grabwebcams utils/loghole \
www/GNUmakefile www/defs.php3 www/dbdefs.php3 \
www/swish.conf www/websearch www/garcia-telemetry/GNUmakefile \
vis/GNUmakefile vis/webvistopology vis/dbvistopology \
......@@ -2253,6 +2292,7 @@ s%@TESTMODE@%$TESTMODE%g
s%@INSTALL_PROGRAM@%$INSTALL_PROGRAM%g
s%@INSTALL_SCRIPT@%$INSTALL_SCRIPT%g
s%@INSTALL_DATA@%$INSTALL_DATA%g
s%@RSYNC@%$RSYNC%g
s%@DISTCLEAN_FILES@%$DISTCLEAN_FILES%g
CEOF
......
......@@ -581,6 +581,10 @@ AC_PROG_INSTALL
;;
esac]
AC_PATH_PROG(RSYNC, rsync, "", [${PATH}])
if test x"$RSYNC" = x"" || test ! -x "$RSYNC"; then
AC_MSG_ERROR([Rsync is required to use this software, see www.rsync.org.])
fi
outfiles="$outfiles Makeconf GNUmakefile \
assign/GNUmakefile \
......@@ -685,7 +689,7 @@ outfiles="$outfiles Makeconf GNUmakefile \
utils/nsgen/GNUmakefile utils/nsgen/webnsgen \
utils/link_config utils/import_commitlog utils/dhcpd_wrapper \
utils/opsreboot utils/deletenode utils/webdeletenode utils/spewleds \
utils/grabwebcams \
utils/grabwebcams utils/loghole \
www/GNUmakefile www/defs.php3 www/dbdefs.php3 \
www/swish.conf www/websearch www/garcia-telemetry/GNUmakefile \
vis/GNUmakefile vis/webvistopology vis/dbvistopology \
......
......@@ -3053,6 +3053,7 @@ sub TBGetSiteVar($;$)
"virt_vtypes",
"virt_programs",
"virt_node_desires",
"virt_node_startloc",
"virt_simnode_attributes",
# vis_nodes is locked during update in prerender, so we
# will get a consistent dataset when we backup.
......
......@@ -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.
#
......@@ -91,6 +91,10 @@ my %virtual_tables =
tag => "node_desires",
row => "node_desire",
attrs => [ "vname", "desire" ]},
"virt_node_startloc" => { rows => undef,
tag => "node_startlocs",
row => "node_startloc",
attrs => [ "vname", "building" ]},
"virt_routes" => { rows => undef,
tag => "routes",
row => "route",
......
/*
* 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.
*/
......@@ -360,8 +360,6 @@ int event_poll_blocking(event_handle_t handle, unsigned int timeout)
int
event_main(event_handle_t handle)
{
int loop = 1;
if (!handle) {
ERROR("invalid parameter\n");
return 0;
......@@ -372,9 +370,15 @@ event_main(event_handle_t handle)
return 0;
}
if (handle->do_loop) {
ERROR("loop is already running\n");
return 0;
}
TRACE("entering event loop...\n");
if (handle->mainloop(&loop, handle->status) == 0) {
handle->do_loop = 1;
if (handle->mainloop(&handle->do_loop, handle->status) == 0) {
ERROR("Elvin mainloop failed: ");
elvin_error_fprintf(stderr, handle->status);
return 0;
......@@ -384,6 +388,29 @@ event_main(event_handle_t handle)
}
int event_stop_main(event_handle_t handle)
{
if (!handle) {
ERROR("invalid parameter\n");
return 0;
}
if (!handle->mainloop) {
ERROR("multithreaded programs do not have a mainloop\n");
return 0;
}
if (!handle->do_loop) {
ERROR("mainloop is not running\n");
return 0;
}
handle->do_loop = 0;
return 1;
}
/*
* Send the event notification NOTIFICATION. NOTIFICATION is
* allocated by event_notification_alloc, and may optionally
......@@ -485,7 +512,6 @@ event_schedule(event_handle_t handle, event_notification_t notification,
return event_notify(handle, notification);
}
/*
* Allocate an event notification. The address TUPLE defines who
* should receive the notification. Returns a pointer to an event
......@@ -534,6 +560,7 @@ event_notification_alloc(event_handle_t handle, address_tuple_t tuple)
!EVPUT("OBJTYPE", objtype) ||
!EVPUT("OBJNAME", objname) ||
!EVPUT("EVENTTYPE", eventtype) ||
!EVPUT("TIMELINE", timeline) ||
! event_notification_put_int32(handle, notification, "SCHEDULER", 0)) {
ERROR("could not add attributes to notification %p\n", notification);
return NULL;
......@@ -554,7 +581,11 @@ int
event_notification_free(event_handle_t handle,
event_notification_t notification)
{
if (!handle || !notification || !notification->elvin_notification) {
if (!notification) {
return 1;
}
if (!handle || !notification->elvin_notification) {
ERROR("invalid parameter\n");
return 0;
}
......@@ -984,7 +1015,7 @@ event_notification_remove(event_handle_t handle,
if (elvin_notification_remove(notification->elvin_notification,
name, handle->status) == 0) {
ERROR("elvin_notification_remove failed: ");
ERROR("elvin_notification_remove of %s failed: ", name);
elvin_error_fprintf(stderr, handle->status);
return 0;
}
......@@ -1135,7 +1166,12 @@ event_subscribe(event_handle_t handle, event_notify_callback_t callback,
! addclause("EVENTTYPE", tuple->eventtype,
&expression[index], sizeof(expression) - index, &index))
return NULL;
if (tuple->timeline &&
! addclause("TIMELINE", tuple->timeline,
&expression[index], sizeof(expression) - index, &index))
return NULL;
index += snprintf(&expression[index], sizeof(expression) - index,
"%s SCHEDULER == %d ",
(index ? "&&" : ""),
......@@ -1212,6 +1248,7 @@ notify_callback(elvin_handle_t server,
/* If MAC does not match, throw it away */
if (handle->keydata &&
event_notification_check_hmac(handle, &notification)) {
ERROR("bad hmac\n");
return;
}
......
/*
* 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.
*/
......@@ -15,18 +15,24 @@
#define __EVENT_H__
#include <stdio.h>
#include <stdarg.h>
#include <elvin/elvin.h>
#ifndef MAXHOSTNAMELEN
#define MAXHOSTNAMELEN 64
#endif /* MAXHOSTNAMELEN */
#ifdef __cplusplus
extern "C" {
#endif
/* Handle to the event server: */
struct event_handle {
elvin_handle_t server;
elvin_error_t status;
unsigned char *keydata;
int keylen;
int do_loop;
/* API function pointers: */
elvin_error_t (*init)(void);
int (*connect)(elvin_handle_t handle, elvin_error_t error);
......@@ -67,6 +73,7 @@ typedef struct _address_tuple {
char *objname; /* link0, cbr0, cbr1, etc ... */
char *eventtype; /* START, STOP, UP, DOWN, etc ... */
int scheduler; /* A dynamic event to schedule */
char *timeline; /* The timeline to schedule under */
} address_tuple, *address_tuple_t;
#define ADDRESSTUPLE_ANY NULL
#define ADDRESSTUPLE_ALL "*"
......@@ -96,6 +103,8 @@ int address_tuple_free(address_tuple_t);
event_notification_get_string(handle, note, "ARGS", buf, len)
#define event_notification_set_arguments(handle, note, buf) \
event_notification_put_string(handle, note, "ARGS", buf)
#define event_notification_get_timeline(handle, note, buf, len) \
event_notification_get_string(handle, note, "TIMELINE", buf, len)
/*
* For dynamic events.
......@@ -161,6 +170,7 @@ int event_unregister(event_handle_t handle);
int event_poll(event_handle_t handle);
int event_poll_blocking(event_handle_t handle, unsigned int timeout);
int event_main(event_handle_t handle);
int event_stop_main(event_handle_t handle);
int event_notify(event_handle_t handle, event_notification_t notification);
int event_schedule(event_handle_t handle, event_notification_t notification,
struct timeval *time);
......@@ -240,4 +250,8 @@ int event_do(event_handle_t handle, ea_tag_t tag, ...);
void *xmalloc(int size);
void *xrealloc(void *p, int size);
#ifdef __cplusplus
}
#endif
#endif /* __EVENT_H__ */
......@@ -65,6 +65,9 @@ class NotificationWrapper:
self.notification,
args)
def getTimeline(self, args):
return event_notification_get_timeline(self.handle, self.notification)
def getSender(self):
return event_notification_get_sender(self.handle, self.notification)
......
......@@ -11,11 +11,12 @@ SUBDIR = event/sched
include $(OBJDIR)/Makeconf
all: event-sched event-sched_rrpc
all: event-sched_rrpc
include $(TESTBED_SRCDIR)/GNUmakerules
CFLAGS += -pthread -DBINDIR='"$(INSTALL_BINDIR)"'
CFLAGS += -g -pthread -DBINDIR='"$(INSTALL_BINDIR)"'
CFLAGS += -DSBINDIR='"$(INSTALL_SBINDIR)"'
#CFLAGS += -DDEBUG
CFLAGS += -O -Wall
CFLAGS += -I. -I${OBJDIR} -I$(SRCDIR)/../lib -I$(TESTBED_SRCDIR)/lib/libtb
......@@ -27,7 +28,8 @@ DBLIBS = -L/usr/local/lib/mysql -lmysqlclient -lz
LIBS += -levent_r -ltb -lcipher -lz
ULXRINC = -I/usr/local/include -I/usr/local/include/ulxmlrpcpp
CXXFLAGS += -pthread -O $(ULXRINC) -I$(OBJDIR)
CXXFLAGS += -pthread -O $(ULXRINC) -I$(OBJDIR) -I$(TESTBED_SRCDIR)/lib/libtb
CXXFLAGS += -I$(SRCDIR)/../lib
ULXRLIBS = -L/usr/local/lib -lulsshxmlrpcpp -lulxmlrpcpp -lexpat
#
......@@ -40,38 +42,50 @@ LIBS += -L/usr/local/lib -lvin4mt -lvin4c -lvin4 -lssl -lcrypto -lm
OBJS = event-sched.o
event-sched: event-sched.c queue.o event-sched.h ../lib/libevent.a
$(CC) $(CFLAGS) -DDBIFACE=1 $(LDFLAGS) -o $@ $< queue.o $(LIBS) $(DBLIBS)
event-sched_rpc: event-sched_rpc.o rpc.o queue.o event-sched.h \
../lib/libevent.a
$(CXX) $(CFLAGS) -static $(LDFLAGS) -o $@ event-sched_rpc.o \
rpc.o queue.o $(ULXRLIBS) $(LIBS)
event-sched_rrpc: event-sched_rpc.o rrpc.o queue.o event-sched.h \
../lib/libevent.a
$(CXX) $(CFLAGS) -static $(LDFLAGS) -o $@ event-sched_rpc.o \
rrpc.o queue.o $(ULXRLIBS) $(LIBS)
version.c: event-sched.c
echo >$@ "char build_info[] = \"Built on `date +%d-%b-%Y` by `id -nu`@`hostname | sed 's/\..*//'`:`pwd`\";"
OBJS = \
error-record.o \
event-sched_rpc.o \
group-agent.o \
listNode.o \
local-agent.o \
node-agent.o \
queue.o \
rrpc.o \
simulator-agent.o \
timeline-agent.o \
version.o
event-sched_rrpc: $(OBJS) event-sched.h ../lib/libevent.a
$(CXX) $(CFLAGS) -static $(LDFLAGS) -o $@ $(OBJS) $(ULXRLIBS) $(LIBS)
queue.o: event-sched.h
event-sched_rpc.o: event-sched.c event-sched.h rpc.h
listNode.o: listNode.h
error-record.o: error-record.h
local-agent.o: local-agent.h
group-agent.o: group-agent.h
event-sched_rpc.o: event-sched.c event-sched.h rpc.h listNode.h
$(CC) $(CFLAGS) -DRPC -c -o $@ $<
rpc.o: rpc.cc rpc.h
$(CXX) $(CXXFLAGS) -DSSHRPC $(ULXRINC) -c $<
rrpc.o: rpc.cc rpc.h
$(CXX) $(CXXFLAGS) -DSSLRPC $(ULXRINC) -c -o rrpc.o $<
$(CXX) -g $(CXXFLAGS) -DSSLRPC $(ULXRINC) -c -o rrpc.o $<
install: event-sched_rrpc
-mkdir -p $(INSTALL_DIR)/opsdir/sbin
$(INSTALL_PROGRAM) event-sched_rrpc $(INSTALL_DIR)/opsdir/sbin/event-sched
$(INSTALL_PROGRAM) $< $(INSTALL_DIR)/opsdir/sbin/event-sched
$(INSTALL) -m 0644 $(SRCDIR)/event-sched.8 \
$(DESTDIR)$(INSTALL_DIR)/opsdir/man/man8/event-sched.8
control-install: event-sched_rrpc
$(INSTALL_PROGRAM) event-sched_rrpc $(INSTALL_SBINDIR)/event-sched
$(INSTALL_PROGRAM) $< $(INSTALL_SBINDIR)/event-sched
# not a client thing
client:
client-install: client
clean:
/bin/rm -f *.o event-sched event-sched_rpc event-sched_rrpc
/bin/rm -f *.o event-sched event-sched_rpc event-sched_rrpc version.c
/*
* EMULAB-COPYRIGHT
* Copyright (c) 2004 University of Utah and the Flux Group.
* All rights reserved.
*/
/**
* @file error-record.c
*
* Implementation of the error-record code.
*/
#include "config.h"
#include <stdio.h>
#include <errno.h>
#include <assert.h>
#include <unistd.h>
#include "tbdefs.h"
#include "popenf.h"
#include "error-record.h"
/**
* Dump a digested version of the program-agent's "status" file to the given
* output. The status file is a 'key=value' formatted file that describes a
* particular invocation of a program agent. The file is written out by the
* agent after the program finishes, pulled back to ops by a 'loghole sync' and
* then gets included in the Simulator's report through this function.
*
* @param er An error record for a failed program-agent invocation.
* @param out The file to write any output to.
* @return Zero on success, -1 otherwise.
*
* @see send_report
*/
static int dump_agent_status(error_record_t er, FILE *out);
/**
* Utility function to pipe the tail of a file to the given output file. After
* the program-agent's "status" file is dumped to the output, the tail of the
* file gets included as well so the user can get an idea of what went wrong.
*
* @param path The path of the file whose tail should be written to the output
* file.
* @param out The file to write any output to.
* @return Zero on success, -1 otherwise.
*
* @see dump_agent_status
*/
static int tail_file(char *path, FILE *out);
error_record_t create_error_record(void)
{
error_record_t retval = NULL;
if ((retval = calloc(1, sizeof(struct _error_record))) == NULL) {
errno = ENOMEM;
}
else {
retval->er_agent = NULL;
retval->er_token = -1;
retval->er_error = 0;
}
return retval;
}
void delete_error_record(error_record_t er)
{
if (er != NULL) {
free(er);
er = NULL;
}
}
int error_record_invariant(error_record_t er)
{
assert(er != NULL);
assert(er->er_agent != NULL);
assert(agent_invariant(er->er_agent));
assert(er->er_token != -1);
return 1;
}
void delete_error_records(struct lnList *list)
{
error_record_t er;
assert(list != NULL);
lnCheck(list);
while ((er = (error_record_t)lnRemHead(list)) != NULL) {
delete_error_record(er);
er = NULL;
}
lnCheck(list);
assert(lnEmptyList(list));
}
static int dump_agent_status(error_record_t er, FILE *out)
{
/*
* The format of the path for the status file:
* logs/<node>/usr/testbed/logs/<agent>.<token>.status
*/
static char *file_format = "logs/%s/usr/testbed/logs/%s.%lu.status";
/*
* A map of status file 'keys' that people may be interested in and
* some human-readable prefix text.
*/
static struct {
char *key;
char *desc;
} status_map[] = {
{ "DIR", " Directory:\t" },
{ "COMMAND", " Command:\t" },
{ "START_TIME", " Started at:\t" },
{ "EXIT_CODE", " Exit code:\t" },
{ "TIMEOUT_FIRED", " Timeout Fired:\t" },
{ NULL, NULL }
};
char buffer[BUFSIZ];
int retval;
FILE *file;
assert(er != NULL);
assert(error_record_invariant(er));
assert(strcmp(er->er_agent->objtype, TBDB_OBJECTTYPE_PROGRAM) == 0);
assert(out != NULL);
snprintf(buffer,
sizeof(buffer),
file_format,
er->er_agent->vnode,
er->er_agent->name,
er->er_token);
/*
* We expect the file to have been brought over already, so just try
* to open out.
*/
if ((file = fopen(buffer, "r")) == NULL) {
fprintf(out, "warning: missing status file: '%s'\n", buffer);
retval = 0;
}
else {
/* Print out a short header, then */
fprintf(out,
"Program agent: '%s' located on node '%s'\n",
er->er_agent->name,
er->er_agent->vnode);
/* ... write out the data most people would care about. */
while (fgets(buffer, sizeof(buffer), file) != NULL) {
char *value;
if ((value = strchr(buffer, '=')) == NULL) {
warning("Bad line in status file: %s\n",
buffer);
}
else {
int lpc;
*value = '\0';
value += 1;
for (lpc = 0; status_map[lpc].key; lpc++) {
if (strcmp(buffer,
status_map[lpc].key) == 0) {
fprintf(out,
"%s%s",
status_map[lpc].desc,
value);
}
}
}
}
fclose(file);
file = NULL;
retval = 0;
}
return retval;
}
static int tail_file(char *path, FILE *out)
{
FILE *tail_in;
int retval;
assert(path != NULL);
assert(strlen(path) > 0);
assert(out != NULL);
if (access(path, R_OK) != 0) {
fprintf(out, "warning: missing log file: '%s'\n", path);
retval = 0; // Non-fatal error...
}
else if ((tail_in = popenf("tail %s", "r", path)) == NULL) {
fprintf(out, "error: cannot tail log '%s'\n", path);
retval = -1;
}
else {
char buffer[1024];
int rc;
/* Print a separator header, */
fprintf(out, ">> Tail of log '%s' <<\n", path);
/* ... the tail of the file, and */
while ((rc = fread(buffer,
1,
sizeof(buffer),
tail_in)) > 0) {
fwrite(buffer, 1, rc, out);
}
if (pclose(tail_in) == -1) {
warning("pclose for tail %s failed\n", path);
}
tail_in = NULL;
/* ... a separator footer. */
memset(buffer, '=', 79);
buffer[79] = '\0';
fprintf(out, "%s\n\n", buffer);
retval = 0;