Changes to support cord-tester to test restarts with existing ONOS cord instance.
This wraps the existing configuration of ONOS cord instances already running, namely:
 a) onoscord
 b) onosfabric
on Cord and provides cord-tester restart test cases to work through the cord-tester agent running on CORD.
A cleanup should restore back to the ONOS cord instance by restoring back the saved running config.

Change-Id: Ic4d52cb7886a958a69c18b27bf010f99863ffdd5
diff --git a/src/test/setup/cord-test.py b/src/test/setup/cord-test.py
index 37d816f..1f95311 100755
--- a/src/test/setup/cord-test.py
+++ b/src/test/setup/cord-test.py
@@ -398,10 +398,10 @@
 
 def runTest(args):
     #Start the cord test tcp server
-    test_server_params = args.server.split(':')
+    test_manifest = TestManifest(args = args)
+    test_server_params = test_manifest.server.split(':')
     test_host = test_server_params[0]
     test_port = CORD_TEST_PORT
-    test_manifest = TestManifest(args = args)
     if len(test_server_params) > 1:
         test_port = int(test_server_params[1])
     try:
@@ -668,7 +668,7 @@
        for c in update_map.keys():
            update_map[c] = True
 
-    onos_cord_loc = args.onos_cord
+    onos_cord_loc = test_manifest.onos_cord
     if onos_cord_loc:
         if onos_cord_loc.find(os.path.sep) < 0:
             onos_cord_loc = os.path.join(os.getenv('HOME'), onos_cord_loc)
@@ -695,18 +695,9 @@
     head_node = test_manifest.head_node
     iterations = test_manifest.iterations
 
-    ##If onos/radius was already started
-    if args.test_controller:
-        ips = args.test_controller.split('/')
-        onos_ip = ips[0]
-        if len(ips) > 1:
-            radius_ip = ips[1]
-        else:
-            radius_ip = None
-
     onos_cord = None
     if onos_cord_loc:
-        if not args.test_controller:
+        if not test_manifest.onos_ip:
             ##Unexpected case. Specify the external controller ip when running on cord node
             print('Specify ONOS ip using \"-e\" option when running the cord-tester on cord node')
             sys.exit(1)
@@ -801,7 +792,7 @@
                         network = test_manifest.docker_network)
         print('Quagga started')
 
-    params = args.server.split(':')
+    params = test_manifest.server.split(':')
     ip = params[0]
     port = CORD_TEST_PORT
     if len(params) > 1:
@@ -868,6 +859,17 @@
         args.olt = manifest.olt
         args.onos = manifest.onos_image
         args.server = manifest.server
+        args.onos_ip = manifest.onos_ip
+        args.radius_ip = manifest.radius_ip
+        args.onos_cord = manifest.onos_cord
+    else:
+        args.onos_ip = None
+        args.radius_ip = None
+        if args.test_controller:
+            ips = args.test_controller.split('/')
+            args.onos_ip = ips[0]
+            if len(ips) > 1:
+                args.radius_ip = ips[1]
 
     image_name = args.onos
     prefix = args.prefix
@@ -890,6 +892,10 @@
             Onos.remove_data_map(volume, Onos.guest_data_dir)
         Onos.cleanup_runtime()
 
+    if args.onos_cord:
+        #restore the ONOS cord instance
+        OnosCord.restore_onos_cord(args.onos_cord, args.onos_ip)
+
     if args.xos:
         ##cleanup XOS images
         xos_images = ( '{}:{}'.format(XosServer.IMAGE,XosServer.TAG),
@@ -1095,6 +1101,8 @@
                             help='Specify the log level for the test cases')
     parser_run.add_argument('-jvm-heap-size', '--jvm-heap-size', default='', type=str, help='ONOS JVM heap size')
     parser_run.add_argument('-network', '--network', default='', type=str, help='Docker network to attach')
+    parser_run.add_argument('-onos-cord', '--onos-cord', default='', type=str,
+                            help='Specify config location for ONOS cord when running on podd')
     parser_run.set_defaults(func=runTest)
 
     parser_setup = subparser.add_parser('setup', help='Setup cord tester environment')
@@ -1115,8 +1123,8 @@
                               choices=['DEBUG','TRACE','ERROR','WARN','INFO'],
                               help='Specify the log level for the test cases')
     parser_setup.add_argument('-s', '--start-switch', action='store_true', help='Start OVS when running under OLT config')
-    parser_setup.add_argument('-c', '--onos-cord', default='', type=str,
-                              help='Specify cord location for ONOS cord when running on podd')
+    parser_setup.add_argument('-onos-cord', '--onos-cord', default='', type=str,
+                              help='Specify config location for ONOS cord when running on podd')
     parser_setup.add_argument('-m', '--manifest', default='', type=str, help='Provide test configuration manifest')
     parser_setup.add_argument('-p', '--prefix', default='', type=str, help='Provide container image prefix')
     parser_setup.add_argument('-i', '--identity-file', default=identity_file_default,
@@ -1168,6 +1176,11 @@
                                 help='Cleanup XOS containers')
     parser_cleanup.add_argument('-r', '--server', default=cord_test_server_address, type=str,
                                 help='ip:port address for cord test server to cleanup')
+    parser_cleanup.add_argument('-e', '--test-controller', default='', type=str,
+                                help='External test controller ip for Onos and/or radius server. '
+                                'Eg: 10.0.0.2/10.0.0.3 to specify ONOS and Radius ip')
+    parser_cleanup.add_argument('-onos-cord', '--onos-cord', default='', type=str,
+                                help='Specify config location for ONOS cord instance when running on podd to restore')
     parser_cleanup.add_argument('-m', '--manifest', default='', type=str, help='Provide test manifest')
     parser_cleanup.set_defaults(func=cleanupTests)
 
diff --git a/src/test/utils/CordContainer.py b/src/test/utils/CordContainer.py
index 29d1d8c..df9a196 100644
--- a/src/test/utils/CordContainer.py
+++ b/src/test/utils/CordContainer.py
@@ -296,6 +296,7 @@
     onos_config_dir_guest = '/root/onos/config'
     onos_config_dir = os.path.join(onos_cord_dir, 'config')
     docker_yaml = os.path.join(onos_cord_dir, 'docker-compose.yml')
+    onos_cfg_save_loc = os.path.join(onos_cord_dir, 'network-cfg.json.saved')
 
     def __init__(self, onos_ip, conf, boot_delay = 60):
         self.onos_ip = onos_ip
@@ -330,6 +331,11 @@
         self.xos_onos_name = '{}_{}_1'.format(cord_conf_dir_basename, image)
         ##Create an container instance of xos onos
         self.xos_onos = Container(self.xos_onos_name, image, tag = '')
+        #fetch the current config of onos cord instance
+        try:
+            self.last_cfg = OnosCtrl.get_config(controller = onos_ip)
+        except:
+            self.last_cfg = None
 
     def start(self, restart = False, network_cfg = None):
         if restart is True:
@@ -338,6 +344,11 @@
                 print('Killing container %s' %self.name)
                 self.kill()
             if self.xos_onos.exists():
+                if self.last_cfg is not None:
+                    #save the current network config of onos cord instance
+                    json_data = json.dumps(self.last_cfg, indent=4)
+                    with open(self.onos_cfg_save_loc, 'w') as f:
+                        f.write(json_data)
                 print('Killing container %s' %self.xos_onos.name)
                 self.xos_onos.kill()
 
@@ -357,6 +368,45 @@
         build_cmd = 'cd {} && docker-compose build'.format(self.onos_cord_dir)
         os.system(build_cmd)
 
+    @classmethod
+    def restore_onos_cord(cls, onos_cord, onos_ip):
+        #bring down the onos cord wrapper container
+        #if there is no saved config, there is nothing to restore as it was never restarted
+        if not os.access(cls.onos_cfg_save_loc, os.F_OK):
+            return
+        if not onos_cord or not os.access(onos_cord, os.F_OK):
+            return
+
+        print('Stopping the existing ONOS cord wrapper instance at %s' %(cls.onos_cord_dir))
+        cmd = 'cd {} && docker-compose down'.format(cls.onos_cord_dir)
+        try:
+            os.system(cmd)
+        except:pass
+
+        print('Starting the ONOS cord instance at %s' %(onos_cord))
+        #bring back up the onos cord container
+        cmd = 'cd {} && docker-compose up -d'.format(onos_cord)
+        try:
+            os.system(cmd)
+            time.sleep(30)
+        except:
+            pass
+
+        #now restore back the old config
+        print('Restoring back the saved ONOS cord config at %s for ONOS cord instance' %(cls.onos_cfg_save_loc))
+        with open(cls.onos_cfg_save_loc, 'r') as f:
+            config = json.load(f)
+            try:
+                OnosCtrl.config(config, controller = onos_ip)
+                os.unlink(cls.onos_cfg_save_loc)
+            except: pass
+
+        print('Cleaning up the ONOS cord wrapper directory at %s' %(cls.onos_cord_dir))
+        try:
+            os.system('rm -rf {}'.format(cls.onos_cord_dir))
+        except:
+            pass
+
 class OnosCordStopWrapper(Container):
     onos_cord_dir = os.path.join(os.getenv('HOME'), 'cord-tester-cord')
     docker_yaml = os.path.join(onos_cord_dir, 'docker-compose.yml')
diff --git a/src/test/utils/OnosCtrl.py b/src/test/utils/OnosCtrl.py
index 6401a18..bd931a7 100644
--- a/src/test/utils/OnosCtrl.py
+++ b/src/test/utils/OnosCtrl.py
@@ -60,16 +60,24 @@
         if config is not None:
             json_data = json.dumps(config)
 	    if controller is None:
-	        print('default Onos config url is %s'%cls.cfg_url)
                 resp = requests.post(cls.cfg_url, auth = cls.auth, data = json_data)
 	    else:
 		cfg_url = 'http://%s:8181/onos/v1/network/configuration/' %(controller)
-		print('non-default Onos config url is %s'%cfg_url)
 	        resp = requests.post(cfg_url, auth = cls.auth, data = json_data)
             return resp.ok, resp.status_code
         return False, 400
 
     @classmethod
+    def get_config(cls, controller=None):
+	if controller is None:
+            controller = cls.controller
+	cfg_url = 'http://%s:8181/onos/v1/network/configuration/' %(controller)
+	resp = requests.get(cfg_url, auth = cls.auth)
+        if resp.ok:
+            return resp.json()
+        return None
+
+    @classmethod
     def delete(cls, config, controller=None):
         if config:
             json_data = json.dumps(config)
@@ -78,7 +86,6 @@
                 resp = requests.delete(cls.cfg_url, auth = cls.auth, data = json_data)
 	    else:
 		cfg_url = 'http://%s:8181/onos/v1/network/configuration/' %(controller)
-		print('non-default Onos config url is %s'%cfg_url)
 	        resp = requests.delete(cfg_url, auth = cls.auth, data = json_data)
             return resp.ok, resp.status_code
         return False, 400
diff --git a/src/test/utils/TestManifest.py b/src/test/utils/TestManifest.py
index fdd35ee..792926a 100644
--- a/src/test/utils/TestManifest.py
+++ b/src/test/utils/TestManifest.py
@@ -35,15 +35,26 @@
             self.start_switch = args.start_switch
             self.image_prefix = args.prefix
             self.onos_image = args.onos
+            self.test_controller = args.test_controller
+            if self.test_controller:
+                ips = self.test_controller.split('/')
+                self.onos_ip = ips[0]
+                if len(ips) > 1:
+                    self.radius_ip = ips[1]
+            self.onos_cord = args.onos_cord if args.onos_cord else None
             self.docker_network = args.network if args.network else None
             self.iterations = None
-            self.server = '{}:{}'.format(CORD_TEST_HOST, CORD_TEST_PORT)
+            self.server = args.server
             self.jvm_heap_size = args.jvm_heap_size if args.jvm_heap_size else None
         else:
             with open(self.manifest, 'r') as fd:
                 data = json.load(fd)
             self.onos_ip = data.get('onos', None)
             self.radius_ip = data.get('radius', None)
+            self.test_controller = '' if self.onos_ip is None else self.onos_ip
+            if self.onos_ip and self.radius_ip:
+                self.test_controller = '{}/{}'.format(self.onos_ip, self.radius_ip)
+            self.onos_cord = data.get('onos_cord', None)
             self.head_node = data.get('head_node', platform.node())
             self.log_level = data.get('log_level', 'INFO').upper()
             self.onos_instances = data.get('onos_instances', 1)