def test_here_single(device, config, name, usrparam=None, dry_run=DRY_RUN): """Test a configuration sequence. Args: device: device fixture config[]: map of all config chunks: config[n][0]: first part of config chunk config[n][1]: second part of config chunk config[n][n]: last part of config chunk name: name of config chunk to run Returns: nothing Given the input ["set A 1 set A 2", "set B 1 set B 2"] the test will run the following CLI commands: (a) set A 1 set A 2 # commit - compare (b) set B 1 set B 2 # commit - compare (c) rollback to (a) - compare rollback to (c) - compare rollback to (b) - compare rollback to (a) - compare """ device.save("drned-work/before-test.cfg") commit_id_base = len(device.commit_id) # Handle all chunks settings = Settings() try: try: for i,conf in enumerate(config[name]): _chunk_part(device, i, name, conf, usrparam=usrparam, settings=settings, dry_run=dry_run, operations=["load", "commit", "compare-config"]) except StopIteration: device.sync_from() rollback_to_start(device, commit_id_base, dry_run) except: rollback_to_start(device, commit_id_base, dry_run) raise else: rollback_to_start(device, commit_id_base, dry_run) # Rollback to end, and then incrementally to start again for i in reversed(range(commit_id_base, len(device.commit_id))): device.rollback_compare(id=device.commit_id[i], dry_run=dry_run) finally: settings.restore_all() # Test that we're back device.save("drned-work/after-test.cfg") if not common.filecmp("drned-work/before-test.cfg", "drned-work/after-test.cfg"): pytest.fail("The state after rollback differs from before load. " + "Please check before-test.cfg and after-test.cfg")
def _here_union(device, config, it, dry_run=False): device.save("drned-work/before-test.cfg") names = sorted(config.keys()) if it in [2, 4, 6]: names = reversed(names) commit_id_base = len(device.commit_id) settings = Settings() try: try: for name in names: device.trace("\npy.test -k 'test_here_single[%s]'\n" % name) # Commit loop for i, conf in enumerate(config[name]): if it in [1, 2]: operations = ["load", "commit", "compare-config"] else: operations = ["load"] _chunk_part(device, i, name, conf, settings=settings, dry_run=dry_run, operations=operations, do_functions=False) if it in [3, 4]: device.commit_compare(dry_run=dry_run) if it in [5, 6]: device.commit_compare(dry_run=dry_run) except StopIteration: device.sync_from() rollback_to_start(device, commit_id_base, dry_run) except Exception: rollback_to_start(device, commit_id_base, dry_run) raise else: rollback_to_start(device, commit_id_base, dry_run) # Rollback to end, and then incrementally to start again for i in reversed(range(commit_id_base, len(device.commit_id))): device.rollback_compare(id=device.commit_id[i], dry_run=dry_run) finally: settings.restore_all() # Test that we're back device.save("drned-work/after-test.cfg") if not common.filecmp("drned-work/before-test.cfg", "drned-work/after-test.cfg"): pytest.fail("The state after rollback differs from before load. " + "Please check before-test.cfg and after-test.cfg")
def test_template_single(device, template, op): """Normal test with template file. The rollback[+-no] operation specifies an index in a list of all commits that have been performed in this function. This means that rollback-1 will undo the last commit, rollback+0 will undo all commits, etc. The operations to perform can also be specified either in the template file itself, or in a .load file (see Device.load). An example of the template file syntax: !op=(load,commit) This test is using the device fixture, which means that the device state is restored after test. Args: device: device fixture template: name of the template file op[0..n]: operations to be performed: "": no-op load: load file commit: commit configuration compare-config: compare configuration check-sync: check that device is in sync rollback[+-no]: rollback configuration Default: ["load", "commit", "compare-config", "rollback", "commit", "compare-config"] Returns: nothing """ if op is None: op = ["load", "commit", "compare-config", "rollback", "commit", "compare-config"] device.save("drned-work/before-test.cfg") src,_,_ = _drned_single_file(device, template, op) device.save("drned-work/after-test.cfg") if not src and not common.filecmp("drned-work/before-test.cfg", "drned-work/after-test.cfg"): pytest.fail("The state after rollback differs from before load. " + "Please check before-test.cfg and after-test.cfg")
def device(request): devname = request.config.getoption("--device") if devname == None: pytest.fail("Please enter a device name using the --device " + "command-line parameter") # Time to create device use = request.config.getoption("--use") device = drned.Device(devname, use=use, request=request) device.trace("\n%s\n" % request._pyfuncitem.name) # Save state in XML to be able to restore # reliably. Note that the entire "devices" tree is # saved, the "replace" load option requires it. device.save("drned-work/before-session.xml", path="devices", fmt="xml") # Also save in CLI format to make it easier to # compare device.save("drned-work/before-session.cfg") yield device print("\n### TEARDOWN, RESTORE DEVICE ###") device.reset_cli() # Try to restore device twice, required for some NCS releases laps = 2 for i in range(laps): device.sync_from() device.load("drned-work/before-session.xml", mode="replace") device.commit() try: device.compare_config() break except pytest.fail.Exception as e: if i >= (laps - 1): raise # Check if restore successful device.save("drned-work/after-session.cfg") if not common.filecmp("drned-work/before-session.cfg", "drned-work/after-session.cfg"): pytest.fail("Could not restore device to state before session. " + "Please check before-session.cfg and after-session.cfg")
def _drned_single_set(device, init, fname, init_op, op, end_op, it, ordered): src_in_set = False device.save("drned-work/before-test.cfg") # Load init files init_id_start = len(device.commit_id) if init is not None: if init_op is None: init_op = ["load", "commit", "compare-config"] for init_file in init: src,_,_ = _drned_single_file(device, init_file, init_op) src_in_set = src_in_set or src init_id_stop = len(device.commit_id) # Must at least have one file if not fname: pytest.fail("Please specify a file with the --fname option") # Divide in sets file_sets = [re.sub(r":[^\.]*", ":*", f) for f in fname] if ordered == "true": tsets = sorted(list(set(file_sets))) else: # need to preserve the tset order, but remove duplicities t_set = set() tsets = [] for fset in file_sets: if fset not in t_set: tsets.append(fset) t_set.add(fset) if not it % 2: tsets = reversed(tsets) if op is None: op = ["load", "commit", "compare-config"] pinit = " --init=" + " --init=".join(init) if init else "" piop = " --init-op=" + " --init-op=".join(init_op) if init_op else "" pop = " --op=" + " --op=".join(op) if op else "" peop = " --end-op=" + " --end-op=".join(end_op) if end_op else "" # Loop for sets for tset in tsets: device.trace("\npy.test -k test_template_set%s " % (pinit) + "--fname=%s%s%s%s --device=%s\n" % (tset, piop, pop, peop, device.name)) commit_id_base = len(device.commit_id) tset_files = sorted(glob.glob(tset)) set_all_operation = True # Use upper scheme as default the_end = end_op try: for tset_file in tset_files: src,eop,sop = _drned_single_file(device, tset_file, op) src_in_set = src_in_set or src set_all_operation = set_all_operation and sop # Get end operations from file, if present if eop: # File operations override the_end = eop if the_end == None: # Default action is to first rollback to start, then to # end, then rollback all commits, one by one if set_all_operation: the_end = ["rollback+0", "commit", "compare-config"] extra_rollback = 1 else: the_end = [] extra_rollback = 0 for i in reversed(range(commit_id_base, len(device.commit_id) + extra_rollback)): the_end += ["rollback+%d" % (i - commit_id_base), "commit", "compare-config"] # Use the file function also for rollbacks _drned_single_file(device, None, the_end, commit_id_base=commit_id_base) except KeyboardInterrupt: # do not intercept CTRL-C raise except BaseException: # Record the failed state and, if this is not the last # state in the list, restore the device to its original # configuration and continue testing. If this is the last # test, re-raise the exception and trigger regular # cleanup/error reporting. m = re.match(r".*/(\S*).state.(cfg|xml)", tset) device.failed_states.append(m.group(1)) if tset != tsets[-1]: # Re-raise walk states error to trigger error condition. device.restore() else: raise continue # Rollback init files for i in reversed(range(init_id_start, init_id_stop)): device.rollback_compare(id=device.commit_id[i], dry_run=False) # Check that we're back device.save("drned-work/after-test.cfg") if not src_in_set and end_op is None \ and not common.filecmp("drned-work/before-test.cfg", "drned-work/after-test.cfg"): pytest.fail("The state after rollback differs from before load. " + "Please check before-test.cfg and after-test.cfg")
def _drned_single_set(device, init, fname, init_op, op, end_op, it, ordered): src_in_set = False device.save("drned-work/before-test.cfg") # Load init files init_id_start = len(device.commit_id) if init != None: if init_op == None: init_op = ["load", "commit", "compare-config"] for init_file in init: src,_,_ = _drned_single_file(device, init_file, init_op) src_in_set = src_in_set or src init_id_stop = len(device.commit_id) # Must at least have one file if not fname: pytest.fail("Please specify a file with the --fname option") # Divide in sets tsets = [re.sub(":[^\.]*", ":*", f) for f in fname] if ordered == "true": tsets = sorted(list(set(tsets))) if not it % 2: tsets = reversed(tsets) if op == None: op = ["load", "commit", "compare-config"] pinit = " --init=" + " --init=".join(init) if init else "" piop = " --init-op=" + " --init-op=".join(init_op) if init_op else "" pop = " --op=" + " --op=".join(op) if op else "" peop = " --end-op=" + " --end-op=".join(end_op) if end_op else "" # Loop for sets for tset in tsets: device.trace("\npy.test -k test_template_set%s " % (pinit) + "--fname=%s%s%s%s --device=%s\n" % (tset, piop, pop, peop, device.name)) commit_id_base = len(device.commit_id) tset_files = sorted(glob.glob(tset)) set_all_operation = True # Use upper scheme as default the_end = end_op for tset_file in tset_files: src,eop,sop = _drned_single_file(device, tset_file, op) src_in_set = src_in_set or src set_all_operation = set_all_operation and sop # Get end operations from file, if present if eop: # File operations override the_end = eop if the_end == None: # Default action is to first rollback to start, then to # end, then rollback all commits, one by one if set_all_operation: the_end = ["rollback+0", "commit", "compare-config"] extra_rollback = 1 else: the_end = [] extra_rollback = 0 for i in reversed(range(commit_id_base, len(device.commit_id) + extra_rollback)): the_end += ["rollback+%d" % (i - commit_id_base), "commit", "compare-config"] # Use the file function also for rollbacks _drned_single_file(device, None, the_end, commit_id_base=commit_id_base) # Rollback init files for i in reversed(range(init_id_start, init_id_stop)): device.rollback_compare(id=device.commit_id[i], dry_run=False) # Check that we're back device.save("drned-work/after-test.cfg") if not src_in_set and end_op == None \ and not common.filecmp("drned-work/before-test.cfg", "drned-work/after-test.cfg"): pytest.fail("The state after rollback differs from before load. " + "Please check before-test.cfg and after-test.cfg")