From 6c5f918917a34e7093f5fdace2755094ec70752e Mon Sep 17 00:00:00 2001 From: Alina Quereilhac Date: Sun, 9 Feb 2014 03:01:18 +0100 Subject: [PATCH] Remote ns-3 with ping working --- src/nepi/resources/linux/application.py | 2 +- src/nepi/resources/linux/ns3/ns3client.py | 11 ++- src/nepi/resources/linux/ns3/ns3simulation.py | 17 ++-- src/nepi/resources/ns3/ns3application.py | 28 +----- src/nepi/resources/ns3/ns3base.py | 2 + src/nepi/resources/ns3/ns3client.py | 3 + src/nepi/resources/ns3/ns3server.py | 50 +++++----- src/nepi/resources/ns3/ns3simulation.py | 7 +- src/nepi/resources/ns3/ns3wrapper.py | 91 ++++++++++--------- test/resources/linux/ns3/ns3client.py | 15 ++- test/resources/linux/ns3/ns3simulation.py | 26 +++--- test/resources/ns3/ns3wrapper.py | 7 ++ 12 files changed, 144 insertions(+), 115 deletions(-) diff --git a/src/nepi/resources/linux/application.py b/src/nepi/resources/linux/application.py index 642b287c..4e9cce78 100644 --- a/src/nepi/resources/linux/application.py +++ b/src/nepi/resources/linux/application.py @@ -593,7 +593,7 @@ class LinuxApplication(ResourceManager): sudo = self._sudo_kill) # TODO: check if execution errors occurred - if proc.poll() or err: + if (proc and proc.poll()) or err: msg = " Failed to STOP command '%s' " % self.get("command") self.error(msg, out, err) diff --git a/src/nepi/resources/linux/ns3/ns3client.py b/src/nepi/resources/linux/ns3/ns3client.py index 59b04589..76a37bf8 100644 --- a/src/nepi/resources/linux/ns3/ns3client.py +++ b/src/nepi/resources/linux/ns3/ns3client.py @@ -94,16 +94,19 @@ class LinuxNS3Client(NS3Client): return self.send_msg(NS3WrapperMessage.GET, *args) - def trace(self, *args): - return self.send_msg(NS3WrapperMessage.TRACE, *args) + def enable_trace(self, *args): + return self.send_msg(NS3WrapperMessage.ENABLE_TRACE, *args) + + def flush(self): + return self.send_msg(NS3WrapperMessage.FLUSH, []) def start(self): return self.send_msg(NS3WrapperMessage.START, []) def stop(self, time = None): - args = None + args = [] if time: - args = [time] + args.append(time) return self.send_msg(NS3WrapperMessage.STOP, *args) diff --git a/src/nepi/resources/linux/ns3/ns3simulation.py b/src/nepi/resources/linux/ns3/ns3simulation.py index e3d41f7a..6fbcb73a 100644 --- a/src/nepi/resources/linux/ns3/ns3simulation.py +++ b/src/nepi/resources/linux/ns3/ns3simulation.py @@ -35,7 +35,8 @@ class LinuxNS3Simulation(LinuxApplication, NS3Simulation): @classmethod def _register_attributes(cls): ns_log = Attribute("nsLog", - "NS_LOG environment variable ", + "NS_LOG environment variable. " \ + " Will only generate output if ns-3 is compiled in DEBUG mode. ", flags = Flags.Design) verbose = Attribute("verbose", @@ -70,6 +71,10 @@ class LinuxNS3Simulation(LinuxApplication, NS3Simulation): return os.path.join("/", "tmp", self.socket_name) + def trace(self, name, attr = TraceAttr.ALL, block = 512, offset = 0): + self._client.flush() + return LinuxApplication.trace(self, name, attr, block, offset) + def upload_sources(self): self.node.mkdir(os.path.join(self.node.src_dir, "ns3wrapper")) @@ -156,6 +161,7 @@ class LinuxNS3Simulation(LinuxApplication, NS3Simulation): self.info("Starting ns-3 simulation") if self.state == ResourceState.READY: + self._client.start() self.set_started() else: msg = " Failed to execute command '%s'" % command @@ -167,7 +173,8 @@ class LinuxNS3Simulation(LinuxApplication, NS3Simulation): """ if self.state == ResourceState.STARTED: - # TODO: Stop simulation + self._client.stop() + self._client.shutdown() LinuxApplication.do_stop(self) def do_release(self): @@ -178,8 +185,6 @@ class LinuxNS3Simulation(LinuxApplication, NS3Simulation): self.node.execute(tear_down) self.do_stop() - - self._client.shutdown() super(LinuxApplication, self).do_release() @@ -193,12 +198,10 @@ class LinuxNS3Simulation(LinuxApplication, NS3Simulation): ns_log = self.get("nsLog") if ns_log: command.append("-L %s" % ns_log) + if self.get("verbose"): command.append("-v") - command.append("-H") - command.append(self.run_home) - command = " ".join(command) return command diff --git a/src/nepi/resources/ns3/ns3application.py b/src/nepi/resources/ns3/ns3application.py index c30a00c5..aa11a722 100644 --- a/src/nepi/resources/ns3/ns3application.py +++ b/src/nepi/resources/ns3/ns3application.py @@ -17,7 +17,7 @@ # # Author: Alina Quereilhac -from nepi.execution.resource import clsinit_copy +from nepi.execution.resource import clsinit_copy, ResourceState from nepi.resources.ns3.ns3base import NS3Base @clsinit_copy @@ -48,7 +48,6 @@ class NS3BaseApplication(NS3Base): self.simulation.invoke(node.uuid, "AddApplication", self.uuid) self._connected.add(node.uuid) - def do_stop(self): if self.state == ResourceState.STARTED: # No need to do anything, simulation.Destroy() will stop every object @@ -58,27 +57,10 @@ class NS3BaseApplication(NS3Base): @property def state(self): - #if self._state == ResourceState.STARTED: + if self._state == ResourceState.STARTED: + is_running = self.simulation.invoke(self.uuid, "isAppRunning") + if not is_running: + self._state = ResourceState.STOPPED return self._state - """ - now = testbed_instance.ns3.Simulator.Now() - if now.IsZero(): - return STATUS_NOT_STARTED - app = testbed_instance.elements[guid] - parameters = testbed_instance._get_parameters(guid) - if "StartTime" in parameters and parameters["StartTime"]: - start_value = parameters["StartTime"] - start_time = testbed_instance.ns3.Time(start_value) - if now.Compare(start_time) < 0: - return STATUS_NOT_STARTED - if "StopTime" in parameters and parameters["StopTime"]: - stop_value = parameters["StopTime"] - stop_time = testbed_instance.ns3.Time(stop_value) - if now.Compare(stop_time) < 0: - return STATUS_RUNNING - else: - return STATUS_FINISHED - return STATUS_UNDETERMINED - """ diff --git a/src/nepi/resources/ns3/ns3base.py b/src/nepi/resources/ns3/ns3base.py index adcbb8b4..358011a8 100644 --- a/src/nepi/resources/ns3/ns3base.py +++ b/src/nepi/resources/ns3/ns3base.py @@ -29,6 +29,8 @@ class NS3Base(ResourceManager): _rtype = "abstract::ns3::Object" _backend_type = "ns3" + SIMULATOR_UUID = "singleton::Simulator" + def __init__(self, ec, guid): super(NS3Base, self).__init__(ec, guid) self._uuid = None diff --git a/src/nepi/resources/ns3/ns3client.py b/src/nepi/resources/ns3/ns3client.py index 86bb77bf..c9e6d775 100644 --- a/src/nepi/resources/ns3/ns3client.py +++ b/src/nepi/resources/ns3/ns3client.py @@ -40,6 +40,9 @@ class NS3Client(object): def trace(self, *args): pass + def flush(self): + pass + def start(self): pass diff --git a/src/nepi/resources/ns3/ns3server.py b/src/nepi/resources/ns3/ns3server.py index becf6057..0b27a681 100644 --- a/src/nepi/resources/ns3/ns3server.py +++ b/src/nepi/resources/ns3/ns3server.py @@ -35,7 +35,8 @@ class NS3WrapperMessage: INVOKE = "INVOKE" SET = "SET" GET = "GET" - TRACE = "TRACE" + ENABLE_TRACE = "ENABLE_TRACE" + FLUSH = "FLUSH" START = "START" STOP = "STOP" SHUTDOWN = "SHUTDOWN" @@ -85,7 +86,8 @@ def handle_message(ns3_wrapper, msg, args): uuid = args.pop(0) operation = args.pop(0) - ns3_wrapper.logger.debug("INVOKE %s %s %s" % (uuid, operation, str(args))) + ns3_wrapper.logger.debug("INVOKE %s %s %s " % (uuid, operation, + str(args))) uuid = ns3_wrapper.invoke(uuid, operation, *args) return uuid @@ -104,14 +106,26 @@ def handle_message(ns3_wrapper, msg, args): name = args.pop(0) value = args.pop(0) - ns3_wrapper.logger.debug("SET %s %s" % (uuid, name, str(value))) + ns3_wrapper.logger.debug("SET %s %s %s" % (uuid, name, str(value))) value = ns3_wrapper.set(uuid, name, value) return value - if msg == NS3WrapperMessage.TRACE: - ns3_wrapper.logger.debug("TRACE") - return "NOT IMPLEMENTED" + if msg == NS3WrapperMessage.ENABLE_TRACE: + ns3_wrapper.logger.debug("ENABLE_TRACE") + + return "NOT YET IMPLEMENTED" + + if msg == NS3WrapperMessage.FLUSH: + # Forces flushing output and error streams. + # NS-3 output will stay unflushed until the program exits or + # explicit invocation flush is done + sys.stdout.flush() + sys.stderr.flush() + + ns3_wrapper.logger.debug("FLUSHED") + + return "FLUSHED" def create_socket(socket_name): sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) @@ -157,7 +171,7 @@ def send_reply(conn, reply): conn.send("%s\n" % encoded) def get_options(): - usage = ("usage: %prog -S -L -H -v ") + usage = ("usage: %prog -S -L -v ") parser = OptionParser(usage = usage) @@ -169,10 +183,6 @@ def get_options(): help = "NS_LOG environmental variable to be set", default = "", type="str") - parser.add_option("-H", "--homedir", dest="homedir", - help = "Home directory where to store results", - default = "", type="str") - parser.add_option("-v", "--verbose", help="Print debug output", action="store_true", @@ -180,11 +190,9 @@ def get_options(): (options, args) = parser.parse_args() - return (options.socket_name, options.homedir, options.verbose, - options.ns_log) + return (options.socket_name, options.verbose, options.ns_log) -def run_server(socket_name, homedir = None, level = logging.INFO, - ns_log = None): +def run_server(socket_name, level = logging.INFO, ns_log = None): # Sets NS_LOG environmental variable for NS debugging if ns_log: @@ -192,7 +200,7 @@ def run_server(socket_name, homedir = None, level = logging.INFO, ###### ns-3 wrapper instantiation - ns3_wrapper = NS3Wrapper(homedir = homedir, loglevel=level) + ns3_wrapper = NS3Wrapper(loglevel=level) ns3_wrapper.logger.info("STARTING...") @@ -237,7 +245,7 @@ def run_server(socket_name, homedir = None, level = logging.INFO, if __name__ == '__main__': - (socket_name, homedir, verbose, ns_log) = get_options() + (socket_name, verbose, ns_log) = get_options() ## configure logging FORMAT = "%(asctime)s %(name)s %(levelname)-4s %(message)s" @@ -245,12 +253,6 @@ if __name__ == '__main__': logging.basicConfig(format = FORMAT, level = level) - # Make sure to send DEBUG messages to stdout instead of stderr - root = logging.getLogger() - handler = logging.StreamHandler(sys.stdout) - handler.setLevel(logging.DEBUG) - root.addHandler(handler) - ## Run the server - run_server(socket_name, homedir, level, ns_log) + run_server(socket_name, level, ns_log) diff --git a/src/nepi/resources/ns3/ns3simulation.py b/src/nepi/resources/ns3/ns3simulation.py index 2baf1a34..abcf8473 100644 --- a/src/nepi/resources/ns3/ns3simulation.py +++ b/src/nepi/resources/ns3/ns3simulation.py @@ -37,8 +37,11 @@ class NS3Simulation(object): def get(self, uuid, name): return self.client.get(uuid, name) - def trace(self, *args): - return self.client.trace(*args) + def enable_trace(self, *args): + return self.client.enable_trace(*args) + + def flush(self): + return self.client.flush() def start(self): return self.client.start() diff --git a/src/nepi/resources/ns3/ns3wrapper.py b/src/nepi/resources/ns3/ns3wrapper.py index 9f84db70..00245bbb 100644 --- a/src/nepi/resources/ns3/ns3wrapper.py +++ b/src/nepi/resources/ns3/ns3wrapper.py @@ -94,26 +94,18 @@ def load_ns3_module(): return ns3mod class NS3Wrapper(object): - def __init__(self, homedir = None, loglevel = logging.INFO): + 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 self._logger = logging.getLogger("ns3wrapper") self._logger.setLevel(loglevel) @@ -161,10 +153,6 @@ class NS3Wrapper(object): return self._allowed_types - @property - def homedir(self): - return self._homedir - @property def logger(self): return self._logger @@ -217,11 +205,14 @@ class NS3Wrapper(object): return uuid def invoke(self, uuid, operation, *args): + if operation == "isAppRunning": + return self._is_app_running(uuid) + 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++ @@ -231,8 +222,8 @@ class NS3Wrapper(object): result = method(*realargs) if not result: - return None - + return result + newuuid = self.make_uuid() self._objects[newuuid] = result @@ -253,11 +244,15 @@ class NS3Wrapper(object): # 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 @@ -270,12 +265,15 @@ class NS3Wrapper(object): 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) @@ -301,7 +299,6 @@ class NS3Wrapper(object): #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() @@ -322,17 +319,16 @@ class NS3Wrapper(object): 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() @@ -342,21 +338,16 @@ class NS3Wrapper(object): # 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() @@ -432,3 +423,19 @@ class NS3Wrapper(object): return realargs + 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) + + start_value = self.get(uuid, "StartTime") + start_time = self.ns3.Time(start_value) + + if now.Compare(start_time) >= 0 and now.Compare(stop_time) <= 0: + return True + + return False + diff --git a/test/resources/linux/ns3/ns3client.py b/test/resources/linux/ns3/ns3client.py index aea015b0..afe75862 100644 --- a/test/resources/linux/ns3/ns3client.py +++ b/test/resources/linux/ns3/ns3client.py @@ -38,6 +38,15 @@ import threading import time import unittest +class DummySimulation(object): + def __init__(self, socket_name): + self.socket_name = socket_name + self.node = dict({'hostname': 'localhost'}) + + @property + def local_socket(self): + return self.socket_name + class LinuxNS3ClientTest(unittest.TestCase): def setUp(self): self.socket_name = os.path.join("/", "tmp", "NS3WrapperServer.sock") @@ -59,8 +68,11 @@ class LinuxNS3ClientTest(unittest.TestCase): # Verify that the communication socket was created self.assertTrue(os.path.exists(self.socket_name)) + # Create a dummy simulation object + simulation = DummySimulation(self.socket_name) + # Instantiate the NS3 client - client = LinuxNS3Client(self.socket_name) + client = LinuxNS3Client(simulation) # Define a real time simulation stype = client.create("StringValue", "ns3::RealtimeSimulatorImpl") @@ -181,6 +193,7 @@ class LinuxNS3ClientTest(unittest.TestCase): # wait until simulation is over client.shutdown() + ## TODO: Add assertions !! if __name__ == '__main__': unittest.main() diff --git a/test/resources/linux/ns3/ns3simulation.py b/test/resources/linux/ns3/ns3simulation.py index cf08f0e3..53c03a2a 100644 --- a/test/resources/linux/ns3/ns3simulation.py +++ b/test/resources/linux/ns3/ns3simulation.py @@ -36,22 +36,23 @@ import time import unittest class LinuxNS3ClientTest(unittest.TestCase): - def test_runtime_attr_modify(self): - ssh_key = "%s/.ssh/id_rsa_planetlab" % (os.environ['HOME']) + def setUp(self): + self.fedora_host = "nepi2.pl.sophia.inria.fr" + #self.fedora_host = "peeramide.irisa.fr" + self.fedora_user = "inria_test" + def test_simple_ping(self): ec = ExperimentController(exp_id = "test-ns3-simu") node = ec.register_resource("LinuxNode") - #ec.set(node, "hostname", "roseval.pl.sophia.inria.fr") - #ec.set(node, "username", "alina") - ec.set(node, "hostname", "peeramide.irisa.fr") - #ec.set(node, "hostname", "planetlab2.upc.es") - ec.set(node, "username", "inria_alina") - ec.set(node, "identity", ssh_key) + ec.set(node, "hostname", self.fedora_host) + ec.set(node, "username", self.fedora_user) ec.set(node, "cleanProcesses", True) + ec.set(node, "cleanHome", True) simu = ec.register_resource("LinuxNS3Simulation") ec.set(simu, "verbose", True) + ec.set(simu, "nsLog", "V4Ping:Node") ec.register_connection(simu, node) nsnode1 = ec.register_resource("ns3::Node") @@ -109,9 +110,12 @@ class LinuxNS3ClientTest(unittest.TestCase): ec.deploy() - #time.sleep(60) - ec.wait_started([ping]) - #ec.wait_finised([ping]) + ec.wait_finished([ping]) + + stdout = ec.trace(simu, "stdout") + + expected = "20 packets transmitted, 20 received, 0% packet loss" + self.assertTrue(stdout.find(expected) > -1) ec.shutdown() diff --git a/test/resources/ns3/ns3wrapper.py b/test/resources/ns3/ns3wrapper.py index 3843864c..87657616 100755 --- a/test/resources/ns3/ns3wrapper.py +++ b/test/resources/ns3/ns3wrapper.py @@ -32,7 +32,9 @@ from nepi.resources.ns3.ns3wrapper import NS3Wrapper +import StringIO import subprocess +import sys import time import unittest @@ -218,6 +220,7 @@ class NS3WrapperTest(unittest.TestCase): p.communicate() def test_start(self): + # Instantiate ns-3 wrapper = NS3Wrapper() ### create 2 nodes @@ -291,6 +294,8 @@ class NS3WrapperTest(unittest.TestCase): # wait until simulation is over wrapper.shutdown() + # TODO: Add assertions !! + def test_runtime_attr_modify(self): wrapper = NS3Wrapper() @@ -416,6 +421,8 @@ class NS3WrapperTest(unittest.TestCase): p = subprocess.Popen("rm /tmp/trace-p2p-*", shell = True) p.communicate() + + # TODO: Add assertions !! if __name__ == '__main__': unittest.main() -- 2.43.0