Commit d3274468 authored by Shashi Guruprasad's avatar Shashi Guruprasad

Simulation/Emulation Transparency integration

parent b0b2497b
......@@ -9,7 +9,8 @@ SUBDIR = tbsetup/ns2ir
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
nsobject.tcl traffic.tcl vtype.tcl parse.tcl program.tcl \
nsenode.tcl nstb_compat.tcl
LIBEXEC_STUFF = parse-ns
#
......
......@@ -52,6 +52,18 @@ Node instproc init {s} {
$self set tarfiles ""
$self set failureaction "fatal"
$self set fixed ""
$self set nseconfig ""
var_import ::GLOBALS::simulated
var_import ::GLOBALS::curnsenode
if { $simulated == 1 } {
$self set simulated 1
$self set nsenode $curnsenode
} else {
$self set simulated 0
$self set nsenode ""
}
$self set nsenode_vport ""
}
# The following procs support renaming (see README)
......@@ -94,10 +106,17 @@ Node instproc updatedb {DB} {
$self instvar agentlist
$self instvar routelist
$self instvar sim
$self instvar simulated
$self instvar nseconfig
var_import ::GLOBALS::pid
var_import ::GLOBALS::eid
var_import ::GLOBALS::default_ip_routing_type
# currently we don't want to update the DB for simulated nodes
if { $simulated == 1 } {
return
}
# If we haven't specified a osid so far then we should fill it
# with the id from the node_types table now.
if {$osid == {}} {
......@@ -114,15 +133,21 @@ Node instproc updatedb {DB} {
incr i
}
set nseconfig ""
foreach agent $agentlist {
$agent updatedb $DB
append nseconfig [$agent get_nseconfig]
}
if {$nseconfig != {}} {
set nsecfg_script ""
set simu [lindex [Simulator info instances] 0]
append nsecfg_script "set $simu \[new Simulator]\n"
append nsecfg_script "\$$simu use-scheduler RealTime\n\n"
append nsecfg_script $nseconfig
# update the per-node nseconfigs table in the DB
sql exec $DB "insert into nseconfigs (pid,eid,vname,nseconfig) values ('$pid','$eid','$self','$nseconfig')";
sql exec $DB "insert into nseconfigs (pid,eid,vname,nseconfig) values ('$pid','$eid','$self','$nsecfg_script')";
}
$self add_routes_to_DB $DB
......
######################################################################
# nsenode.tcl
#
# This defines the NSENode class. Instances of this class are created by
# the 'nsenode' method of Simulator.
# This node supports a method make-simulated and when invoked with
# code in-between {} as an argument, treats the code as simulated
# and runs NSE on the physical NSENode with the provided script
######################################################################
Class NSENode -superclass Node
# need to have a single namespace for simulated node names
# lets have simulated node -> nsenode map
# need to support simulated to real node links, simulated to simulated
# across nse nodes => we need a duplex-link specialization even in
# child interpreters
# routing must be
# manually enabled so that a RAW IP packet written should automatically
# make it out
NSENode instproc init {s} {
set new_classes(NSENode) {}
$self next $s
$self set nseconfig ""
$self set simcode_present 0
}
NSENode instproc make-simulated {args} {
var_import ::GLOBALS::simulated
var_import ::GLOBALS::curnsenode
$self instvar nseconfig
$self instvar simcode_present
set simulated 1
set curnsenode $self
global script
set script [string trim $args "\{\}"]
if { $script == {} } {
set simulated 0
set curnsenode ""
return
}
set simcode_present 1
# we ignore any type of errors coz they have
# been caught when we ran the script through NSE
catch {uplevel 1 eval $script}
append nseconfig $script
set simulated 0
set curnsenode ""
}
NSENode instproc updatedb {DB} {
$self instvar nseconfig
$self instvar simcode_present
# for all the simulated nodes, we need to find out if
# they are connected to a real node and if so, find the
# ip address of the port on nsenode
foreach node [Node info instances] {
if { [$node set simulated] == 1 && [$node set nsenode] == $self } {
append nseconfig "\n\n\$$node set simulated 1\n"
append nseconfig "\$$node set nsenode $self\n"
set nsenode_vport [$node set nsenode_vport]
if { $nsenode_vport != {} } {
append nseconfig "\$$node set nsenode_vport $nsenode_vport\n"
append nseconfig "\$$node set nsenode_ipaddr [$self ip $nsenode_vport]\n\n"
}
}
}
if { $simcode_present == 1 } {
append nseconfig "set simcode_present $simcode_present\n\n"
}
# NSE code runs only FreeBSD as of now
$self set osid "FBSD-STD"
$self next $DB
}
# This is a nop tb_compact.tcl file that should be used when running scripts
# under ns.
proc tb-set-ip {node ip} {}
proc tb-set-ip-interface {src dst ip} {}
proc tb-set-ip-link {src link ip} {}
proc tb-set-ip-lan {src lan ip} {}
proc tb-set-hardware {node type args} {}
proc tb-set-node-os {node os} {}
proc tb-set-link-loss {src args} {}
proc tb-set-lan-loss {lan rate} {}
proc tb-set-node-rpms {node args} {}
proc tb-set-node-startup {node cmd} {}
proc tb-set-node-cmdline {node cmd} {}
proc tb-set-node-deltas {node args} {}
proc tb-set-node-tarfiles {node args} {}
proc tb-set-node-lan-delay {node lan delay} {}
proc tb-set-node-lan-bandwidth {node lan bw} {}
proc tb-set-node-lan-loss {node lan loss} {}
proc tb-set-node-lan-params {node lan delay bw loss} {}
proc tb-set-node-failure-action {node type} {}
proc tb-set-ip-routing {type} {}
proc tb-fix-node {v p} {}
proc tb-make-weighted-vtype {name weight types} {}
proc tb-make-soft-vtype {name types} {}
proc tb-make-hard-vtype {name types} {}
proc tb-set-lan-simplex-params {lan node todelay tobw toloss fromdelay frombw fromloss} {}
proc tb-set-link-simplex-params {link src delay bw loss} {}
Class Program
Program instproc unknown {m args} {
}
Class NSENode -superclass Node
NSENode instproc make-simulated {args} {
uplevel 1 eval $args
}
# We are just syntax checking the NS file
Simulator instproc run {args} {
}
Simulator instproc nsenode {args} {
return [new NSENode]
}
......@@ -9,10 +9,11 @@
Class NullClass
NullClass instproc init {mytype} {
$self set type $mytype
NullClass instproc init {mtype} {
$self set type $mtype
}
NullClass instproc unknown {m args} {
$self instvar type
punsup "$type $m"
}
......@@ -56,6 +56,15 @@ proc var_import {varspec} {
###
proc perror {msg} {
var_import ::GLOBALS::errors
var_import ::GLOBALS::simulated
# If this was a true error in specifying
# the simulation, it would have been
# caught when run with NSE
if { $simulated == 1 } {
return 0
}
global argv0
puts stderr "*** $argv0: "
puts stderr " $msg"
......@@ -69,7 +78,12 @@ proc perror {msg} {
proc punsup {msg} {
var_import ::GLOBALS::verbose
var_import ::GLOBALS::WARN_FILE
if {$verbose == 1} {
var_import ::GLOBALS::simulated
# If this was a true error in specifying
# the simulation, it would have been
# caught when run with NSE
if {$simulated == 0 && $verbose == 1} {
puts stderr "*** WARNING: Unsupported NS Statement!"
puts stderr " $msg"
puts $WARN_FILE "*** WARNING: Unsupported NS Statement!"
......@@ -143,6 +157,9 @@ namespace eval GLOBALS {
# 'source tb_compat.tcl' statement succeeds.
variable tbcompat "$libdir/tb_compat.tcl"
# This is used in running the script through nse for syntax errors
variable nstbcompat "$libdir/nstb_compat.tcl"
# Is 1 if any errors have occured so far.
variable errors 0
......@@ -161,6 +178,15 @@ namespace eval GLOBALS {
# This is the file handler for the warnings file
variable WARN_FILE [open "$eid.warnings" w]
# This distinguishes whether the script that
# is being parsed should go into a NSE simulation or not
variable simulated 0
# If simulated is 1, then curnsenode gives us the name
# of the physical node on which the simulation script
# should go into
variable curnsenode ""
}
# Connect to the DB
......@@ -187,6 +213,7 @@ source ${GLOBALS::libdir}/nsobject.tcl
source ${GLOBALS::libdir}/sim.tcl
source ${GLOBALS::libdir}/lanlink.tcl
source ${GLOBALS::libdir}/node.tcl
source ${GLOBALS::libdir}/nsenode.tcl
source ${GLOBALS::libdir}/null.tcl
source ${GLOBALS::libdir}/traffic.tcl
source ${GLOBALS::libdir}/vtype.tcl
......@@ -289,7 +316,7 @@ proc new {class args} {
if {! [info exists new_classes($class)]} {
punsup "Object: $class"
set id null[incr new_counter]
NullClass $id
NullClass $id $class
return $id
}
......@@ -364,16 +391,25 @@ proc parse_delay {dspec} {
# We now have all our infrastructure in place. We are ready to load
# the NS file.
file copy -force ${GLOBALS::tbcompat} .
source ${GLOBALS::nsfile}
# We first run the script through NSE to check syntax errors
file copy -force ${GLOBALS::nstbcompat} tb_compat.tcl
if { [catch {exec nse ${GLOBALS::nsfile}}] == 1 } {
perror "Syntax check with NS failed\nerrorcode:$errorCode"
}
# Clean up
sql disconnect ${GLOBALS::DB}
if { ${GLOBALS::errors} != 1 } {
file copy -force ${GLOBALS::tbcompat} .
source ${GLOBALS::nsfile}
if {${GLOBALS::ran} == 0} {
if {${GLOBALS::ran} == 0} {
perror "No 'Simulator run' statement found."
}
}
# Clean up
sql disconnect ${GLOBALS::DB}
close ${GLOBALS::WARN_FILE}
exit ${GLOBALS::errors}
......@@ -48,6 +48,13 @@ Program instproc updatedb {DB} {
return
}
sql exec $DB "insert into virt_agents (pid,eid,vnode,vname,objecttype) values ('$pid','$eid','$node','$self','$objtypes(PROGRAM)')";
set progvnode $node
# if the attached node is a simulated one, we attach the
# program to the physical node on which the simulation runs
if { [$node set simulated] == 1 } {
set progvnode [$node set nsenode]
}
sql exec $DB "insert into virt_agents (pid,eid,vnode,vname,objecttype) values ('$pid','$eid','$prognode','$self','$objtypes(PROGRAM)')";
}
......@@ -51,6 +51,15 @@ Simulator instproc init {args} {
# Program list.
$self instvar prog_list;
array set prog_list {}
var_import ::GLOBALS::last_class
set last_class $self
}
# renaming the simulator instance
# needed to find the name of the instance
# for use in NSE code
Simulator instproc rename {old new} {
}
# node
......@@ -58,6 +67,8 @@ Simulator instproc init {args} {
# of the node.
Simulator instproc node {args} {
var_import ::GLOBALS::last_class
var_import ::GLOBALS::simulated
var_import ::GLOBALS::curnsenode
$self instvar id_counter
$self instvar node_list
......@@ -67,7 +78,40 @@ Simulator instproc node {args} {
set curnode n[incr id_counter]
Node $curnode $self
# not adding simulated nodes to the node_list
if { $simulated == 0 } {
set node_list($curnode) {}
}
set last_class $curnode
return $curnode
}
# nsenode
# this method does pretty much what the node proc does
# with some XXXXX
Simulator instproc nsenode {args} {
var_import ::GLOBALS::last_class
var_import ::GLOBALS::simulated
var_import ::GLOBALS::curnsenode
$self instvar id_counter
$self instvar node_list
if {($args != {})} {
punsup "Arguments for node: $args"
}
set curnode n[incr id_counter]
NSENode $curnode $self
# not adding simulated nodes to the node_list
if { $simulated == 0 } {
set node_list($curnode) {}
} else {
$curnode set simulated 1
$curnode set nsenode $curnsenode
}
set last_class $curnode
return $curnode
......@@ -79,6 +123,8 @@ Simulator instproc node {args} {
# parse_delay. Currently only the type 'DropTail' is supported.
Simulator instproc duplex-link {n1 n2 bw delay type args} {
var_import ::GLOBALS::last_class
var_import ::GLOBALS::simulated
var_import ::GLOBALS::curnsenode
$self instvar id_counter
$self instvar lanlink_list
$self instvar link_map
......@@ -97,16 +143,57 @@ Simulator instproc duplex-link {n1 n2 bw delay type args} {
}
if {$error} {return}
set curlink l[incr id_counter]
# Convert bandwidth and delay
set rbw [parse_bw $bw]
set rdelay [parse_delay $delay]
Link $curlink $self "$n1 $n2" $rbw $rdelay $type
set n1sim [$n1 set simulated]
set n2sim [$n2 set simulated]
set n1node $n1
set n2node $n2
set simnode ""
# If one of the nodes is a real node, we
# create a link between the real node and
# the physical node corresponding to the
# simulated node. If both are simulated
# nodes, we don't need to do anything
if { $n1sim == 1 && $n2sim == 0 } {
set simnode $n1
set n1node [$n1 set nsenode]
} elseif { $n1sim == 0 && $n2sim == 1 } {
set simnode $n2
set n2node [$n2 set nsenode]
} elseif { $n1sim == 1 && $n2sim == 1 } {
return ""
}
# At this point we have one of the nodes of
# the link to be real. We need to make sure
# that this is not being defined in make-simulated.
# In other words links or lans from real nodes and
# simulated nodes should happen outside make-simulated
if { $simulated == 1 } {
set simulated 0
perror "Please define links between real and simulated nodes outside make-simulated"
set simulated 1
return ""
}
set curlink l[incr id_counter]
Link $curlink $self "$n1node $n2node" $rbw $rdelay $type
set lanlink_list($curlink) {}
set link_map($n1:$n2) $curlink
set link_map($n2:$n1) $curlink
set link_map($n1node:$n2node) $curlink
set link_map($n2node:$n1node) $curlink
# get the vport number on nsenode on which we make
# the link and store it in the simulated node that
# goes into NSE
if { $simnode != {} } {
set vport [[$simnode set nsenode] find_port $curlink]
$simnode set nsenode_vport $vport
}
set last_class $curlink
return $curlink
......@@ -118,6 +205,8 @@ Simulator instproc duplex-link {n1 n2 bw delay type args} {
# parse_delay.
Simulator instproc make-lan {nodelist bw delay args} {
var_import ::GLOBALS::last_class
var_import ::GLOBALS::simulated
var_import ::GLOBALS::curnsenode
$self instvar id_counter
$self instvar lanlink_list
......@@ -125,17 +214,56 @@ Simulator instproc make-lan {nodelist bw delay args} {
punsup "Arguments for make-lan: $args"
}
set modified_nodelist ""
set realnode_present 0
set simnode_list ""
foreach node $nodelist {
if { [$node set simulated] == 1 } {
append modified_nodelist [$node set nsenode]
append simnode_list $node
} else {
append modified_nodelist $node
set realnode_present 1
}
}
if { $realnode_present == 1 } {
# At this point we have one of the nodes of
# the lan to be real. We need to make sure
# that this is not being defined in make-simulated.
# In other words links or lans from real nodes and
# simulated nodes should happen outside make-simulated
if { $simulated == 1 } {
set simulated 0
perror "Please define links between real and simulated nodes outside make-simulated"
set simulated 1
return ""
}
set curlan lan[incr id_counter]
# Convert bandwidth and delay
set rbw [parse_bw $bw]
set rdelay [parse_delay $delay]
Lan $curlan $self $nodelist $rbw $rdelay {}
Lan $curlan $self $modified_nodelist $rbw $rdelay {}
set lanlink_list($curlan) {}
set last_class $curlan
# get the vport number on nsenode on which we make
# the lan and store it in the simulated node that
# goes into NSE
foreach simnode $simnode_list {
set vport [[$simnode set nsenode] find_port $curlan]
$simnode set nsenode_vport $vport
}
return $curlan
} else {
return ""
}
}
# run
......@@ -228,6 +356,8 @@ Simulator instproc run {} {
# attach-agent <node> <agent>
# This creates an attachment between <node> and <agent>.
Simulator instproc attach-agent {node agent} {
var_import ::GLOBALS::simulated
if {! [$agent info class Agent]} {
perror "\[attach-agent] $agent is not an Agent."
return
......@@ -236,6 +366,16 @@ Simulator instproc attach-agent {node agent} {
perror "\[attach-agent] $node is not a Node."
return
}
# If the node is real and yet this code is in make-simulated
# we don't allow it
if { [$node set simulated] == 0 && $simulated == 1 } {
set simulated 0
perror "Please attach agents on to real nodes outside make-simulated"
set simulated 1
return ""
}
$node attach-agent $agent
}
......@@ -245,7 +385,7 @@ Simulator instproc connect {src dst} {
set error 0
if {! [$src info class Agent]} {
perror "\[connect] $src is not an Agent."
set error 1OB
set error 1
}
if {! [$dst info class Agent]} {
perror "\[connect] $dst is not an Agent."
......@@ -264,6 +404,14 @@ Simulator instproc connect {src dst} {
# <link> down
# ...
Simulator instproc at {time eventstring} {
var_import ::GLOBALS::simulated
var_import ::GLOBALS::curnsenode
# ignore at statement for simulated case
if { $simulated == 1 } {
return
}
# Check that time is float
if {[regexp {(^[0-9]+(\.[0-9]+)?$)|(^\.[0-9]+$)} $time] == 0} {
perror "Invalid time spec: $time"
......@@ -507,6 +655,13 @@ Simulator instproc at {time eventstring} {
#
Simulator instproc rtproto {type args} {
var_import ::GLOBALS::default_ip_routing_type
var_import ::GLOBALS::simulated
var_import ::GLOBALS::curnsenode
# ignore at statement for simulated case
if { $simulated == 1 } {
return
}
if {$args != {}} {
punsup "rtproto: arguments ignored: $args"
......@@ -556,8 +711,12 @@ Simulator instproc rename_lanlink {old new} {
}
Simulator instproc rename_node {old new} {
$self instvar node_list
# simulated nodes won't exist in the node_list
if { [info exists node_list($old)] } {
unset node_list($old)
set node_list($new) {}
}
}
Simulator instproc rename_program {old new} {
......
......@@ -16,7 +16,7 @@ Class Agent -superclass NSObject
Class Agent/UDP -superclass Agent
Class Agent/TCP -superclass Agent
Class Agent/Null -superclass Agent
Class Agent/TCPSINK -superclass Agent
Class Agent/TCPSink -superclass Agent
Class Agent/TCP/FullTcp -superclass Agent
Class Agent/TCP/FullTcp/Reno -superclass Agent/TCP/FullTcp
Class Agent/TCP/FullTcp/Newreno -superclass Agent/TCP/FullTcp
......@@ -33,7 +33,7 @@ namespace eval GLOBALS {
set new_classes(Agent/UDP) {}
set new_classes(Agent/Null) {}
set new_classes(Agent/TCP) {}
set new_classes(Agent/TCPSINK) {}
set new_classes(Agent/TCPSink) {}
set new_classes(Agent/TCP/FullTcp) {}
set new_classes(Agent/TCP/FullTcp/Reno) {}
set new_classes(Agent/TCP/FullTcp/Newreno) {}
......@@ -150,19 +150,28 @@ Agent/UDP instproc connect {dst} {
perror "\[connect] $self is not attached to a node."
set error 1
}
if {$application == {}} {
perror "\[connect] $self does not have an attached application."
set error 1
}
set dest [$destination set node]
if {$dest == {}} {
perror "\[connect] $destination is not attached to a node."
set error 1
}
if {[llength [$node set portlist]] != 1} {
perror "\[connect] $node must have exactly one link to be a traffic generator."
# for the case when the node is simulated
# we just ignore
if { [$node set simulated] == 1 } {
return
}
if {$application == {}} {
perror "\[connect] $self does not have an attached application."
set error 1
}
# if {[llength [$node set portlist]] != 1} {
# perror "\[connect] $node must have exactly one link to be a traffic generator."
# set error 1
# }
if {$error} {return}
$self set proto "udp"
......@@ -180,19 +189,26 @@ Agent/TCP instproc connect {dst} {
perror "\[connect] $self is not attached to a node."
set error 1
}
if {$application == {}} {
perror "\[connect] $self does not have an attached application."
set error 1
}
set dest [$destination set node]
if {$dest == {}} {
perror "\[connect] $destination is not attached to a node."
set error 1
}
if {[llength [$node set portlist]] != 1} {
perror "\[connect] $node must have exactly one link to be a traffic generator."
# for the case when the node is simulated
# we just ignore
if { [$node set simulated] == 1 } {
return
}
if {$application == {}} {
perror "\[connect] $self does not have an attached application."
set error 1
}
# if {[llength [$node set portlist]] != 1} {
# perror "\[connect] $node must have exactly one link to be a traffic generator."
# set error 1
# }
if {$error} {return}
$self set proto "tcp"
......@@ -298,6 +314,10 @@ Agent/TCP/FullTcp instproc get_nseconfig {} {
return $nseconfig
}
# we set a global variable to indicate that NSE trafgen
# is present so that nseinput.tcl can take appropriate
# action
append nseconfig "set nsetrafgen_present 1"
if { ($tcptype == "") || ($tcptype == "Reno") } {
set nseconfig "set $self \[new Agent/TCP/FullTcp]\n"
} else {
......@@ -338,6 +358,13 @@ Agent/TCP/FullTcp instproc connect {dst} {
perror "\[connect] $self is not attached to a node."
set error 1
}
# for the case when the node is simulated
# we just ignore
if { [$node set simulated] == 1 } {
return
}
if { ($role == "source") && ($application == {}) } {
perror "\[connect] $self does not have an attached application."
set error 1
......@@ -351,10 +378,10 @@ Agent/TCP/FullTcp instproc connect {dst} {
# This was an artifact of SEND-CONSUME CBR model. Now that we run freebsd on the
# traffic generator nodes, all we need to do is either enable OSPF routing by
# default or setup static routes
if {[llength [$node set portlist]] != 1} {
perror "\[connect] $node must have exactly one link to be a traffic generator."
set error 1
}
# if {[llength [$node set portlist]] != 1} {
# perror "\[connect] $node must have exactly one link to be a traffic generator."
# set error 1
# }
if {$error} {return}
$self set proto "tcp"
......@@ -379,17 +406,24 @@ Agent/Null instproc connect {dst} {
perror "\[connect] $destination is not attached to a node."
set error 1
}
if {[llength [$node set portlist]] != 1} {
perror "\[connect] $node must have exactly one link to be a traffic consumer."
set error 1
# for the case when the node is simulated
# we just ignore
if { [$node set simulated] == 1 } {
return
}
# if {[llength [$node set portlist]] != 1} {
# perror "\[connect] $node must have exactly one link to be a traffic consumer."
# set error 1
# }
if {$error} {return}
$self set role "sink"
}
# Agent/Null
Agent/TCPSINK instproc connect {dst} {
Agent/TCPSink instproc connect {dst} {
$self next $dst
$self instvar node
$self instvar application
......@@ -404,10 +438,17 @@ Agent/TCPSINK instproc connect {dst} {
perror "\[connect] $destination is not attached to a node."
set error 1
}
if {[llength [$node set portlist]] != 1} {
perror "\[connect] $node must have exactly one link to be a traffic consumer."
set error 1