Rest interface to start/cleanup and run the cord tester test cases from webserver.
Sample test-tls curl to start tls test case.

To test, first install the flask with:
sudo pip install -r flask-requirements.txt
Then start webserver:
nohup ./webserver-run.py >/tmp/webserver-output.txt 2>&1 &
Then run the tls test case with the config and manifest as in:
./webserver-test-tls.curl
And monitor the output in /tmp/webserver-output.txt for the test.

Change-Id: I7d6cf121718664eb3f0ea0fedf71d91e6a17fdf9
diff --git a/src/test/setup/webserver-test-tls.curl b/src/test/setup/webserver-test-tls.curl
new file mode 100755
index 0000000..7cace25
--- /dev/null
+++ b/src/test/setup/webserver-test-tls.curl
@@ -0,0 +1,32 @@
+#!/usr/bin/env bash
+
+# example cord webserver post request to run tls test case
+
+cat > /tmp/testrun.json <<EOF
+{
+    "manifest" : "manifest.json",
+    "test" : "tls:eap_auth_exchange.test_eap_tls",
+    "config" : {
+        "VOLTHA_HOST" : "172.17.0.1",
+        "VOLTHA_REST_PORT" : 8881,
+        "VOLTHA_OLT_TYPE" : "ponsim_olt",
+        "VOLTHA_OLT_MAC" : "00:0c:e2:31:10:00"
+    }
+}
+EOF
+status=`curl -s -w "%{http_code}" -o /dev/null -H "Content-Type: application/json" -X POST -d '{ "manifest" : "manifest.json" }' http://localhost:5000/start`
+if [ $status -ne 200 ]; then
+  echo "Test setup failed with status code $status"
+  exit 1
+fi
+status=`curl -s -w "%{http_code}" -o /dev/null -H "Content-Type: application/json" -X POST -d @/tmp/testrun.json http://localhost:5000/test`
+if [ $status -ne 200 ]; then
+  echo "Test run failed with status code $status"
+  exit 1
+fi
+status=`curl -s -w "%{http_code}" -o /dev/null -H "Content-Type: application/json" -X POST -d '{ "manifest" : "manifest.json" }' http://localhost:5000/cleanup`
+if [ $status -ne 200 ]; then
+  echo "Test cleanup failed with status code $status"
+fi
+rm -f /tmp/testrun.json
+echo "Test successful"
\ No newline at end of file
diff --git a/src/test/setup/webserver/cordTestConfig.py b/src/test/setup/webserver/cordTestConfig.py
index 33bdc1e..df21675 100644
--- a/src/test/setup/webserver/cordTestConfig.py
+++ b/src/test/setup/webserver/cordTestConfig.py
@@ -4,10 +4,58 @@
 import json
 import os
 import sys
+import copy
+
+class CordTesterRun(object):
+    our_path = os.path.dirname(os.path.realpath(__file__))
+    exec_base = os.path.realpath(os.path.join(our_path, '..'))
+
+    @classmethod
+    def start(cls, manifest):
+        status = False
+        manifest_file = os.path.join(cls.exec_base, manifest)
+        if os.access(manifest_file, os.F_OK):
+            cmd = 'sudo {}/cord-test.py setup -m {}'.format(cls.exec_base, manifest_file)
+            ret = os.system(cmd)
+            status = True if ret == 0 else False
+
+        return status
+
+    @classmethod
+    def cleanup(cls, manifest):
+        status = False
+        manifest_file = os.path.join(cls.exec_base, manifest)
+        if os.access(manifest_file, os.F_OK):
+            cmd = 'sudo {}/cord-test.py cleanup -m {}'.format(cls.exec_base, manifest_file)
+            os.system(cmd)
+            status = True
+
+        return status
+
+    @classmethod
+    def test(cls, manifest, test, config = None):
+        manifest_file = os.path.join(cls.exec_base, manifest)
+        if not os.access(manifest_file, os.F_OK):
+            return False
+        #get test case as we could give a specific test to execute within a test case
+        test_case = test.split(':')[0]
+        cordWeb = CordTesterWebConfig(test_case)
+        if config:
+            status = cordWeb.update(config)
+            #test case is invalid
+            if status is False:
+                return status
+        cmd = 'sudo {}/cord-test.py run -m {} -t {}'.format(cls.exec_base, manifest_file, test)
+        ret = os.system(cmd)
+        status = True if ret == 0 else False
+        if config:
+            cordWeb.restore()
+        return status
 
 class CordTesterWebConfig(object):
     our_path = os.path.dirname(os.path.realpath(__file__))
     test_base = os.path.realpath(os.path.join(our_path, '..', '..'))
+    restore_config = {}
 
     def __init__(self, test_case):
         self.test_case = test_case
@@ -24,7 +72,7 @@
             if os.access(self.test_config, os.F_OK):
                 with open(self.test_config, 'r') as f:
                     cur_config = json.load(f)
-                os.rename(self.test_config, '{}.save'.format(self.test_config))
+                self.save(copy.copy(cur_config))
             for k, v in config.iteritems():
                 cur_config[k] = v
                 with open(self.test_config, 'w') as f:
@@ -32,13 +80,18 @@
             return True
         return False
 
+    def save(self, cur_config):
+        self.restore_config[self.test_case] = cur_config
+
     def restore(self):
+        config = None
         if self.test_config:
-            if os.access(self.test_config, os.F_OK):
-                restore_file = '{}.save'.format(self.test_config)
-                if os.access(restore_file, os.F_OK):
-                    os.rename(restore_file, self.test_config)
+            if self.test_case in self.restore_config:
+                config = self.restore_config[self.test_case]
+                with open(self.test_config, 'w') as f:
+                    json.dump(config, f, indent = 4)
                 return True
+
         return False
 
     def get(self):
@@ -89,3 +142,34 @@
         if status:
             response = ('', httplib.OK)
     return response
+
+@app.route('/start', methods = ['POST'])
+def start():
+    data = request.get_json(force = True)
+    manifest = data.get('manifest', 'manifest.json')
+    status = CordTesterRun.start(manifest)
+    if status:
+        return ('', httplib.OK)
+    return ('', httplib.NOT_ACCEPTABLE)
+
+@app.route('/cleanup', methods = ['POST'])
+def cleanup():
+    data = request.get_json(force = True)
+    manifest = data.get('manifest', 'manifest.json')
+    status = CordTesterRun.cleanup(manifest)
+    if status:
+        return ('', httplib.OK)
+    return ('', httplib.NOT_ACCEPTABLE)
+
+@app.route('/test', methods = ['POST'])
+def test():
+    data = request.get_json(force = True)
+    manifest = data.get('manifest', 'manifest.json')
+    test = data.get('test', None)
+    config = data.get('config', None)
+    if test is None:
+        return ('', httplib.NOT_FOUND)
+    status = CordTesterRun.test(manifest, test, config = config)
+    if status:
+        return ('', httplib.OK)
+    return ('', httplib.NOT_ACCEPTABLE)