Fixing DCE application status, adding is_app_started function
[nepi.git] / src / nepi / resources / ns3 / ns3wrapper.py
index b7ed192..d61be5b 100644 (file)
@@ -30,11 +30,10 @@ 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
@@ -62,9 +61,13 @@ def load_ns3_module():
             # 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)
 
@@ -98,7 +101,7 @@ def load_ns3_module():
     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
@@ -127,6 +130,14 @@ class NS3Wrapper(object):
         # Collection of allowed ns3 classes
         self._allowed_types = None
 
+        # Object to dump instructions to reproduce and debug experiment
+        from 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:
@@ -163,7 +174,20 @@ class NS3Wrapper(object):
 
     @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()
@@ -172,10 +196,21 @@ class NS3Wrapper(object):
         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)
 
@@ -185,16 +220,30 @@ class NS3Wrapper(object):
 
         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++
@@ -203,44 +252,104 @@ class NS3Wrapper(object):
        
         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)
-        if operation == "addStaticRoute":
-            return self._add_static_route(uuid, *args)
+        ### 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
+
+        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)
 
-        if uuid.startswith(SINGLETON):
-            obj = self._singleton(uuid)
+        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)
 
-        result = method(*realargs, **realkwargs)
+            if uuid.startswith(SINGLETON):
+                obj = self._singleton(uuid)
+            else:
+                obj = self.get_object(uuid)
+            
+            method = getattr(obj, operation)
 
-        if result is None or \
-                isinstance(result, bool):
-            return result
-      
-        newuuid = self.make_uuid()
-        self._objects[newuuid] = result
+            # 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)
 
-        return newuuid
+            result = method(*realargs, **realkwargs)
+
+            # 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)
@@ -263,12 +372,22 @@ class NS3Wrapper(object):
         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)
@@ -283,9 +402,17 @@ class NS3Wrapper(object):
         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()
@@ -294,15 +421,26 @@ class NS3Wrapper(object):
                 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)
@@ -318,6 +456,9 @@ class NS3Wrapper(object):
         sys.stdout.flush()
         sys.stderr.flush()
 
+        ### DEBUG
+        self.logger.debug("SHUTDOWN")
+
     def _simulator_run(self, condition):
         # Run simulation
         self.ns3.Simulator.Run()
@@ -443,11 +584,14 @@ class NS3Wrapper(object):
 
         return realkwargs
 
-    def _is_app_running(self, uuid): 
+    def _is_app_running(self, uuid):
         now = self.ns3.Simulator.Now()
         if now.IsZero():
             return False
 
+        if self.ns3.Simulator.IsFinished():
+            return False
+
         app = self.get_object(uuid)
         stop_time_value = self.ns3.TimeValue()
         app.GetAttribute("StopTime", stop_time_value)
@@ -457,10 +601,14 @@ class NS3Wrapper(object):
         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
+        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)
@@ -511,3 +659,48 @@ class NS3Wrapper(object):
                     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.iteritems():
+                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
+
+