Complete overhaul of the implementation of ONOS cord instance wrapper for cord-tester.
It now patches the onos-cord compose instance in place and restores it back on cleanup.
A sample manifest-cord.json illustrates the usage if one wants to wrap the existing onos-cord running instance
in CiaB head-node (called prod)
This is required because of the manner in which the onos-cord instance networks are created automatically matching the docker networks that already exist for the onos instances.
The service profile spec allows one to synchronize the configuration/apps for the onos-cord running instance.
Change-Id: Icd066d8b953eccb7dcd7330775b548d36a7fb33e
diff --git a/src/test/utils/CordContainer.py b/src/test/utils/CordContainer.py
index 1a7656b..5f1ea2c 100644
--- a/src/test/utils/CordContainer.py
+++ b/src/test/utils/CordContainer.py
@@ -25,7 +25,7 @@
from nsenter import Namespace
from docker import Client
from docker import utils as dockerutils
-from shutil import rmtree
+import shutil
from OnosCtrl import OnosCtrl
from OnosLog import OnosLog
from threadPool import ThreadPool
@@ -292,132 +292,122 @@
class OnosCord(Container):
"""Use this when running the cord tester agent on the onos compute node"""
- onos_cord_dir = os.path.join(os.getenv('HOME'), 'cord-tester-cord')
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):
+ def __init__(self, onos_ip, conf, service_profile, synchronizer, start = True, boot_delay = 60):
+ if not os.access(conf, os.F_OK):
+ raise Exception('ONOS cord configuration location %s is invalid' %conf)
+ if not os.access(service_profile, os.F_OK):
+ raise Exception('ONOS cord service profile location is not accessible' %service_profile)
self.onos_ip = onos_ip
- self.cord_conf_dir = conf
+ self.onos_cord_dir = conf
self.boot_delay = boot_delay
- if os.access(self.cord_conf_dir, os.F_OK) and not os.access(self.onos_cord_dir, os.F_OK):
- os.mkdir(self.onos_cord_dir)
- os.mkdir(self.onos_config_dir)
- ##copy the config file from cord-tester-config
- cmd = 'cp {}/* {}'.format(self.cord_conf_dir, self.onos_cord_dir)
- os.system(cmd)
+ self.synchronizer = synchronizer
+ self.service_profile = service_profile
+ self.docker_yaml = os.path.join(conf, 'docker-compose.yml')
+ self.docker_yaml_saved = os.path.join(conf, 'docker-compose.yml.saved')
+ self.onos_config_dir = os.path.join(conf, 'config')
+ self.onos_cfg_save_loc = os.path.join(conf, 'network-cfg.json.saved')
+ instance_active = False
+ #if we have a wrapper onos instance already active, back out
+ if os.access(self.onos_config_dir, os.F_OK) or os.access(self.docker_yaml_saved, os.F_OK):
+ instance_active = True
+ else:
+ if start is True:
+ os.mkdir(self.onos_config_dir)
+ shutil.copy(self.docker_yaml, self.docker_yaml_saved)
+ self.start_wrapper = instance_active is False and start is True
##update the docker yaml with the config volume
with open(self.docker_yaml, 'r') as f:
yaml_config = yaml.load(f)
image = yaml_config['services'].keys()[0]
- name = 'cordtestercord_{}_1'.format(image)
+ cord_conf_dir_basename = os.path.basename(self.onos_cord_dir.replace('-', ''))
+ xos_onos_name = '{}_{}_1'.format(cord_conf_dir_basename, image)
volumes = yaml_config['services'][image]['volumes']
config_volumes = filter(lambda e: e.find(self.onos_config_dir_guest) >= 0, volumes)
if not config_volumes:
config_volume = '{}:{}'.format(self.onos_config_dir, self.onos_config_dir_guest)
volumes.append(config_volume)
- docker_yaml_changed = '{}-changed'.format(self.docker_yaml)
- with open(docker_yaml_changed, 'w') as wf:
- yaml.dump(yaml_config, wf)
-
- os.rename(docker_yaml_changed, self.docker_yaml)
+ if self.start_wrapper:
+ docker_yaml_changed = '{}-changed'.format(self.docker_yaml)
+ with open(docker_yaml_changed, 'w') as wf:
+ yaml.dump(yaml_config, wf)
+ os.rename(docker_yaml_changed, self.docker_yaml)
self.volumes = volumes
- super(OnosCord, self).__init__(name, image, tag = '')
- cord_conf_dir_basename = os.path.basename(self.cord_conf_dir.replace('-', ''))
- 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
+ self.xos_onos = Container(xos_onos_name, image, tag = '')
+ self.last_cfg = None
+ if self.start_wrapper:
+ #fetch the current config of onos cord instance and save it
+ try:
+ self.last_cfg = OnosCtrl.get_config(controller = onos_ip)
+ json_data = json.dumps(self.last_cfg, indent=4)
+ with open(self.onos_cfg_save_loc, 'w') as f:
+ f.write(json_data)
+ except:
+ pass
+ #start the container back with the shared onos config volume
+ self.start()
def start(self, restart = False, network_cfg = None):
- if restart is True:
- if self.exists():
- ##Kill the existing instance
- 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()
-
- if network_cfg is not None:
+ if network_cfg:
json_data = json.dumps(network_cfg, indent=4)
with open('{}/network-cfg.json'.format(self.onos_config_dir), 'w') as f:
f.write(json_data)
-
- #start the container using docker-compose
- cmd = 'cd {} && docker-compose up -d'.format(self.onos_cord_dir)
- os.system(cmd)
- #Delay to make sure ONOS fully boots
+ if restart is False:
+ #stop and start and synchronize the services before installing tester cord apps
+ cmds = [ 'cd {} && docker-compose down'.format(self.onos_cord_dir),
+ 'cd {} && docker-compose up -d'.format(self.onos_cord_dir),
+ 'sleep 45',
+ 'cd {} && make {}'.format(self.service_profile, self.synchronizer)
+ ]
+ for cmd in cmds:
+ try:
+ print(cmd)
+ os.system(cmd)
+ except:pass
+ Onos.install_cord_apps(onos_ip = self.onos_ip)
+ else:
+ cmd = 'cd {} && docker-compose restart'.format(self.onos_cord_dir)
+ try:
+ os.system(cmd)
+ except: pass
+ print('Waiting %d seconds for ONOS instance to start' %self.boot_delay)
time.sleep(self.boot_delay)
- Onos.install_cord_apps(onos_ip = self.onos_ip)
def build_image(self):
build_cmd = 'cd {} && docker-compose build'.format(self.onos_cord_dir)
os.system(build_cmd)
- @classmethod
- def cleanup(cls):
- if not os.access(cls.onos_cord_dir, os.F_OK):
+ def restore(self, force = False):
+ restore = self.start_wrapper is True or force is True
+ if not restore:
return
- cmd = 'cd {} && docker-compose down'.format(cls.onos_cord_dir)
- try:
- os.system(cmd)
- 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
-
- @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 False
- if not onos_cord or not os.access(onos_cord, os.F_OK):
- return False
-
- 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)
+ #restore the config files back. The synchronizer restore should bring the last config back
+ cmds = ['cd {} && docker-compose down'.format(self.onos_cord_dir),
+ 'rm -rf {}'.format(self.onos_config_dir),
+ 'mv {} {}'.format(self.docker_yaml_saved, self.docker_yaml),
+ 'cd {} && docker-compose up -d'.format(self.onos_cord_dir),
+ 'sleep 45',
+ 'cd {} && make {}'.format(self.service_profile, self.synchronizer)
+ ]
+ for cmd in cmds:
try:
- OnosCtrl.config(config, controller = onos_ip)
- os.unlink(cls.onos_cfg_save_loc)
+ print(cmd)
+ os.system(cmd)
except: pass
- cls.cleanup()
- return True
+ #We may not have to restore the config but still it should match synchronizer last config
+ if os.access(self.onos_cfg_save_loc, os.F_OK):
+ with open(self.onos_cfg_save_loc, 'r') as f:
+ cfg = json.load(f)
+ try:
+ OnosCtrl.config(cfg, controller = self.onos_ip)
+ os.unlink(self.onos_cfg_save_loc)
+ except:
+ pass
class OnosCordStopWrapper(Container):
onos_cord_dir = os.path.join(os.getenv('HOME'), 'cord-tester-cord')
@@ -508,7 +498,7 @@
def remove_data_map(cls, host_volume, guest_volume_dir):
host_volume_dir = os.path.join(cls.setup_dir, os.path.basename(host_volume))
if os.path.exists(host_volume_dir):
- rmtree(host_volume_dir)
+ shutil.rmtree(host_volume_dir)
def remove_data_volume(self):
if self.data_map is not None:
diff --git a/src/test/utils/TestManifest.py b/src/test/utils/TestManifest.py
index 792926a..b5ec2d4 100644
--- a/src/test/utils/TestManifest.py
+++ b/src/test/utils/TestManifest.py
@@ -42,6 +42,8 @@
if len(ips) > 1:
self.radius_ip = ips[1]
self.onos_cord = args.onos_cord if args.onos_cord else None
+ self.service_profile = args.service_profile if args.service_profile else None
+ self.synchronizer = args.synchronizer if args.synchronizer else None
self.docker_network = args.network if args.network else None
self.iterations = None
self.server = args.server
@@ -55,6 +57,8 @@
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.service_profile = data.get('service_profile', None)
+ self.synchronizer = data.get('synchronizer', 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)