Arbitrary arguments

refs: #4360

Change-Id: I1c746c3349a72d1e262b75d4a864f096a2dfc018
diff --git a/bin/minindn b/bin/minindn
index bf255df..4a1feb0 100755
--- a/bin/minindn
+++ b/bin/minindn
@@ -98,10 +98,13 @@
 
     def __call__(self, parser, namespace, values, option_string=None):
         experimentNames = ExperimentManager.getExperimentNames()
+        experimentArgs = ExperimentManager.getExperimentArgs()
 
         print("Mini-NDN experiments:")
         for experiment in experimentNames:
-            print("  %s" % experiment)
+            print("  {}".format(experiment))
+            if experiment in experimentArgs:
+                print("      ({})".format(experimentArgs[experiment]))
 
         sys.exit(0)
 
@@ -126,6 +129,7 @@
         self.placer = None
         self.tunnelType = None
         self.faceType = "udp"
+        self.arbArgs = {}
 
 def createResultsDir(resultDir, faces, rType):
     if faces == 0:
@@ -211,6 +215,14 @@
 
     parser.add_argument("--face-type", dest='faceType', default='udp', choices=['udp', 'tcp'])
 
+    args, unknownArgs = parser.parse_known_args()
+
+    unknownArgsList = []
+    for arg in unknownArgs:
+        if arg.startswith(("--")):
+            parser.add_argument(arg)
+            unknownArgsList.append(arg.split("--")[1])
+
     args = parser.parse_args()
 
     options = ProgramOptions()
@@ -234,6 +246,10 @@
     options.placeList = args.placeList
     options.faceType = args.faceType
 
+    for k in args.__dict__:
+        if k in unknownArgsList:
+            options.arbArgs[k] = args.__dict__[k]
+
     if options.experimentName is not None and options.experimentName not in ExperimentManager.getExperimentNames():
         print("No experiment named %s" % options.experimentName)
         sys.exit(1)
@@ -439,7 +455,9 @@
             "nPings": options.nPings,
             "strategy": Nfd.STRATEGY_BEST_ROUTE,
             "pctTraffic": options.pctTraffic,
-            "nlsrSecurity": options.nlsrSecurity
+            "nlsrSecurity": options.nlsrSecurity,
+            "workDir": options.workDir,
+            "arbArgs" : options.arbArgs
         }
 
         experiment = ExperimentManager.create(experimentName, experimentArgs)
diff --git a/docs/EXPERIMENTS.md b/docs/EXPERIMENTS.md
index 3b83226..547c2e8 100644
--- a/docs/EXPERIMENTS.md
+++ b/docs/EXPERIMENTS.md
@@ -179,3 +179,23 @@
                 host.cmd("nfdc status report > status.txt")
 
     Experiment.register("example-name", ExampleExperiment)
+
+## Passing arbitrary arguments to experiments
+
+One can pass any arbitrary argument to the Mini-NDN command line
+as long as the arguments don't clash with Mini-NDN's arguments.
+This feature allows users to pass any argument to Mini-NDN and process
+them in an experiment without modifying Mini-NDN's core.
+
+Please look at `ndn/experiments/arbitrary_arguments_experiment.py`
+to see how these arguments can be accessed. To have the experiment
+options printed in `sudo minindn --list-experiments` when using
+arbitrary arguments one can add the static `arguments` method as
+shown in the aforementioned experiment.
+
+To run the experiment:
+
+    sudo minindn --experiment arbitrary-arguments --ds 400 --logging false
+
+The experiment will print out the supplied arbitrary values which are --ds
+and --logging. --experiment is a fixed argument of Mini-NDN.
\ No newline at end of file
diff --git a/ndn/experiment_manager.py b/ndn/experiment_manager.py
index f505646..5f2a174 100644
--- a/ndn/experiment_manager.py
+++ b/ndn/experiment_manager.py
@@ -35,6 +35,7 @@
 
     def __init__(self):
         self.experiments = {}
+        self.expArgs = {}
 
     def loadModules(self):
         currentDir = os.path.dirname(__file__)
@@ -51,6 +52,12 @@
     def register(self, name, experimentClass):
         if name not in self.experiments:
             self.experiments[name] = experimentClass
+            try:
+                helpStr = experimentClass.arguments()
+                if type(helpStr) is str:
+                    self.expArgs[name] = experimentClass.arguments()
+            except:
+                pass
         else:
             raise _ExperimentManager.Error("Experiment '%s' has already been registered" % name)
 
@@ -84,3 +91,8 @@
         experimentNames.append(key)
 
     return experimentNames
+
+def getExperimentArgs():
+    manager = __getInstance()
+
+    return manager.expArgs
diff --git a/ndn/experiments/arbitrary_arguments_experiment.py b/ndn/experiments/arbitrary_arguments_experiment.py
new file mode 100644
index 0000000..159e2a7
--- /dev/null
+++ b/ndn/experiments/arbitrary_arguments_experiment.py
@@ -0,0 +1,32 @@
+from ndn.experiments.experiment import Experiment
+
+class AbitraryArgumentsExperiment(Experiment):
+    def __init__(self, args):
+        Experiment.__init__(self, args)
+        if "ds" in self.arbArgs:
+            self.ds = int(self.arbArgs["ds"])
+        else:
+            self.ds = 1000
+
+        if "logging" in self.arbArgs:
+            self.logging = self.arbArgs["logging"]
+            if self.logging == "true":
+                self.logging = True
+            else:
+                self.logging = False
+        else:
+            self.logging = False
+
+    def setup(self):
+        pass
+
+    def run(self):
+        print("Argument ds: {}".format(self.ds))
+        print("Argument logging: {}".format(self.logging))
+
+    @staticmethod
+    def arguments():
+        ''' This will be printed in sudo minindn --list-experiments'''
+        return "--ds <num-data-streams> --logging <true/false>"
+
+Experiment.register("arbitrary-arguments", AbitraryArgumentsExperiment)
\ No newline at end of file
diff --git a/ndn/experiments/experiment.py b/ndn/experiments/experiment.py
index 4be2aac..23efebd 100644
--- a/ndn/experiments/experiment.py
+++ b/ndn/experiments/experiment.py
@@ -36,6 +36,7 @@
         self.strategy = args["strategy"]
         self.pctTraffic = args["pctTraffic"]
         self.nlsrSecurity = args["nlsrSecurity"]
+        self.arbArgs = args["arbArgs"]
 
         # Used to restart pings on the recovered node if any
         self.pingedDict = {}