diff --git a/db/libdb.pm.in b/db/libdb.pm.in index 1bd2aba99946d85cce70a40db665744e414c1b6d..1e5940292a05b9bc4faf392b0421a80190c64375 100644 --- a/db/libdb.pm.in +++ b/db/libdb.pm.in @@ -2733,6 +2733,7 @@ sub TBGetSiteVar($) "virt_simnode_attributes", "nseconfigs", "eventlist", + "event_groups", "ipsubnets", "nsfiles"); diff --git a/db/xmlconvert.in b/db/xmlconvert.in index 2470c9dd8712cf9828de92518ebd882f9056b3c3..1d67a7aea975aec6a09c6836582633dea94ff6a8 100644 --- a/db/xmlconvert.in +++ b/db/xmlconvert.in @@ -106,8 +106,11 @@ my %virtual_tables = "eventlist" => { rows => undef, tag => "events", row => "event", - attrs => [ "vname" ]}); - + attrs => [ "vname" ]}, + "event_groups" => { rows => undef, + tag => "event_groups", + row => "event_group", + attrs => [ "group_name", "agent-name" ]}); # XXX # The experiment table is special. Only certain fields are allowed to diff --git a/event/lib/event.h b/event/lib/event.h index 236c8afa83dbbc1d3cbcc70566455ebd61962df1..6bdfdf198f3ed607c567fa0f91072e159b6b1e17 100644 --- a/event/lib/event.h +++ b/event/lib/event.h @@ -108,6 +108,10 @@ int address_tuple_free(address_tuple_t); event_notification_remove(handle, note, "OBJTYPE") #define event_notification_set_objtype(handle, note, buf) \ event_notification_put_string(handle, note, "OBJTYPE", buf) +#define event_notification_clear_objname(handle, note) \ + event_notification_remove(handle, note, "OBJNAME") +#define event_notification_set_objname(handle, note, buf) \ + event_notification_put_string(handle, note, "OBJNAME", buf) /* * Event library sets this field. Holds the sender of the event, as diff --git a/event/sched/event-sched.c b/event/sched/event-sched.c index 6c8a19900bdf41eb3948a2ceb2eeb56a11dc3b79..6b7c2cbe1d69fe5eac35c4c5491ff73906b3ec79 100644 --- a/event/sched/event-sched.c +++ b/event/sched/event-sched.c @@ -1,6 +1,6 @@ /* * EMULAB-COPYRIGHT - * Copyright (c) 2000-2003 University of Utah and the Flux Group. + * Copyright (c) 2000-2004 University of Utah and the Flux Group. * All rights reserved. */ @@ -42,6 +42,7 @@ static void cleanup(void); static void quit(int); struct agent { + char name[TBDB_FLEN_EVOBJNAME]; /* Agent *or* group name */ char nodeid[TBDB_FLEN_NODEID]; char vnode[TBDB_FLEN_VNAME]; char objname[TBDB_FLEN_EVOBJNAME]; @@ -173,6 +174,8 @@ main(int argc, char **argv) fatal("could not subscribe to EVENT_SCHEDULE event"); } + goto doit; + /* * Hacky. Need to wait until all nodes in the experiment are * in the ISUP state before we can start the event list rolling. @@ -195,6 +198,8 @@ main(int argc, char **argv) sleep(3); } + doit: + /* * Read the static events list and schedule. */ @@ -220,82 +225,105 @@ enqueue(event_handle_t handle, event_notification_t notification, void *data) { 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 = event_notification_clone(handle, notification); - if (!event.notification) { - error("event_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); - goto bad; - } + int x, sent = 0; /* Get the event's firing time: */ - if (! event_notification_get_int32(handle, event.notification, "time_usec", + if (! event_notification_get_int32(handle, notification, "time_usec", (int *) &event.time.tv_usec) || - ! event_notification_get_int32(handle, event.notification, "time_sec", + ! event_notification_get_int32(handle, notification, "time_sec", (int *) &event.time.tv_sec)) { error("could not get time from notification %p\n", - event.notification); - goto bad; + notification); + return; } /* * Must map the event to the proper agent running on a particular - * node. + * node. Might be multiple agents if its a group event; send a cloned + * event for each one. */ - if (! event_notification_get_objname(handle, event.notification, + if (! event_notification_get_objname(handle, 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; + notification); + return; } - 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); - event_notification_insert_hmac(handle, event.notification); - - event.simevent = !strcmp(agents[x].objtype, TBDB_OBJECTTYPE_SIMULATOR); - - if (debug > 1) { - struct timeval now; + for (x = 0; x < numagents; x++) { + if (!strcmp(agents[x].name, objname)) { + /* + * Clone the event notification, since we want the + * notification to live beyond the callback function: + */ + event.notification = + event_notification_clone(handle, notification); + + if (!event.notification) { + error("event_notification_clone failed!\n"); + return; + } + + /* + * Clear the scheduler flag. Not allowed to do this above + * the loop cause the notification thats comes in is + * "read-only". + */ + 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; + } + + 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); + /* + * Indicates a group event; must change the name of the + * event to the underlying agent. + */ + if (strcmp(agents[x].objname, agents[x].name)) { + event_notification_clear_objname(handle, + event.notification); + event_notification_set_objname(handle, + event.notification, agents[x].objname); + } + + event_notification_insert_hmac(handle, event.notification); + event.simevent = !strcmp(agents[x].objtype, + TBDB_OBJECTTYPE_SIMULATOR); + + if (debug > 1) { + struct timeval now; - gettimeofday(&now, NULL); + 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); + 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); + sent++; + } + } + if (!sent) { + error("Could not map object to an agent: %s\n", objname); } - - /* Enqueue the event notification for resending at the indicated - time: */ - sched_event_enqueue(event); - return; - bad: - event_notification_free(handle, event.notification); } /* Dequeue events from the event queue and fire them at the @@ -347,7 +375,7 @@ dequeue(event_handle_t handle) static int get_static_events(event_handle_t handle) { - MYSQL_RES *res; + MYSQL_RES *res, *agent_res, *group_res; MYSQL_ROW row; int nrows; struct timeval now, time; @@ -366,32 +394,50 @@ get_static_events(event_handle_t handle) * That is, 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,o.type " - " from virt_agents as vi " - "left join reserved as r on " - " r.vname=vi.vnode and r.pid=vi.pid and r.eid=vi.eid " - "left join event_objecttypes as o on " - " o.idx=vi.objecttype " - "where vi.pid='%s' and vi.eid='%s'", - 4, pid, eid); - - if (!res) { + agent_res = + mydb_query("select vi.vname,vi.vnode,r.node_id,o.type " + " from virt_agents as vi " + "left join reserved as r on " + " r.vname=vi.vnode and r.pid=vi.pid and " + " r.eid=vi.eid " + "left join event_objecttypes as o on " + " o.idx=vi.objecttype " + "where vi.pid='%s' and vi.eid='%s'", + 4, pid, eid); + + if (!agent_res) { error("getting virt_agents list for %s/%s", pid, eid); return 0; } - nrows = mysql_num_rows(res); - agents = calloc(nrows, sizeof(struct agent)); + + /* + * We also need the event_groups table so we know how many entries. + */ + group_res = + mydb_query("select group_name,agent_name from event_groups " + "where pid='%s' and eid='%s'", + 2, pid, eid); + + if (!group_res) { + error("getting virt_groups list for %s/%s", pid, eid); + return 0; + } + + agents = calloc(mysql_num_rows(agent_res) + mysql_num_rows(group_res), + sizeof(struct agent)); if (agents == NULL) { - error("cannot allocate memory, too many agents (%d)\n", nrows); + error("cannot allocate memory, too many agents/groups\n"); return 0; } + nrows = mysql_num_rows(agent_res); while (nrows--) { - row = mysql_fetch_row(res); + row = mysql_fetch_row(agent_res); if (!row[0] || !row[1] || !row[3]) continue; + strcpy(agents[numagents].name, row[0]); strcpy(agents[numagents].objname, row[0]); strcpy(agents[numagents].vnode, row[1]); strcpy(agents[numagents].objtype, row[3]); @@ -425,7 +471,7 @@ get_static_events(event_handle_t handle) } numagents++; } - mysql_free_result(res); + mysql_free_result(agent_res); if (debug) { for (adx = 0; adx < numagents; adx++) { @@ -438,6 +484,50 @@ get_static_events(event_handle_t handle) } } + /* + * Now get the group list. To make life simple, I just create + * new entries in the agents table, named by the group name, but + * with the info from the underlying agent duplicated, for each + * member of the group. + */ + nrows = mysql_num_rows(group_res); + while (nrows--) { + row = mysql_fetch_row(group_res); + + /* + * Find the agent entry. + */ + for (adx = 0; adx < numagents; adx++) { + if (!strcmp(agents[adx].name, row[1])) + break; + } + if (adx == numagents) { + error("Could not find group event %s/%s", + row[0], row[1]); + return 0; + } + + /* + * Copy the entry, but rename it to the group name. + */ + memcpy((void *)&agents[numagents], (void *)&agents[adx], + sizeof(struct agent)); + strcpy(agents[numagents].name, row[0]); + numagents++; + } + mysql_free_result(group_res); + + if (debug) { + for (adx = 0; adx < numagents; adx++) { + if (!strcmp(agents[adx].objname, agents[adx].name)) + continue; + + info("Group %d: %10s %10s\n", adx, + agents[adx].name, + agents[adx].objname); + } + } + /* * Now get the eventlist. There should be entries in the * agents table for anything we find in the list. @@ -491,7 +581,7 @@ get_static_events(event_handle_t handle) firetime = atof(EXTIME); for (adx = 0; adx < numagents; adx++) { - if (!strcmp(agents[adx].objname, OBJNAME)) + if (!strcmp(agents[adx].name, OBJNAME)) break; } if (adx == numagents) { diff --git a/sql/database-create.sql b/sql/database-create.sql index 0d53a4ba3e68c85e85f946726c666ccfef2a56ce..1883f8424cd00da118dc8e33a5ca51a56e46e3bc 100644 --- a/sql/database-create.sql +++ b/sql/database-create.sql @@ -157,6 +157,21 @@ CREATE TABLE event_objecttypes ( PRIMARY KEY (idx) ) TYPE=MyISAM; +-- +-- Table structure for table `event_groups` +-- + +CREATE TABLE event_groups ( + pid varchar(12) NOT NULL default '', + eid varchar(32) NOT NULL default '', + idx int(10) unsigned NOT NULL auto_increment, + group_name varchar(64) NOT NULL default '', + agent_name varchar(64) NOT NULL default '', + PRIMARY KEY (pid,eid,idx), + KEY group_name (group_name), + KEY agent_name (agent_name) +) TYPE=MyISAM; + -- -- Table structure for table `eventlist` -- @@ -170,6 +185,7 @@ CREATE TABLE eventlist ( vname varchar(64) NOT NULL default '', objecttype smallint(5) unsigned NOT NULL default '0', eventtype smallint(5) unsigned NOT NULL default '0', + isgroup tinyint(1) unsigned default '0', arguments text, atstring text, PRIMARY KEY (pid,eid,idx), @@ -1710,6 +1726,7 @@ CREATE TABLE virt_lans ( emulated tinyint(4) default '0', uselinkdelay tinyint(4) default '0', nobwshaping tinyint(4) default '0', + mustdelay tinyint(1) default '0', usevethiface tinyint(4) default '0', trivial_ok tinyint(4) default '1', protocol varchar(30) NOT NULL default 'ethernet', diff --git a/sql/database-fill.sql b/sql/database-fill.sql index 9b628974e3ee1aadb58fc3a01122536ee845a8ad..c05aa35c622d5282564c2e3752c4c39931355b4e 100644 --- a/sql/database-fill.sql +++ b/sql/database-fill.sql @@ -539,6 +539,7 @@ REPLACE INTO table_regex VALUES ('virt_lans','widearea','int','redirect','defaul REPLACE INTO table_regex VALUES ('virt_lans','emulated','int','redirect','default:boolean',0,0,NULL); REPLACE INTO table_regex VALUES ('virt_lans','uselinkdelay','int','redirect','default:boolean',0,0,NULL); REPLACE INTO table_regex VALUES ('virt_lans','nobwshaping','int','redirect','default:boolean',0,0,NULL); +REPLACE INTO table_regex VALUES ('virt_lans','mustdelay','int','redirect','default:boolean',0,0,NULL); REPLACE INTO table_regex VALUES ('virt_lans','usevethiface','int','redirect','default:boolean',0,0,NULL); REPLACE INTO table_regex VALUES ('virt_lans','trivial_ok','int','redirect','default:boolean',0,0,NULL); REPLACE INTO table_regex VALUES ('virt_node_desires','pid','text','redirect','projects:pid',0,0,NULL); @@ -634,6 +635,10 @@ REPLACE INTO table_regex VALUES ('virt_lan_member_settings','capkey','text','red REPLACE INTO table_regex VALUES ('virt_lan_member_settings','capval','text','redirect','virt_lan_settings:capval',0,0,NULL); REPLACE INTO table_regex VALUES ('virt_lans','est_bandwidth','int','redirect','default:int',0,2147483647,NULL); REPLACE INTO table_regex VALUES ('virt_lans','rest_bandwidth','int','redirect','default:int',0,2147483647,NULL); +REPLACE INTO table_regex VALUES ('event_groups','pid','text','redirect','projects:pid',0,0,NULL); +REPLACE INTO table_regex VALUES ('event_groups','eid','text','redirect','experiments:eid',0,0,NULL); +REPLACE INTO table_regex VALUES ('event_groups','group_name','text','redirect','eventlist:vname',0,0,NULL); +REPLACE INTO table_regex VALUES ('event_groups','agent_name','text','redirect','eventlist:vname',0,0,NULL); -- -- Dumping data for table `testsuite_preentables` diff --git a/sql/database-migrate.txt b/sql/database-migrate.txt index 092636f7a08fe644fa90c73f4155edaead382aa1..13659a5635e32ec06d02c2bde45bedbd95c78af0 100644 --- a/sql/database-migrate.txt +++ b/sql/database-migrate.txt @@ -1778,3 +1778,42 @@ last_net_act,last_cpu_act,last_ext_act); 1.267: Remove table definition that snuck in while developing; skip to next entry; + +1.268: Add event_groups table to allow users to define groups of + targets for events. The agent_name refers to an entry in the + virt_agents table. All members of an eventgroup must of course + be of the same type. I am not currently enforcing this. (note + that the vnode slot of the eventlist table was effectively + deprecated quite some time ago; the event scheduler uses the + vnode slot of the virt_agents entry instead). + + CREATE TABLE event_groups ( + pid varchar(12) NOT NULL default '', + eid varchar(32) NOT NULL default '', + idx int(10) unsigned NOT NULL auto_increment, + group_name varchar(64) NOT NULL default '', + agent_name varchar(64) NOT NULL default '', + PRIMARY KEY (pid,eid,idx), + KEY group_name (group_name), + KEY agent_name (agent_name) + ) TYPE=MyISAM; + + + Also add a boolean to the eventlist table to mark an event as a + group event. + + alter table eventlist add isgroup tinyint(1) unsigned \ + NOT NULL default '0' after eventtype; + + Add mustdelay boolean to virt_lans to relieve assign_wrapper + from the chore of guessing when a delay node needs to be + inserted; assign_wrapper can still override of course, but this + should make it less error prone. + + alter table virt_lans add mustdelay tinyint(1) unsigned \ + default '0' after nobwshaping; + update virt_lans set mustdelay=q_red; + + Then run: + + ./mustdelay.pl diff --git a/sql/mustdelay.pl b/sql/mustdelay.pl new file mode 100755 index 0000000000000000000000000000000000000000..a391df08a7137ab05f0ba1dc0df51d4737a06696 --- /dev/null +++ b/sql/mustdelay.pl @@ -0,0 +1,28 @@ +#!/usr/bin/perl -w +# +# EMULAB-COPYRIGHT +# Copyright (c) 2000-2004 University of Utah and the Flux Group. +# All rights reserved. +# +use English; + +use lib "/usr/testbed/lib"; +use libdb; +use libtestbed; + +# +# Untaint the path +# +$ENV{'PATH'} = '/bin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin'; +delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'}; + +$query_result = + DBQueryFatal("select distinct ex.pid,ex.eid,vname 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 ". + "where ot.type='LINK'"); + +while (($pid,$eid,$lan) = $query_result->fetchrow_array()) { + DBQueryFatal("update virt_lans set mustdelay=1 ". + "where pid='$pid' and eid='$eid' and vname='$lan'"); +} diff --git a/tbsetup/assign_wrapper.in b/tbsetup/assign_wrapper.in index d4869aa34d7c050902c072a42cf1b8ca637a3452..f7c53758a0a5530f246fe8d4c40151d1093feefa 100644 --- a/tbsetup/assign_wrapper.in +++ b/tbsetup/assign_wrapper.in @@ -3469,11 +3469,12 @@ sub LoadVirtLans() my $useveth = $rowref->{"usevethiface"}; my $trivial_ok = $rowref->{"trivial_ok"}; my $protocol = $rowref->{"protocol"}; + my $mustdelay = $rowref->{"mustdelay"}; # Extend the DB info with this stuff: # # If RED, must insert traffic shapping. - $virt_lans{$vname}->{"MUSTDELAY"} = $rowref->{"q_red"}; + $virt_lans{$vname}->{"MUSTDELAY"} = $mustdelay; # User has requested the link/lan be emulated. Not typical. $virt_lans{$vname}->{"EMULATED"} = $isemulated; # User has requested "endnodeshaping" (dummynet on end nodes). @@ -3592,21 +3593,6 @@ sub LoadVirtLans() "$rdelay $rbandwidth $rlossrate\n"; printdb " $port:$vname is a lan of $node\n"; } - - # - # Check event list. Anytime we find an event to control a link, we need - # to drop a delay node in. start/stop especially, since thats the easiest - # way to do that, even if the link has no other traffic shaping in it. - # - printdb "Checking events for LINK commands.\n"; - $result = - DBQueryFatal("select distinct vname 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 ". - "where ot.type='LINK' and ex.pid='$pid' and ex.eid='$eid'"); - while (($vname) = $result->fetchrow_array) { - $virt_lans{$vname}->{"MUSTDELAY"} = 1; - } } sub virtlanexists($) { return exists($virt_lans{$_[0]}); } sub virtlanname($) { return $virt_lans{$_[0]}->{"VNAME"}; } diff --git a/tbsetup/ns2ir/GNUmakefile.in b/tbsetup/ns2ir/GNUmakefile.in index 4868152795c4ad2c698019e8f87ac656c73d2196..9f9660d69434b676c03a0dab3b468caadc6424f1 100644 --- a/tbsetup/ns2ir/GNUmakefile.in +++ b/tbsetup/ns2ir/GNUmakefile.in @@ -16,7 +16,7 @@ include $(OBJDIR)/Makeconf LIB_STUFF = lanlink.tcl node.tcl sim.tcl tb_compat.tcl null.tcl \ nsobject.tcl traffic.tcl vtype.tcl parse.tcl program.tcl \ - nsenode.tcl nstb_compat.tcl + nsenode.tcl nstb_compat.tcl event.tcl BOSSLIBEXEC = parse-ns USERLIBEXEC = parse.proxy diff --git a/tbsetup/ns2ir/event.tcl b/tbsetup/ns2ir/event.tcl new file mode 100644 index 0000000000000000000000000000000000000000..1b3273a7cc1228a0662ccbfddd1e9cd4d8131cf8 --- /dev/null +++ b/tbsetup/ns2ir/event.tcl @@ -0,0 +1,100 @@ +# -*- tcl -*- +# +# EMULAB-COPYRIGHT +# Copyright (c) 2004 University of Utah and the Flux Group. +# All rights reserved. +# + +###################################################################### +# +# Event group support. +# +###################################################################### + +Class EventGroup -superclass NSObject + +namespace eval GLOBALS { + set new_classes(EventGroup) {} +} + +EventGroup instproc init {s} { + global ::GLOBALS::last_class + + $self set sim $s + $self set mytype {} + + $self instvar members + array set members {} + + # Link simulator to this new object. + $s add_eventgroup $self + + set ::GLOBALS::last_class $self +} + +EventGroup instproc rename {old new} { + $self instvar sim + + $sim rename_eventgroup $old $new +} + +# +# Add members to the event group. +# +EventGroup instproc add {args} { + $self instvar members + $self instvar mytype + + foreach obj $args { + if {[$obj info class] == "Lan" || [$obj info class] == "Link"} { + set thisclass "LanLink" + } else { + set thisclass [$obj info class] + } + + if {$mytype == {}} { + set mytype $thisclass + } + + if {$thisclass != $mytype} { + perror "\[$self add $obj] All members must be of the same type!" + return + } + set members($obj) {} + } +} + +# +# Return list of member objects +# +EventGroup instproc members {} { + $self instvar members + + return [array names members] +} + +# updatedb DB +EventGroup instproc updatedb {DB} { + var_import ::GLOBALS::pid + var_import ::GLOBALS::eid + $self instvar members + $self instvar sim + + if {[array size members] == 0} { + perror "\[updatedb] $self has no member list." + return + } + + foreach member [array names members] { + if {[$member info class] == "Queue"} { + set agent_name [$member agent_name] + } else { + set agent_name $member + } + + set names [list "group_name" "agent_name"] + set vals [list $self $agent_name] + $sim spitxml_data "event_groups" $names $vals + } +} + diff --git a/tbsetup/ns2ir/lanlink.tcl b/tbsetup/ns2ir/lanlink.tcl index d14e2b473cdd828a87df10f50acec805da601d8f..fd958f86e471c67af83496b788e3545e20dcf8e4 100644 --- a/tbsetup/ns2ir/lanlink.tcl +++ b/tbsetup/ns2ir/lanlink.tcl @@ -32,7 +32,9 @@ SimplexLink instproc init {link dir} { SimplexLink instproc queue {} { $self instvar mylink $self instvar mydir - return [$mylink set ${mydir}queue] + + set myqueue [$mylink set ${mydir}queue] + return $myqueue } LLink instproc init {lan node} { $self set mylan $lan @@ -49,14 +51,10 @@ LLink instproc queue {} { # Don't need any rename procs since these never use their own name and # can not be generated during Link creation. -Queue instproc init {link type dir} { +Queue instproc init {link type node} { $self set mylink $link + $self set mynode $node - - # direction is either "to" indicating src to dst or "from" indicating - # the dst to src. I.e. to dst or from dst. - $self set direction $dir - # These control whether the link was created RED or GRED. It # filters through the DB. $self set gentle_ 0 @@ -83,15 +81,23 @@ Queue instproc init {link type dir} { if {$type == "RED"} { set red_ 1 + $link mustdelay } elseif {$type == "GRED"} { set red_ 1 set gentle_ 1 + $link mustdelay } elseif {$type != "DropTail"} { punsup "Link type $type, using DropTail!" } } } +Queue instproc rename {old new} { + $self instvar mylink + + $mylink rename_queue $old $new +} + Queue instproc rename_lanlink {old new} { $self instvar mylink @@ -104,19 +110,20 @@ Queue instproc get_link {} { return $mylink } -# Hacky. Need to create an association bewteen the queue direction -# and a dummynet pipe. This should happen later on, but I do not -# have time right now to make all the changes. Instead, convert -# "to" to "pipe0" and "from" to "pipe1". -Queue instproc get_pipe {} { - $self instvar direction - - if {$direction == "to"} { - set pipe "pipe0" - } else { - set pipe "pipe1" - } - return $pipe +Queue instproc agent_name {} { + $self instvar mylink + $self instvar mynode + + return "$mylink-$mynode" +} + +# +# A queue is associated with a node on a link. Return that node. +# +Queue instproc get_node {} { + $self instvar mynode + + return $mynode } Link instproc init {s nodes bw d type} { @@ -134,8 +141,8 @@ Link instproc init {s nodes bw d type} { var_import GLOBALS::new_counter set q1 q[incr new_counter] - Queue to$q1 $self $type to - Queue from$q1 $self $type from + Queue to$q1 $self $type $src + Queue from$q1 $self $type $dst $self set toqueue to$q1 $self set fromqueue from$q1 @@ -172,6 +179,13 @@ LanLink instproc init {s nodes bw d type} { # Allow user to turn off actual bw shaping on emulated links. $self set nobwshaping 0 + # mustdelay; force a delay (or linkdelay) to be inserted. assign_wrapper + # is free to override this, but not sure why it want to! When used in + # conjunction with nobwshaping, you get a delay node, but with no ipfw + # limits on the bw part, and assign_wrapper ignores the bw when doing + # assignment. + $self set mustdelay 0 + # Allow user to turn on veth devices on emulated links. $self set useveth 0 @@ -242,11 +256,19 @@ LanLink instproc init {s nodes bw d type} { lappend nodelist $nodepair set lq q[incr new_counter] - Queue lq$lq $self $type to + Queue lq$lq $self $type $node set linkq($nodepair) lq$lq } } +# +# Set the mustdelay flag. +# +LanLink instproc mustdelay {} { + $self instvar mustdelay + set mustdelay 1 +} + # get_port # This takes a node and returns the port that the node is connected # to the LAN with. If a node is in a LAN multiple times for some @@ -421,7 +443,6 @@ LanLink instproc cost {c} { } } - Link instproc rename {old new} { $self next $old $new @@ -431,6 +452,19 @@ Link instproc rename {old new} { $fromqueue rename_lanlink $old $new } +Link instproc rename_queue {old new} { + $self next $old $new + + $self instvar toqueue + $self instvar fromqueue + + if {$old == $toqueue} { + set toqueue $new + } elseif {$old == $fromqueue} { + set fromqueue $new + } +} + # The following methods are for renaming objects (see README). LanLink instproc rename {old new} { $self instvar nodelist @@ -449,6 +483,7 @@ LanLink instproc rename_node {old new} { $self instvar rbandwidth $self instvar rdelay $self instvar rloss + $self instvar linkq $self instvar accesspoint # XXX Temporary @@ -472,16 +507,32 @@ LanLink instproc rename_node {old new} { set rbandwidth($newnodeport) $rbandwidth($nodeport) set rdelay($newnodeport) $rdelay($nodeport) set rloss($newnodeport) $rloss($nodeport) + set linkq($newnodepair) linkq($nodeport) + unset bandwidth($nodeport) unset delay($nodeport) unset loss($nodeport) unset rbandwidth($nodeport) unset rdelay($nodeport) unset rloss($nodeport) + unset linkq($nodeport) } set nodelist $newnodelist } +LanLink instproc rename_queue {old new} { + $self instvar nodelist + $self instvar linkq + + foreach nodeport $nodelist { + set foo linkq($nodeport) + + if {$foo == $old} { + set linkq($nodeport) $new + } + } +} + Link instproc updatedb {DB} { $self instvar toqueue $self instvar fromqueue @@ -507,6 +558,7 @@ Link instproc updatedb {DB} { $self instvar sim $self instvar netmask $self instvar protocol + $self instvar mustdelay if {$protocol != "ethernet"} { perror "Link must be an ethernet only, not a $protocol" @@ -566,7 +618,7 @@ Link instproc updatedb {DB} { set nodeportraw [join $nodeport ":"] - set fields [list "vname" "member" "mask" "delay" "rdelay" "bandwidth" "rbandwidth" "lossrate" "rlossrate" "cost" "widearea" "emulated" "uselinkdelay" "nobwshaping" "usevethiface" "q_limit" "q_maxthresh" "q_minthresh" "q_weight" "q_linterm" "q_qinbytes" "q_bytes" "q_meanpsize" "q_wait" "q_setbit" "q_droptail" "q_red" "q_gentle" "trivial_ok" "vnode" "vport" "ip"] + set fields [list "vname" "member" "mask" "delay" "rdelay" "bandwidth" "rbandwidth" "lossrate" "rlossrate" "cost" "widearea" "emulated" "uselinkdelay" "nobwshaping" "usevethiface" "q_limit" "q_maxthresh" "q_minthresh" "q_weight" "q_linterm" "q_qinbytes" "q_bytes" "q_meanpsize" "q_wait" "q_setbit" "q_droptail" "q_red" "q_gentle" "trivial_ok" "vnode" "vport" "ip" "mustdelay"] # Treat estimated bandwidths differently - leave them out of the lists # unless the user gave a value - this way, they get the defaults if not @@ -579,7 +631,7 @@ Link instproc updatedb {DB} { lappend fields "rest_bandwidth" } - set values [list $self $nodeportraw $netmask $delay($nodeport) $rdelay($nodeport) $bandwidth($nodeport) $rbandwidth($nodeport) $loss($nodeport) $rloss($nodeport) $cost($nodeport) $widearea $emulated $uselinkdelay $nobwshaping $useveth $limit_ $maxthresh_ $thresh_ $q_weight_ $linterm_ ${queue-in-bytes_} $bytes_ $mean_pktsize_ $wait_ $setbit_ $droptail_ $red_ $gentle_ $trivial_ok $node $port $ip] + set values [list $self $nodeportraw $netmask $delay($nodeport) $rdelay($nodeport) $bandwidth($nodeport) $rbandwidth($nodeport) $loss($nodeport) $rloss($nodeport) $cost($nodeport) $widearea $emulated $uselinkdelay $nobwshaping $useveth $limit_ $maxthresh_ $thresh_ $q_weight_ $linterm_ ${queue-in-bytes_} $bytes_ $mean_pktsize_ $wait_ $setbit_ $droptail_ $red_ $gentle_ $trivial_ok $node $port $ip $mustdelay] if { [info exists ebandwidth($nodeport)] } { lappend values $ebandwidth($nodeport) @@ -621,6 +673,7 @@ Lan instproc updatedb {DB} { $self instvar accesspoint $self instvar settings $self instvar member_settings + $self instvar mustdelay if {$modelnet_cores > 0 || $modelnet_edges > 0} { perror "Lans are not allowed when using modelnet; just duplex links." @@ -692,7 +745,7 @@ Lan instproc updatedb {DB} { set is_accesspoint 1 } - set fields [list "vname" "member" "mask" "delay" "rdelay" "bandwidth" "rbandwidth" "lossrate" "rlossrate" "cost" "widearea" "emulated" "uselinkdelay" "nobwshaping" "usevethiface" "q_limit" "q_maxthresh" "q_minthresh" "q_weight" "q_linterm" "q_qinbytes" "q_bytes" "q_meanpsize" "q_wait" "q_setbit" "q_droptail" "q_red" "q_gentle" "trivial_ok" "protocol" "is_accesspoint" "vnode" "vport" "ip"] + set fields [list "vname" "member" "mask" "delay" "rdelay" "bandwidth" "rbandwidth" "lossrate" "rlossrate" "cost" "widearea" "emulated" "uselinkdelay" "nobwshaping" "usevethiface" "q_limit" "q_maxthresh" "q_minthresh" "q_weight" "q_linterm" "q_qinbytes" "q_bytes" "q_meanpsize" "q_wait" "q_setbit" "q_droptail" "q_red" "q_gentle" "trivial_ok" "protocol" "is_accesspoint" "vnode" "vport" "ip" "mustdelay"] # Treat estimated bandwidths differently - leave them out of the lists # unless the user gave a value - this way, they get the defaults if not @@ -705,7 +758,7 @@ Lan instproc updatedb {DB} { lappend fields "rest_bandwidth" } - set values [list $self $nodeportraw $netmask $delay($nodeport) $rdelay($nodeport) $bandwidth($nodeport) $rbandwidth($nodeport) $loss($nodeport) $rloss($nodeport) $cost($nodeport) $widearea $emulated $uselinkdelay $nobwshaping $useveth $limit_ $maxthresh_ $thresh_ $q_weight_ $linterm_ ${queue-in-bytes_} $bytes_ $mean_pktsize_ $wait_ $setbit_ $droptail_ $red_ $gentle_ $trivial_ok $protocol $is_accesspoint $node $port $ip] + set values [list $self $nodeportraw $netmask $delay($nodeport) $rdelay($nodeport) $bandwidth($nodeport) $rbandwidth($nodeport) $loss($nodeport) $rloss($nodeport) $cost($nodeport) $widearea $emulated $uselinkdelay $nobwshaping $useveth $limit_ $maxthresh_ $thresh_ $q_weight_ $linterm_ ${queue-in-bytes_} $bytes_ $mean_pktsize_ $wait_ $setbit_ $droptail_ $red_ $gentle_ $trivial_ok $protocol $is_accesspoint $node $port $ip $mustdelay] if { [info exists ebandwidth($nodeport)] } { lappend values $ebandwidth($nodeport) diff --git a/tbsetup/ns2ir/parse.tcl.in b/tbsetup/ns2ir/parse.tcl.in index d6ba6717d80d69004b9d326aca26a154fe53136c..03716f6078107351ae9052de3ffdb6454dc7f774 100644 --- a/tbsetup/ns2ir/parse.tcl.in +++ b/tbsetup/ns2ir/parse.tcl.in @@ -264,6 +264,7 @@ source ${GLOBALS::libdir}/null.tcl source ${GLOBALS::libdir}/traffic.tcl source ${GLOBALS::libdir}/vtype.tcl source ${GLOBALS::libdir}/program.tcl +source ${GLOBALS::libdir}/event.tcl ################################################## # Redifing Assignment diff --git a/tbsetup/ns2ir/sim.tcl.in b/tbsetup/ns2ir/sim.tcl.in index d36051290c40e881004baabb9d0c5a7bc85a3eb9..0ea72e88982101e7113b51faa5a5394202afce58 100644 --- a/tbsetup/ns2ir/sim.tcl.in +++ b/tbsetup/ns2ir/sim.tcl.in @@ -23,6 +23,7 @@ Class Simulator Class Program -superclass NSObject +Class EventGroup -superclass NSObject Simulator instproc init {args} { # A counter for internal ids @@ -62,6 +63,10 @@ Simulator instproc init {args} { $self instvar prog_list; array set prog_list {} + # EventGroup list. + $self instvar eventgroup_list; + array set eventgroup_list {} + var_import ::GLOBALS::last_class set last_class $self } @@ -226,6 +231,7 @@ Simulator instproc run {} { $self instvar node_list $self instvar event_list $self instvar prog_list + $self instvar eventgroup_list $self instvar simulated $self instvar nseconfig var_import ::GLOBALS::pid @@ -362,6 +368,9 @@ Simulator instproc run {} { foreach prog [array names prog_list] { $prog updatedb "sql" } + foreach egroup [array names eventgroup_list] { + $egroup updatedb "sql" + } set fields [list "mem_usage" "cpu_usage" "forcelinkdelays" "uselinkdelays" "usewatunnels" "uselatestwadata" "wa_delay_solverweight" "wa_bw_solverweight" "wa_plr_solverweight" "veth_encapsulate" "allowfixnode"] set values [list $mem_usage $cpu_usage $forcelinkdelays $uselinkdelays $usewatunnels $uselatestwadata $wa_delay_solverweight $wa_bw_solverweight $wa_plr_solverweight $veth_encapsulate $fix_current_resources] @@ -501,6 +510,23 @@ Simulator instproc at {time eventstring} { set atstring "$event" set args {} set okargs 0 + + # + # Groups are special. It would be nice to optimize the event groups + # so that a single event could be stored in the DB, but some events + # take arguments based on the actual object being controlled. For now + # I will not try to optimize for when they can be lumped, but rather + # will just turn all group events into a list of individual events. + # + if {[$obj info class] == "EventGroup"} { + set cmd [lrange $event 1 end] + + foreach member [$obj members] { + $self at $time "$member $cmd" + } + return + } + switch -- [$obj info class] { "Application/Traffic/CBR" { set otype TRAFGEN @@ -571,7 +597,8 @@ Simulator instproc at {time eventstring} { set vnode [$obj get_node] set vname $obj } - "Link" { + "Link" - + "Lan" { set otype LINK switch -- $cmd { "up" {set etype UP} @@ -615,11 +642,13 @@ Simulator instproc at {time eventstring} { } set vnode {} set vname $obj + $obj mustdelay } "Queue" { set otype LINK - set pipe [$obj get_pipe] - set obj [$obj get_link] + set node [$obj get_node] + set lanlink [$obj get_link] + $lanlink mustdelay switch -- $cmd { "set" { if {[llength $event] < 4} { @@ -662,9 +691,8 @@ Simulator instproc at {time eventstring} { return } } - set args "PIPE=$pipe $args" set vnode {} - set vname $obj + set vname "$lanlink-$node" } "Program" { set otype PROGRAM @@ -817,6 +845,12 @@ Simulator instproc rename_program {old new} { set prog_list($new) {} } +Simulator instproc rename_eventgroup {old new} { + $self instvar eventgroup_list + unset eventgroup_list($old) + set eventgroup_list($new) {} +} + # find_link # This is just an accesor to the link_map datastructure. If no # link is known between and the empty list is returned. @@ -972,6 +1006,13 @@ Simulator instproc add_program {prog} { set prog_list($prog) {} } +# add_eventgroup +# Link to a EventGroup object. +Simulator instproc add_eventgroup {group} { + $self instvar eventgroup_list + set eventgroup_list($group) {} +} + # cost # Set the cost for a link Simulator instproc cost {src dst c} { diff --git a/tbsetup/tbreport.in b/tbsetup/tbreport.in index d3ef1b608f793e537491d072d8f4e8c3ca49fcff..498a575bf7b8b402a2dd6776082c001ee382b5f2 100644 --- a/tbsetup/tbreport.in +++ b/tbsetup/tbreport.in @@ -190,7 +190,7 @@ if ($state eq EXPTSTATE_ACTIVE) { # this. # my $virtnodes_result = - DBQueryFatal("SELECT vname,ips,osname,cmd_line,rpms," . + DBQueryFatal("SELECT vname,osname,cmd_line,rpms," . "startupcmd,tarfiles,type,fixed from virt_nodes ". "where pid='$pid' and eid='$eid' order by vname"); @@ -200,7 +200,7 @@ if ($shownodes) { print "--------------- ------------ --------------- ". "--------------------\n"; - while (($vname,$ips,$osname,$cmd_line,$rpms,$startupcmd, + while (($vname,$osname,$cmd_line,$rpms,$startupcmd, $tarfiles,$type,$fixed) = $virtnodes_result->fetchrow_array()) { my $qualname = "$vname.$eid.$pid.$DOMAIN"; @@ -313,23 +313,27 @@ if ($showmap && $state eq EXPTSTATE_ACTIVE) { # Links and Lans # if ($showlinks) { - $virtnodes_result->dataseek(0); + $result = + DBQueryFatal("select vname,vnode,vport,ip,member,mask,delay, ". + " bandwidth,lossrate, ". + " rdelay,rbandwidth,rlossrate,protocol ". + " from virt_lans as v ". + "where pid='$pid' and eid='$eid' ". + "order by vname,member"); - while (($vname,$ips) = $virtnodes_result->fetchrow_array()) { - foreach $ipinfo (split(" ",$ips)) { - ($port,$ip) = split(":",$ipinfo); - $ipmap{"$vname:$port"} = $ip; - $macmap{$ip}->{"MEMBER"} = "$vname:$port"; - } + while (my ($vname,$vnode,$vport,$ip) = $result->fetchrow_array()) { + $ipmap{"$vnode:$vport"} = $ip; + $macmap{$ip}->{"MEMBER"} = "$vnode:$vport"; } + $result->dataseek(0); - my $result = + my $iface_result = DBQueryFatal("select i.ip,i.mac,i.iface from reserved as r ". "left join interfaces as i on r.node_id=i.node_id ". "where r.pid='$pid' and r.eid='$eid' and ". " i.ip is not NULL and i.ip!=''"); - while (($ip,$mac,$iface) = $result->fetchrow_array()) { + while (($ip,$mac,$iface) = $iface_result->fetchrow_array()) { if ($mac =~ /^(\w{2})(\w{2})(\w{2})(\w{2})(\w{2})(\w{2})$/) { $mac = "$1:$2:$3:$4:$5:$6"; } @@ -337,13 +341,6 @@ if ($showlinks) { $macmap{$ip}->{"IFACE"} = $iface; } - $result = - DBQueryFatal("select vname,member,mask,delay,bandwidth,lossrate, ". - " rdelay,rbandwidth,rlossrate,protocol ". - " from virt_lans as v ". - "where pid='$pid' and eid='$eid' ". - "order by vname,member"); - if ($result->numrows) { print "Virtual Lan/Link Info:\n"; printf "%-15s %-15s %-15s %-9s %-9s %-9s\n", @@ -351,8 +348,8 @@ if ($showlinks) { print "--------------- --------------- --------------- --------- ". "--------- ---------\n"; - while (($vname,$member,$mask,$delay,$bandwidth,$lossrate, - $rdelay,$rbandwidth,$rlossrate,$protocol) + while (($vname,$vnode,$vport,$ip,$member,$mask,$delay,$bandwidth, + $lossrate,$rdelay,$rbandwidth,$rlossrate,$protocol) = $result->fetchrow_array()) { printf "%-15s %-15s %-15s %-9s %-9s %-9s\n", $vname, $member, $ipmap{$member}, $delay, $bandwidth, $lossrate; @@ -371,7 +368,8 @@ if ($showlinks) { print "--------------- --------------- --------------- ". "-------------------- ---------\n"; - while (($vname,$member) = $result->fetchrow_array()) { + while (($vname,$vnode,$vport,$ip,$member) = + $result->fetchrow_array()) { my ($vname,$port) = split(":", $member); printf "%-15s %-15s %-15s %-20s %-9s\n", $vname, @@ -721,6 +719,35 @@ if ($showroutes) { # if ($showevents) { my $result = + DBQueryFatal("select group_name,agent_name from event_groups ". + "where pid='$pid' and eid='$eid' ". + "order by group_name"); + + if ($result->numrows) { + my %egroups = (); + + while (($group_name,$agent_name) = $result->fetchrow_array()) { + if (!exists($egroups{$group_name})) { + $egroups{$group_name} = []; + } + push(@{ $egroups{$group_name} }, $agent_name); + } + print "Event Groups:\n"; + + printf("%-15s %s\n", + "Group Name", "Members"); + print("--------------- --------------------------------". + "-------------------------------\n"); + + foreach my $group (keys(%egroups)) { + my @agents = @{ $egroups{$group} }; + + printf("%-15s %s\n", $group, join(",", @agents)); + } + print "\n"; + } + + $result = DBQueryFatal("select time,vnode,vname,ot.type,et.type,arguments ". " from eventlist as ex ". "left join event_eventtypes as et on ex.eventtype=et.idx ". diff --git a/www/shownsfile.php3 b/www/shownsfile.php3 index 06d9cca456f03d393f086c8a3cd8b482787e14a3..fc9c4da92fa8162345e3109e7827e50691fe8bec 100644 --- a/www/shownsfile.php3 +++ b/www/shownsfile.php3 @@ -1,7 +1,7 @@ FullTcp, FTP and Telnet: Refer to the NS documentation here. + + +

+

+ +Event Groups +

+ +Event Groups allow you to conveniently send events to groups of like +objects. For example, if you want to bring down a set of links at the +same time, you could do it one event at a time: + +
+	$ns at 100.0 "$link0 down"
+	$ns at 100.0 "$link1 down"
+	$ns at 100.0 "$link2 down"
+	$ns at 100.0 "$link3 down" 
+ +which works, but is somewhat verbose. Its also presents a problem when +sending dynamic events with tevc from the shell: + +
+	tevc -e proj/expt now link0 down
+	tevc -e proj/expt now link1 down
+	tevc -e proj/expt now link2 down
+	tevc -e proj/expt now link3 down
+ +These four events will be separated by many milliseconds as each call +to tevc requires forking a command from the shell, contacting boss, +sending the event to the event scheduler, etc. + +

+A better alternative is to create an event group, which will +schedule events for all of the members of the group, sending them at +the same time from the event scheduler. The example above can be more +simply implemented as: +
+	set mylinks [new EventGroup $ns]
+	$mylinks add $link0 $link1 $link2 $link3
+
+	$ns at 60.0 "$mylinks down"
+ +From the command line: + +
+	tevc -e proj/expt now mylinks down
+ +Note: +
    +
  • All of the members of an event group must be of the same type; + you cannot, say, put a link and a program object into the same + event group since they respond to entirely different commands. + The parser will reject such groups. + +
  • An object (such as a link or lan) can be in multiple event + groups. + +
  • Event groups are not hierarchical; you cannot put one event group + into another event group. If you need this functionality, then + you need to put the objects themselves (such as a link or lan) + into each event group directly: + +
    +	set mylinks1 [new EventGroup $ns]
    +	set mylinks2 [new EventGroup $ns]
    +	
    +	$mylinks1 add $link0 $link1 $link2 $link3
    +	$mylinks2 add $link0 $link1 $link2 $link3
+

@@ -479,7 +548,5 @@ Some points worth mentioning:
  • The "stop" command is implemented by sending a SIGTERM to the process group leader (the csh process). If the SIGTERM fails, a SIGKILL is sent. - -
  • If you override