# Copyright (C) 2013 INRIA
#
# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
+# it under the terms of the GNU General Public License version 2 as
+# published by the Free Software Foundation;
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
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
# to prevent infinit loop
if initial_size == len(libs):
raise RuntimeError("Imposible to load shared libraries %s" % str(libs))
- initial_size = 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)
return ns3mod
class NS3Wrapper(object):
- def __init__(self, loglevel = logging.INFO):
+ def __init__(self, loglevel = logging.INFO, enable_dump = False):
super(NS3Wrapper, self).__init__()
# Thread used to run the simulation
self._simulation_thread = None
# Collection of allowed ns3 classes
self._allowed_types = None
+ # Object to dump instructions to reproduce and debug experiment
+ from nepi.resources.ns3.ns3wrapper_debug import NS3WrapperDebuger
+ self._debuger = NS3WrapperDebuger(enabled = enable_dump)
+
+ @property
+ def debuger(self):
+ return self._debuger
+
@property
def ns3(self):
if not self._ns3:
tid_count = type_id.GetRegisteredN()
base = type_id.LookupByName("ns3::Object")
- for i in xrange(tid_count):
+ for i in range(tid_count):
tid = type_id.GetRegistered(i)
if tid.MustHideFromDocumentation() or \
@property
def is_running(self):
- return self._started and self.ns3.Simulator.IsFinished()
+ return self.is_started and not self.ns3.Simulator.IsFinished()
+
+ @property
+ def is_started(self):
+ if not self._started:
+ now = self.ns3.Simulator.Now()
+ if not now.IsZero():
+ self._started = True
+
+ return self._started
+
+ @property
+ def is_finished(self):
+ return self.ns3.Simulator.IsFinished()
def make_uuid(self):
return "uuid%s" % uuid.uuid4()
return self._objects.get(uuid)
def factory(self, type_name, **kwargs):
+ """ This method should be used to construct ns-3 objects
+ that have a TypeId and related introspection information """
+
if type_name not in self.allowed_types:
msg = "Type %s not supported" % (type_name)
self.logger.error(msg)
-
+
+ uuid = self.make_uuid()
+
+ ### DEBUG
+ self.logger.debug("FACTORY %s( %s )" % (type_name, str(kwargs)))
+
+ ### DUMP
+ self.debuger.dump_factory(uuid, type_name, kwargs)
+
factory = self.ns3.ObjectFactory()
factory.SetTypeId(type_name)
- for name, value in kwargs.iteritems():
+ for name, value in kwargs.items():
ns3_value = self._attr_from_string_to_ns3_value(type_name, name, value)
factory.Set(name, ns3_value)
obj = factory.Create()
- uuid = self.make_uuid()
self._objects[uuid] = obj
+ ### DEBUG
+ self.logger.debug("RET FACTORY ( uuid %s ) %s = %s( %s )" % (
+ str(uuid), str(obj), type_name, str(kwargs)))
+
return uuid
def create(self, clazzname, *args):
+ """ This method should be used to construct ns-3 objects that
+ do not have a TypeId (e.g. Values) """
+
if not hasattr(self.ns3, clazzname):
msg = "Type %s not supported" % (clazzname)
self.logger.error(msg)
-
+
+ uuid = self.make_uuid()
+
+ ### DEBUG
+ self.logger.debug("CREATE %s( %s )" % (clazzname, str(args)))
+
+ ### DUMP
+ self.debuger.dump_create(uuid, clazzname, args)
+
clazz = getattr(self.ns3, clazzname)
# arguments starting with 'uuid' identify ns-3 C++
obj = clazz(*realargs)
- uuid = self.make_uuid()
self._objects[uuid] = obj
+ ### DEBUG
+ self.logger.debug("RET CREATE ( uuid %s ) %s = %s( %s )" % (str(uuid),
+ str(obj), clazzname, str(args)))
+
return uuid
def invoke(self, uuid, operation, *args, **kwargs):
- if operation == "isAppRunning":
- return self._is_app_running(uuid)
+ ### DEBUG
+ self.logger.debug("INVOKE %s -> %s( %s, %s ) " % (
+ uuid, operation, str(args), str(kwargs)))
+ ########
+
+ result = None
+ newuuid = None
+
+ if operation == "isRunning":
+ result = self.is_running
+
+ elif operation == "isStarted":
+ result = self.is_started
- if uuid.startswith(SINGLETON):
- obj = self._singleton(uuid)
+ elif operation == "isFinished":
+ result = self.is_finished
+
+ elif operation == "isAppRunning":
+ result = self._is_app_running(uuid)
+
+ elif operation == "isAppStarted":
+ result = self._is_app_started(uuid)
+
+ elif operation == "recvFD":
+ ### passFD operation binds to a different random socket
+ ### en every execution, so the socket name that could be
+ ### dumped to the debug script using dump_invoke is
+ ### not be valid accross debug executions.
+ result = self._recv_fd(uuid, *args, **kwargs)
+
+ elif operation == "addStaticRoute":
+ result = self._add_static_route(uuid, *args)
+
+ ### DUMP - result is static, so will be dumped as plain text
+ self.debuger.dump_invoke(result, uuid, operation, args, kwargs)
+
+ elif operation == "retrieveObject":
+ result = self._retrieve_object(uuid, *args, **kwargs)
+
+ ### DUMP - result is static, so will be dumped as plain text
+ self.debuger.dump_invoke(result, uuid, operation, args, kwargs)
+
else:
- obj = self.get_object(uuid)
-
- method = getattr(obj, operation)
+ newuuid = self.make_uuid()
- # 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)
+ ### DUMP - result is a uuid that encoded an dynamically generated
+ ### object
+ self.debuger.dump_invoke(newuuid, uuid, operation, args, kwargs)
+
+ if uuid.startswith(SINGLETON):
+ obj = self._singleton(uuid)
+ else:
+ obj = self.get_object(uuid)
+
+ method = getattr(obj, operation)
- result = method(*realargs, **realkwargs)
+ # 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)
- if result is None or \
- isinstance(result, bool):
- return result
-
- newuuid = self.make_uuid()
- self._objects[newuuid] = result
+ result = method(*realargs, **realkwargs)
- return newuuid
+ # If the result is an object (not a base value),
+ # then keep track of the object a return the object
+ # reference (newuuid)
+ if not (result is None or type(result) in [
+ bool, float, long, str, int]):
+ self._objects[newuuid] = result
+ result = newuuid
+
+ ### DEBUG
+ self.logger.debug("RET INVOKE %s%s = %s -> %s(%s, %s) " % (
+ "(uuid %s) " % str(newuuid) if newuuid else "", str(result), uuid,
+ operation, str(args), str(kwargs)))
+ ########
+
+ return result
def _set_attr(self, obj, name, ns3_value):
obj.SetAttribute(name, ns3_value)
def set(self, uuid, name, value):
+ ### DEBUG
+ self.logger.debug("SET %s %s %s" % (uuid, name, str(value)))
+
+ ### DUMP
+ self.debuger.dump_set(uuid, name, value)
+
obj = self.get_object(uuid)
type_name = obj.GetInstanceTypeId().GetName()
ns3_value = self._attr_from_string_to_ns3_value(type_name, name, value)
if not event_executed[0]:
self._set_attr(obj, name, ns3_value)
+ ### DEBUG
+ self.logger.debug("RET SET %s = %s -> set(%s, %s)" % (str(value), uuid, name,
+ str(value)))
+
return value
def _get_attr(self, obj, name, ns3_value):
obj.GetAttribute(name, ns3_value)
def get(self, uuid, name):
+ ### DEBUG
+ self.logger.debug("GET %s %s" % (uuid, name))
+
+ ### DUMP
+ self.debuger.dump_get(uuid, name)
+
obj = self.get_object(uuid)
type_name = obj.GetInstanceTypeId().GetName()
ns3_value = self._create_attr_ns3_value(type_name, name)
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)
+ result = self._attr_from_ns3_value_to_string(type_name, name, ns3_value)
+
+ ### DEBUG
+ self.logger.debug("RET GET %s = %s -> get(%s)" % (str(result), uuid, name))
+
+ return result
def start(self):
+ ### DUMP
+ self.debuger.dump_start()
+
# Launch the simulator thread and Start the
# simulator in that thread
self._condition = threading.Condition()
args = [self._condition])
self._simulator_thread.setDaemon(True)
self._simulator_thread.start()
- self._started = True
+
+ ### DEBUG
+ self.logger.debug("START")
def stop(self, time = None):
+ ### DUMP
+ self.debuger.dump_stop(time=time)
+
if time is None:
self.ns3.Simulator.Stop()
else:
self.ns3.Simulator.Stop(self.ns3.Time(time))
+ ### DEBUG
+ self.logger.debug("STOP time=%s" % str(time))
+
def shutdown(self):
+ ### DUMP
+ self.debuger.dump_shutdown()
+
while not self.ns3.Simulator.IsFinished():
#self.logger.debug("Waiting for simulation to finish")
time.sleep(0.5)
sys.stdout.flush()
sys.stderr.flush()
+ ### DEBUG
+ self.logger.debug("SHUTDOWN")
+
def _simulator_run(self, condition):
# Run simulation
self.ns3.Simulator.Run()
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()])
+ for k,v in kwargs.items()])
realkwargs = dict([(k, self._singleton(v) \
if str(v).startswith(SINGLETON) else v )\
- for k, v in realkwargs.iteritems()])
+ for k, v in realkwargs.items()])
return realkwargs
- def _is_app_running(self, uuid):
+ def _is_app_running(self, uuid):
now = self.ns3.Simulator.Now()
if now.IsZero():
return False
- stop_value = self.get(uuid, "StopTime")
- stop_time = self.ns3.Time(stop_value)
+ if self.ns3.Simulator.IsFinished():
+ 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()
- start_value = self.get(uuid, "StartTime")
- start_time = self.ns3.Time(start_value)
+ if now.Compare(start_time) >= 0:
+ if stop_time.IsZero() or now.Compare(stop_time) < 0:
+ return True
+
+ return False
+
+ def _is_app_started(self, uuid):
+ return self._is_app_running(uuid) or self.is_finished
+
+ 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
- self.logger.debug("NOW %s" % now.GetSeconds())
- self.logger.debug("START TIME %s" % start_value)
- self.logger.debug("STOP TIME %s" % stop_value)
+ nexthop = self.ns3.Ipv4Address(nexthop)
- if now.Compare(start_time) >= 0 and now.Compare(stop_time) < 0:
- return True
+ 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 range(nifaces):
+ iface = ipv4.GetInterface(ifidx)
+ naddress = iface.GetNAddresses()
+ for addridx in range(naddress):
+ ifaddr = iface.GetAddress(addridx)
+ ifmask = ifaddr.GetMask()
+
+ ifindex = ipv4.GetInterfaceForPrefix(nexthop, ifmask)
+
+ if ifindex == ifidx:
+ return ifindex
+ return ifindex
+
+ def _retrieve_object(self, uuid, typeid, search = False):
+ obj = self.get_object(uuid)
+
+ type_id = self.ns3.TypeId()
+ tid = type_id.LookupByName(typeid)
+ nobj = obj.GetObject(tid)
+
+ newuuid = None
+ if search:
+ # search object
+ for ouuid, oobj in self._objects.items():
+ if nobj == oobj:
+ newuuid = ouuid
+ break
+ else:
+ newuuid = self.make_uuid()
+ self._objects[newuuid] = nobj
+
+ return newuuid
+
+ def _recv_fd(self, uuid):
+ """ Waits on a local address to receive a file descriptor
+ from a local process. The file descriptor is associated
+ to a FdNetDevice to stablish communication between the
+ simulation and what ever process writes on that file descriptor
+ """
+
+ def recvfd(sock, fdnd):
+ (fd, msg) = passfd.recvfd(sock)
+ # Store a reference to the endpoint to keep the socket alive
+ fdnd.SetFileDescriptor(fd)
+
+ import passfd
+ import socket
+ sock = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
+ sock.bind("")
+ address = sock.getsockname()
+
+ fdnd = self.get_object(uuid)
+ t = threading.Thread(target=recvfd, args=(sock,fdnd))
+ t.start()
+
+ return address
- return False