diff --git a/event/lib/event.h b/event/lib/event.h index 989d8183e43315ea8f639620900d6b4129176f76..3fcf08b24e23eeb49d9bbef853285b1958f9d51e 100644 --- a/event/lib/event.h +++ b/event/lib/event.h @@ -5,7 +5,7 @@ * * @COPYRIGHT@ * - * $Id: event.h,v 1.10 2002-03-05 16:33:41 stoller Exp $ + * $Id: event.h,v 1.11 2002-03-18 18:55:21 stoller Exp $ */ #ifndef __EVENT_H__ @@ -85,6 +85,27 @@ int address_tuple_free(address_tuple_t); #define event_notification_set_arguments(handle, note, buf) \ event_notification_put_string(handle, note, "ARGS", buf) +/* + * For dynamic events. + */ +#define event_notification_clear_host(handle, note) \ + event_notification_remove(handle, note, "HOST") +#define event_notification_set_host(handle, note, buf) \ + event_notification_put_string(handle, note, "HOST", buf) +#define event_notification_clear_objtype(handle, note) \ + event_notification_remove(handle, note, "OBJTYPE") +#define event_notification_set_objtype(handle, note, buf) \ + event_notification_put_string(handle, note, "OBJTYPE", buf) + +/* + * Event library sets this field. Holds the sender of the event, as + * determined by the library when it is initialized. + */ +#define event_notification_get_sender(handle, note, buf, len) \ + event_notification_get_string(handle, note, "___SENDER___", buf, len) +#define event_notification_set_sender(handle, note, buf) \ + event_notification_put_string(handle, note, "___SENDER___", buf) + /* The "any host" string: */ #define EVENT_HOST_ANY "*" diff --git a/event/sched/event-sched.c b/event/sched/event-sched.c index 96c344bff95d6e3718f50977cd601a2a5b525b04..396fa870e58ff83953921ae289fb0b29fd3446a5 100644 --- a/event/sched/event-sched.c +++ b/event/sched/event-sched.c @@ -35,6 +35,16 @@ static int debug; static void cleanup(void); static void quit(int); +#define MAXAGENTS 100 +static struct { + char nodeid[TBDB_FLEN_NODEID]; + char vnode[TBDB_FLEN_VNAME]; + char objname[TBDB_FLEN_EVOBJNAME]; + char objtype[TBDB_FLEN_EVOBJTYPE]; + char ipaddr[32]; +} agents[MAXAGENTS]; +static int numagents; + void usage() { @@ -51,7 +61,7 @@ main(int argc, char **argv) char *server = NULL; char *port = NULL; char *log = NULL; - char buf[BUFSIZ]; + char pideid[BUFSIZ], buf[BUFSIZ]; int c, count; progname = argv[0]; @@ -85,6 +95,7 @@ main(int argc, char **argv) usage(); pid = argv[0]; eid = argv[1]; + sprintf(pideid, "%s/%s", pid, eid); signal(SIGINT, quit); signal(SIGTERM, quit); @@ -138,6 +149,7 @@ main(int argc, char **argv) fatal("could not allocate an address tuple"); } tuple->scheduler = 1; + tuple->expt = pideid; if (event_subscribe(handle, enqueue, tuple, NULL) == NULL) { fatal("could not subscribe to EVENT_SCHEDULE event"); @@ -183,50 +195,81 @@ main(int argc, char **argv) static void enqueue(event_handle_t handle, event_notification_t notification, void *data) { - sched_event_t event; + sched_event_t event; + char objname[TBDB_FLEN_EVOBJNAME]; + int x; /* Clone the event notification, since we want the notification to live beyond the callback function: */ event.notification = elvin_notification_clone(notification, handle->status); if (!event.notification) { - ERROR("elvin_notification_clone failed: "); - elvin_error_fprintf(stderr, handle->status); - return; + error("elvin_notification_clone failed!\n"); + return; } /* Clear the scheduler flag */ if (! event_notification_remove(handle, event.notification, "SCHEDULER") || ! event_notification_put_int32(handle, event.notification, "SCHEDULER", 0)) { - ERROR("could not clear scheduler attribute of notification %p\n", - event.notification); - return; + error("could not clear scheduler attribute of notification %p\n", + event.notification); + goto bad; } /* Get the event's firing time: */ - - if (event_notification_get_int32(handle, event.notification, "time_sec", - (int *) &event.time.tv_sec) - == 0) - { - ERROR("could not get time.tv_sec attribute from notification %p\n", - event.notification); - return; + if (! event_notification_get_int32(handle, event.notification, "time_usec", + (int *) &event.time.tv_usec) || + ! event_notification_get_int32(handle, event.notification, "time_sec", + (int *) &event.time.tv_sec)) { + error("could not get time from notification %p\n", + event.notification); + goto bad; } - if (event_notification_get_int32(handle, event.notification, "time_usec", - (int *) &event.time.tv_usec) - == 0) - { - ERROR("could not get time.tv_usec attribute from notification %p\n", - event.notification); - return; + /* + * Must map the event to the proper agent running on a particular + * node. + */ + if (! event_notification_get_objname(handle, event.notification, + objname, sizeof(objname))) { + error("could not get object name from notification %p\n", + event.notification); + goto bad; + } + for (x = 0; x < numagents; x++) { + if (!strcmp(agents[x].objname, objname)) + break; + } + if (x == numagents) { + error("Could not map object to an agent: %s\n", objname); + goto bad; + } + event_notification_clear_host(handle, event.notification); + event_notification_set_host(handle, + event.notification, agents[x].ipaddr); + event_notification_clear_objtype(handle, event.notification); + event_notification_set_objtype(handle, + event.notification, agents[x].objtype); + + if (debug > 1) { + struct timeval now; + + gettimeofday(&now, NULL); + + info("Sched: note:%p at:%ld:%d now:%ld:%d agent:%d\n", + event.notification, + event.time.tv_sec, event.time.tv_usec, + now.tv_sec, now.tv_usec, + x); } /* Enqueue the event notification for resending at the indicated time: */ sched_event_enqueue(event); + return; + bad: + event_notification_free(handle, event.notification); } /* Returns the amount of time until EVENT fires. */ @@ -284,12 +327,9 @@ dequeue(event_handle_t handle) } if (debug > 1) { - info("firing event (event=(notification=%p, " - "time=(tv_sec=%ld, tv_usec=%ld)) " - "at time (time=(tv_sec=%ld, tv_usec=%ld))\n", - next_event.notification, - next_event.time.tv_sec, - next_event.time.tv_usec, + info("Fire: note:%p at:%ld:%d now:%ld:%d\n", + next_event.notification, + next_event.time.tv_sec, next_event.time.tv_usec, now.tv_sec, now.tv_usec); } @@ -311,28 +351,101 @@ get_static_events(event_handle_t handle) address_tuple_t tuple; char pideid[BUFSIZ]; event_notification_t notification; + int adx = 0; + + /* + * Build up a table of agents that can receive dynamic events. + * Currently, these are trafgens and delay nodes. We want to + * be able to quickly map from "cbr0" to the node on which it + * lives (for dynamic events). + */ + res = mydb_query("select vi.vname,vi.vnode,r.node_id " + " from virt_trafgens as vi " + "left join reserved as r on " + " r.vname=vi.vnode and r.pid=vi.pid and r.eid=vi.eid " + "where vi.role='source' and " + " vi.pid='%s' and vi.eid='%s'", + 3, pid, eid); + + if (!res) { + error("getting virt_trafgens list for %s/%s", pid, eid); + return 0; + } + nrows = mysql_num_rows(res); + while (nrows--) { + row = mysql_fetch_row(res); + + if (!row[0] || !row[1] || !row[2]) + continue; + + strcpy(agents[numagents].objname, row[0]); + strcpy(agents[numagents].vnode, row[1]); + strcpy(agents[numagents].nodeid, row[2]); + strcpy(agents[numagents].objtype, TBDB_OBJECTTYPE_TRAFGEN); + + if (! mydb_nodeidtoip(row[2], agents[numagents].ipaddr)) + continue; + numagents++; + } + mysql_free_result(res); + + res = mydb_query("select d.vname,r.vname,d.node_id from delays as d " + "left join reserved as r on r.node_id=d.node_id " + "where d.pid='%s' and d.eid='%s'", + 3, pid, eid); - res = mydb_query("select ex.time,ex.vnode,ex.vname,ex.arguments," - " ot.type,et.type,i.IP from eventlist as ex " + if (!res) { + error("getting delays list for %s/%s", pid, eid); + return 0; + } + nrows = mysql_num_rows(res); + while (nrows--) { + row = mysql_fetch_row(res); + + if (!row[0] || !row[1] || !row[2]) + continue; + + strcpy(agents[numagents].objname, row[0]); + strcpy(agents[numagents].vnode, row[1]); + strcpy(agents[numagents].nodeid, row[2]); + strcpy(agents[numagents].objtype, TBDB_OBJECTTYPE_LINK); + + if (! mydb_nodeidtoip(row[2], agents[numagents].ipaddr)) + continue; + numagents++; + } + mysql_free_result(res); + + if (debug) { + for (adx = 0; adx < numagents; adx++) { + info("Agent %d: %10s %10s %10s %8s %16s\n", adx, + agents[adx].objname, + agents[adx].objtype, + agents[adx].vnode, + agents[adx].nodeid, + agents[adx].ipaddr); + } + } + + /* + * Now get the eventlist. There should be entries in the + * agents table for anything we find in the list. + */ + res = mydb_query("select ex.idx,ex.time,ex.vnode,ex.vname," + " ex.arguments,ot.type,et.type from eventlist as ex " "left join event_eventtypes as et on " " ex.eventtype=et.idx " "left join event_objecttypes as ot on " " ex.objecttype=ot.idx " - "left join reserved as r on " - " ex.vnode=r.vname and ex.pid=r.pid and ex.eid=r.eid " - "left join nodes as n on r.node_id=n.node_id " - "left join node_types as nt on nt.type=n.type " - "left join interfaces as i on " - " i.node_id=r.node_id and i.iface=nt.control_iface " "where ex.pid='%s' and ex.eid='%s'", 7, pid, eid); -#define EXTIME row[0] -#define EXVNODE row[1] -#define EXVNAME row[2] -#define EXARGS row[3] -#define OBJTYPE row[4] -#define EVTTYPE row[5] -#define IPADDR row[6] +#define EXIDX row[0] +#define EXTIME row[1] +#define EXVNODE row[2] +#define OBJNAME row[3] +#define EXARGS row[4] +#define OBJTYPE row[5] +#define EVTTYPE row[6] if (!res) { error("getting static event list for %s/%s", pid, eid); @@ -364,19 +477,27 @@ get_static_events(event_handle_t handle) row = mysql_fetch_row(res); firetime = atof(EXTIME); - if (debug) - info("EV: %8s %10s %10s %10s %10s %10s %10s\n", - row[0], row[1], row[2], - row[3] ? row[3] : "", - row[4], row[5], - row[6] ? row[6] : ""); + for (adx = 0; adx < numagents; adx++) { + if (!strcmp(agents[adx].objname, OBJNAME)) + break; + } + if (adx == numagents) { + error("Could not map event index %s", EXIDX); + return 0; + } tuple->expt = pideid; - tuple->host = IPADDR; - tuple->objname = EXVNAME; + tuple->host = agents[adx].ipaddr; + tuple->objname = OBJNAME; tuple->objtype = OBJTYPE; tuple->eventtype = EVTTYPE; + if (debug) + info("%8s %10s %10s %10s %10s %10s %10s\n", + EXTIME, EXVNODE, OBJNAME, OBJTYPE, + EVTTYPE, agents[adx].ipaddr, + EXARGS ? EXARGS : ""); + event.notification = event_notification_alloc(handle, tuple); if (! event.notification) { error("could not allocate notification"); diff --git a/event/tbgen/tevc.c b/event/tbgen/tevc.c index 7d0ce3c3b6260d6c75981b9ad16904cee4c0881a..7c0c8ce3d1dd9fe608e5e7a5aa20ce226b52856b 100644 --- a/event/tbgen/tevc.c +++ b/event/tbgen/tevc.c @@ -2,30 +2,44 @@ * This is used to send Testbed events (reboot, ready, etc.) to the * testbed event client. It is intended to be wrapped up by a perl * script that will use the tmcc to figure out where the event server - * lives (host/port) to construct the server string. + * lives (host/port) to construct the server string. + * + * Issues: key and pid/eid. + * valid events, names, types. + * valid arguments. + * vname to ipaddr mapping. */ #include <stdio.h> #include <ctype.h> #include <netdb.h> #include <unistd.h> +#include <time.h> +#include <math.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> +#include "config.h" #include "log.h" - +#include "tbdefs.h" #include "event.h" -static char *progname; +static int debug; void -usage() +usage(char *progname) { - fprintf(stderr, - "Usage: %s [-s server] [-p port] [-i ipaddr] <event>\n", - progname); - exit(-1); + fprintf(stderr, + "Usage: %s [-s server] [-c] event\n" + " %s [-s server] -e pid/eid time objname event [args ...]\n" + " time: 'now' or '+seconds' or [[[[yy]mm]dd]HH]MMss\n" + "Examples:\n" + " %s -e pid/eid now cbr0 set interval_=0.2\n" + " %s -e pid/eid +10 cbr0 start\n" + " %s -e pid/eid +20 cbr0 stop\n", + progname, progname, progname, progname, progname); + exit(-1); } int @@ -34,78 +48,83 @@ main(int argc, char **argv) event_handle_t handle; event_notification_t notification; address_tuple_t tuple; + char *progname; char *server = NULL; char *port = NULL; - char *ipaddr = NULL; - char buf[BUFSIZ], ipbuf[BUFSIZ], *bp; + int control = 0; + char *myeid = NULL; + char buf[BUFSIZ], *bp; + char *evtime = NULL, *objname = NULL, *event; int c; progname = argv[0]; - while ((c = getopt(argc, argv, "s:p:i:")) != -1) { + while ((c = getopt(argc, argv, "ds:p:ce:")) != -1) { switch (c) { + case 'd': + debug++; + break; case 's': server = optarg; break; case 'p': port = optarg; break; - case 'i': - ipaddr = optarg; + case 'c': + control = 1; + break; + case 'e': + myeid = optarg; break; default: - usage(); + usage(progname); } } argc -= optind; argv += optind; - if (argc != 1) - usage(); - - loginit(0, 0); + if (control) { + if (geteuid()) + fatal("Only root can send TBCONTROL events"); + + if (argc != 1) + usage(progname); + + event = argv[0]; + argc -= 1; + argv += 1; + } + else { + if (argc < 3) + usage(progname); - /* - * Uppercase event tags for now. Should be wired in list instead. - */ - bp = argv[0]; - while (*bp) { - *bp = toupper(*bp); - bp++; + evtime = argv[0]; + objname = argv[1]; + event = argv[2]; + + argc -= 3; + argv += 3; } + loginit(0, 0); /* - * Get our IP address. Thats how we name ourselves to the - * Testbed Event System. + * If server is not specified, then it defaults to BOSSNODE. + * This allows the client to work on either users.emulab.net + * or on a client node. */ - if (ipaddr == NULL) { - struct hostent *he; - struct in_addr myip; - - if (gethostname(buf, sizeof(buf)) < 0) { - fatal("could not get hostname"); - } - - if (! (he = gethostbyname(buf))) { - fatal("could not get IP address from hostname: %s", buf); - } - memcpy((char *)&myip, he->h_addr, he->h_length); - strcpy(ipbuf, inet_ntoa(myip)); - ipaddr = ipbuf; - } + if (!server) + server = BOSSNODE; /* * Convert server/port to elvin thing. * * XXX This elvin string stuff should be moved down a layer. */ - if (server) { - snprintf(buf, sizeof(buf), "elvin://%s%s%s", - server, - (port ? ":" : ""), - (port ? port : "")); - server = buf; - } + snprintf(buf, sizeof(buf), "elvin://%s%s%s", + server, + (port ? ":" : ""), + (port ? port : "")); + server = buf; /* * Construct an address tuple for generating the event. @@ -114,16 +133,54 @@ main(int argc, char **argv) if (tuple == NULL) { fatal("could not allocate an address tuple"); } - tuple->objtype = OBJECTTYPE_TESTBED; - tuple->eventtype= argv[0]; - tuple->host = ipaddr; - + /* Register with the event system: */ handle = event_register(server, 0); if (handle == NULL) { fatal("could not register with event system"); } + if (control) { + /* + * Send a control event. + */ + bp = event; + while (*bp) { + *bp = toupper(*bp); + bp++; + } + if (! tbdb_valideventtype(event)) + fatal("Unknown %s event: %s", + OBJECTTYPE_TESTBED, event); + + tuple->objtype = OBJECTTYPE_TESTBED; + tuple->eventtype= event; + } + else { + /* + * A dynamic event. + */ + if (! myeid) + fatal("Must provide pid/eid"); + + bp = event; + while (*bp) { + *bp = toupper(*bp); + bp++; + } + if (!strcmp(event, "SET")) + event = TBDB_EVENTTYPE_MODIFY; + else if (! tbdb_valideventtype(event)) + fatal("Unknown event: %s", event); + + tuple->objname = objname; + tuple->eventtype = event; + tuple->expt = myeid; + tuple->site = ADDRESSTUPLE_ANY; + tuple->host = ADDRESSTUPLE_ANY; + tuple->group = ADDRESSTUPLE_ANY; + } + /* Generate the event */ notification = event_notification_alloc(handle, tuple); @@ -131,8 +188,98 @@ main(int argc, char **argv) fatal("could not allocate notification"); } - if (event_notify(handle, notification) == 0) { - fatal("could not send test event notification"); + /* + * If there are any extra arguments, insert them into + * the notification as an arg string. + * + * XXX For now, uppercase the strings, and remove trailing _. + */ + if (argc) { + buf[0] = NULL; + while (argc) { + if (strlen(*argv) + strlen(buf) >= sizeof(buf)-2) + fatal("Too many event argument strings!"); + + bp = *argv; + while (*bp && *bp != '=') { + *bp = toupper(*bp); + bp++; + } + if (*bp != '=') + fatal("Malformed argument: %s!", *argv); + if (*(bp-1) == '_') + *(bp-1) = NULL; + *bp++ = NULL; + + sprintf(&buf[strlen(buf)], "%s=%s ", *argv, bp); + argc--; + argv++; + } + event_notification_set_arguments(handle, notification, buf); + } + + if (control) { + if (event_notify(handle, notification) == 0) { + fatal("could not send test event notification"); + } + } + else { + struct timeval when; + + /* + * Parse the time. Now is special; set the time to 0. + */ + if (!strcmp(evtime, "now")) { + gettimeofday(&when, NULL); + } + else if (evtime[0] == '+') { + gettimeofday(&when, NULL); + + if (strchr(evtime, '.')) { + double val = atof(evtime); + + when.tv_sec += (int) rint(val); + when.tv_usec += + (int) (1000000 * (val - rint(val))); + + if (when.tv_usec > 1000000) { + when.tv_sec += 1; + when.tv_usec -= 1000000; + } + } + else + when.tv_sec += atoi(evtime); + } + else { + char *format = "%y%m%d%H%M%S"; + int len = strlen(evtime); + struct tm tm; + + if ((len & 1) || (len > strlen(format)) || (len < 4)) + usage(progname); + format += strlen(format) - len; + + gettimeofday(&when, NULL); + localtime_r(&when.tv_sec, &tm); + if (!strptime(evtime, format, &tm)) + usage(progname); + + when.tv_sec = mktime(&tm); + when.tv_usec = 0; + } + + if (debug) { + struct timeval now; + + gettimeofday(&now, NULL); + + info("Now: %ld:%d\n", now.tv_sec, now.tv_usec); + info("When: %ld:%d\n", when.tv_sec, when.tv_usec); + } + + if (event_schedule(handle, notification, &when) == 0) { + fatal("could not send test event notification"); + } } event_notification_free(handle, notification);