import uuid
SINGLETON = "singleton::"
+SIMULATOR_UUID = "singleton::Simulator"
+CONFIG_UUID = "singleton::Config"
+GLOBAL_VALUE_UUID = "singleton::GlobalValue"
+IPV4_GLOBAL_ROUTING_HELPER_UUID = "singleton::Ipv4GlobalRoutingHelper"
-def load_ns3_module():
+def load_ns3_libraries():
import ctypes
import re
- bindings = os.environ.get("NS3BINDINGS")
libdir = os.environ.get("NS3LIBRARIES")
# Load the ns-3 modules shared libraries
regex = re.compile("(.*\.so)$")
libs = [m.group(1) for filename in files for m in [regex.search(filename)] if m]
- libscp = list(libs)
+ initial_size = len(libs)
+ # Try to load the libraries in the right order by trial and error.
+ # Loop until all libraries are loaded.
while len(libs) > 0:
for lib in libs:
libfile = os.path.join(libdir, lib)
ctypes.CDLL(libfile, ctypes.RTLD_GLOBAL)
libs.remove(lib)
except:
+ #import traceback
+ #err = traceback.format_exc()
+ #print err
pass
# if did not load any libraries in the last iteration break
# to prevent infinit loop
- if len(libscp) == len(libs):
+ if initial_size == len(libs):
raise RuntimeError("Imposible to load shared libraries %s" % str(libs))
- libscp = list(libs)
+ initial_size = len(libs)
+
+def load_ns3_module():
+ load_ns3_libraries()
# import the python bindings for the ns-3 modules
+ bindings = os.environ.get("NS3BINDINGS")
if bindings:
sys.path.append(bindings)
import imp
import ns
- # create a module to add all ns3 classes
+ # create a Python module to add all ns3 classes
ns3mod = imp.new_module("ns3")
sys.modules["ns3"] = ns3mod
for importer, modname, ispkg in pkgutil.iter_modules(ns.__path__):
+ if modname in [ "visualizer" ]:
+ continue
+
fullmodname = "ns.%s" % modname
module = __import__(fullmodname, globals(), locals(), ['*'])
return ns3mod
class NS3Wrapper(object):
- def __init__(self, homedir = None):
+ def __init__(self, loglevel = logging.INFO):
super(NS3Wrapper, self).__init__()
# Thread used to run the simulation
self._simulation_thread = None
self._condition = None
- # XXX: Started should be global. There is no support for more than
- # one simulator per process
+ # True if Simulator::Run was invoked
self._started = False
# holds reference to all C++ objects and variables in the simulation
self._objects = dict()
- # create home dir (where all simulation related files will end up)
- self._homedir = homedir or os.path.join("/", "tmp", "ns3_wrapper" )
-
- home = os.path.normpath(self.homedir)
- if not os.path.exists(home):
- os.makedirs(home, 0755)
-
# Logging
- loglevel = os.environ.get("NS3LOGLEVEL", "debug")
self._logger = logging.getLogger("ns3wrapper")
- self._logger.setLevel(getattr(logging, loglevel.upper()))
-
- hdlr = logging.FileHandler(os.path.join(self.homedir, "ns3wrapper.log"))
- formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
- hdlr.setFormatter(formatter)
-
- self._logger.addHandler(hdlr)
+ self._logger.setLevel(loglevel)
## NOTE that the reason to create a handler to the ns3 module,
# that is re-loaded each time a ns-3 wrapper is instantiated,
tid_count = type_id.GetRegisteredN()
base = type_id.LookupByName("ns3::Object")
- # Create a .py file using the ns-3 RM template for each ns-3 TypeId
for i in xrange(tid_count):
tid = type_id.GetRegistered(i)
return self._allowed_types
- @property
- def homedir(self):
- return self._homedir
-
@property
def logger(self):
return self._logger
return self._objects.get(uuid)
def factory(self, type_name, **kwargs):
- if type_name not in allowed_types:
+ if type_name not in self.allowed_types:
msg = "Type %s not supported" % (type_name)
self.logger.error(msg)
return uuid
- def invoke(self, uuid, operation, *args):
+ def invoke(self, uuid, operation, *args, **kwargs):
+ if operation == "isRunning":
+ return self._is_running()
+ if operation == "isAppRunning":
+ return self._is_app_running(uuid)
+ if operation == "addStaticRoute":
+ return self._add_static_route(uuid, *args)
+
if uuid.startswith(SINGLETON):
obj = self._singleton(uuid)
else:
obj = self.get_object(uuid)
-
+
method = getattr(obj, operation)
# arguments starting with 'uuid' identify ns-3 C++
# objects and must be replaced by the actual object
realargs = self.replace_args(args)
+ realkwargs = self.replace_kwargs(kwargs)
- result = method(*realargs)
+ result = method(*realargs, **realkwargs)
- if not result:
- return None
-
+ # If the result is not an object, no need to
+ # keep a reference. Directly return value.
+ if result is None or type(result) in [bool, float, long, str, int]:
+ return result
+
newuuid = self.make_uuid()
self._objects[newuuid] = result
# to set the value by scheduling an event, else
# we risk to corrupt the state of the
# simulation.
+
+ event_executed = [False]
+
if self.is_running:
# schedule the event in the Simulator
- self._schedule_event(self._condition, self._set_attr,
- obj, name, ns3_value)
- else:
+ self._schedule_event(self._condition, event_executed,
+ self._set_attr, obj, name, ns3_value)
+
+ if not event_executed[0]:
self._set_attr(obj, name, ns3_value)
return value
type_name = obj.GetInstanceTypeId().GetName()
ns3_value = self._create_attr_ns3_value(type_name, name)
+ event_executed = [False]
+
if self.is_running:
# schedule the event in the Simulator
- self._schedule_event(self._condition, self._get_attr, obj,
- name, ns3_value)
- else:
- get_attr(obj, name, ns3_value)
+ self._schedule_event(self._condition, event_executed,
+ self._get_attr, obj, name, ns3_value)
+
+ if not event_executed[0]:
+ self._get_attr(obj, name, ns3_value)
return self._attr_from_ns3_value_to_string(type_name, name, ns3_value)
#self.logger.debug("Waiting for simulation to finish")
time.sleep(0.5)
- # TODO!!!! SHOULD WAIT UNTIL THE THREAD FINISHES
if self._simulator_thread:
self._simulator_thread.join()
-
+
self.ns3.Simulator.Destroy()
# Remove all references to ns-3 objects
condition.notifyAll()
condition.release()
- def _schedule_event(self, condition, func, *args):
+ def _schedule_event(self, condition, event_executed, func, *args):
""" Schedules event on running simulation, and wait until
event is executed"""
- def execute_event(contextId, condition, has_event_occurred, func, *args):
+ def execute_event(contextId, condition, event_executed, func, *args):
try:
func(*args)
+ event_executed[0] = True
finally:
- # flag event occured
- has_event_occurred[0] = True
- # notify condition indicating attribute was set
+ # notify condition indicating event was executed
condition.acquire()
condition.notifyAll()
condition.release()
# delay 0 means that the event is expected to execute inmediately
delay = self.ns3.Seconds(0)
+
+ # Mark event as not executed
+ event_executed[0] = False
- # flag to indicate that the event occured
- # because bool is an inmutable object in python, in order to create a
- # bool flag, a list is used as wrapper
- has_event_occurred = [False]
condition.acquire()
-
- simu = self.ns3.Simulator
-
try:
- if not simu.IsFinished():
- simu.ScheduleWithContext(contextId, delay, execute_event,
- condition, has_event_occurred, func, *args)
- while not has_event_occurred[0] and not simu.IsFinished():
- condition.wait()
+ self.ns3.Simulator.ScheduleWithContext(contextId, delay, execute_event,
+ condition, event_executed, func, *args)
+ if not self.ns3.Simulator.IsFinished():
+ condition.wait()
finally:
condition.release()
return realargs
+ # replace uuids and singleton references for the real objects
+ def replace_kwargs(self, kwargs):
+ realkwargs = dict([(k, self.get_object(v) \
+ if str(v).startswith("uuid") else v) \
+ for k,v in kwargs.iteritems()])
+
+ realkwargs = dict([(k, self._singleton(v) \
+ if str(v).startswith(SINGLETON) else v )\
+ for k, v in realkwargs.iteritems()])
+
+ return realkwargs
+
+ def _is_running(self):
+ if self.ns3.Simulator.IsFinished():
+ return False
+
+ now = self.ns3.Simulator.Now()
+ if now.IsZero():
+ return False
+
+ return True
+
+ def _is_app_running(self, uuid):
+ now = self.ns3.Simulator.Now()
+ if now.IsZero():
+ return False
+
+ app = self.get_object(uuid)
+ stop_time_value = self.ns3.TimeValue()
+ app.GetAttribute("StopTime", stop_time_value)
+ stop_time = stop_time_value.Get()
+
+ start_time_value = self.ns3.TimeValue()
+ app.GetAttribute("StartTime", start_time_value)
+ start_time = start_time_value.Get()
+
+ if now.Compare(start_time) >= 0 and now.Compare(stop_time) < 0:
+ return True
+
+ return False
+
+ def _add_static_route(self, ipv4_uuid, network, prefix, nexthop):
+ ipv4 = self.get_object(ipv4_uuid)
+
+ list_routing = ipv4.GetRoutingProtocol()
+ (static_routing, priority) = list_routing.GetRoutingProtocol(0)
+
+ ifindex = self._find_ifindex(ipv4, nexthop)
+ if ifindex == -1:
+ return False
+
+ nexthop = self.ns3.Ipv4Address(nexthop)
+
+ if network in ["0.0.0.0", "0", None]:
+ # Default route: 0.0.0.0/0
+ static_routing.SetDefaultRoute(nexthop, ifindex)
+ else:
+ mask = self.ns3.Ipv4Mask("/%s" % prefix)
+ network = self.ns3.Ipv4Address(network)
+
+ if prefix == 32:
+ # Host route: x.y.z.w/32
+ static_routing.AddHostRouteTo(network, nexthop, ifindex)
+ else:
+ # Network route: x.y.z.w/n
+ static_routing.AddNetworkRouteTo(network, mask, nexthop,
+ ifindex)
+ return True
+
+ def _find_ifindex(self, ipv4, nexthop):
+ ifindex = -1
+
+ nexthop = self.ns3.Ipv4Address(nexthop)
+
+ # For all the interfaces registered with the ipv4 object, find
+ # the one that matches the network of the nexthop
+ nifaces = ipv4.GetNInterfaces()
+ for ifidx in xrange(nifaces):
+ iface = ipv4.GetInterface(ifidx)
+ naddress = iface.GetNAddresses()
+ for addridx in xrange(naddress):
+ ifaddr = iface.GetAddress(addridx)
+ ifmask = ifaddr.GetMask()
+
+ ifindex = ipv4.GetInterfaceForPrefix(nexthop, ifmask)
+
+ if ifindex == ifidx:
+ return ifindex
+ return ifindex
+