diff --git a/aci-preupgrade-validation-script.py b/aci-preupgrade-validation-script.py
index b57821c7..bfca5bb6 100644
--- a/aci-preupgrade-validation-script.py
+++ b/aci-preupgrade-validation-script.py
@@ -1,3 +1,4 @@
+# -*- coding: utf-8 -*-
# SPDX-License-Identifier: Apache-2.0
#
# Copyright 2021 Cisco Systems, Inc. and its affiliates
@@ -20,10 +21,11 @@
from six.moves import input
from textwrap import TextWrapper
from getpass import getpass
-from collections import defaultdict
+from collections import defaultdict, OrderedDict
from datetime import datetime
from argparse import ArgumentParser
from itertools import chain
+import threading
import functools
import shutil
import warnings
@@ -36,7 +38,8 @@
import os
import re
-SCRIPT_VERSION = "v3.2.0"
+SCRIPT_VERSION = "v4.0.0"
+DEFAULT_TIMEOUT = 600 # sec
# result constants
DONE = 'DONE'
PASS = 'PASS'
@@ -64,16 +67,24 @@
ts = datetime.now().strftime('%Y-%m-%dT%H-%M-%S')
BUNDLE_NAME = 'preupgrade_validator_%s%s.tgz' % (ts, tz)
DIR = 'preupgrade_validator_logs/'
-JSON_DIR = DIR + 'json_results/'
-META_FILE = DIR + 'meta.json'
-RESULT_FILE = DIR + 'preupgrade_validator_%s%s.txt' % (ts, tz)
-SUMMARY_FILE = DIR + 'summary.json'
-LOG_FILE = DIR + 'preupgrade_validator_debug.log'
+JSON_DIR = os.path.join(DIR, 'json_results/')
+META_FILE = os.path.join(DIR, 'meta.json')
+RESULT_FILE = os.path.join(DIR, 'preupgrade_validator_%s%s.txt' % (ts, tz))
+SUMMARY_FILE = os.path.join(DIR, 'summary.json')
+LOG_FILE = os.path.join(DIR, 'preupgrade_validator_debug.log')
warnings.simplefilter(action='ignore', category=FutureWarning)
log = logging.getLogger()
+# TimeoutError is only from py3.3
+try:
+ TimeoutError
+except NameError:
+ class TimeoutError(Exception):
+ pass
+
+
class OldVerClassNotFound(Exception):
""" Later versions of ACI can have class properties not found in older versions """
pass
@@ -916,6 +927,286 @@ def is_firstver_gt_secondver(first_ver, second_ver):
return result
+class CustomThread(threading.Thread):
+ def __init__(self, *args, **kwargs):
+ super(CustomThread, self).__init__(*args, **kwargs)
+ self.exception = None
+
+ def start(self, timeout=5.0):
+ """Thread.start() with timeout to wait for the thread start event.
+
+ This method overrides `_started.wait()` at the end with a timeout to
+ prevent the issue explained below.
+ When MemoryError occurs during _start_new_thread(_bootstrap(), ()),
+ this method (start()) could get stuck forever at _started.wait(), which
+ is a threading.Event(), because _bootstrap() is supposed to trigger
+ _started.set() which may never happen because it appears some exceptions
+ are not raised to be captured by try/except in this method.
+ This was observed when the script was used inside a container with a
+ restricted memory allocation and resulted in the script to get stuck
+ and not being able to start remaining threads.
+
+ Args:
+ timeout (float): How long we wait for the thread start event.
+ 5.0 sec by default.
+ """
+ _active_limbo_lock = threading._active_limbo_lock
+ _limbo = threading._limbo
+ _start_new_thread = threading._start_new_thread
+
+ # Python2 uses name mangling
+ if hasattr(self, "_Thread__initialized"):
+ self._initialized = self._Thread__initialized
+ if hasattr(self, "_Thread__started"):
+ self._started = self._Thread__started
+ if hasattr(self, "_Thread__bootstrap"):
+ self._bootstrap = self._Thread__bootstrap
+
+ if not self._initialized:
+ raise RuntimeError("thread.__init__() not called")
+
+ if self._started.is_set():
+ raise RuntimeError("threads can only be started once")
+
+ with _active_limbo_lock:
+ _limbo[self] = self
+ try:
+ _start_new_thread(self._bootstrap, ())
+ except Exception:
+ with _active_limbo_lock:
+ del _limbo[self]
+ raise
+ self._started.wait(timeout)
+ # When self._started was not set within the time limit, handle it
+ # in the same way as when `_start_new_thread()` correctly captures
+ # the exception due to OOM.
+ if not self._started.is_set():
+ with _active_limbo_lock:
+ del _limbo[self]
+ raise RuntimeError("can't start new thread")
+
+ def run(self):
+ # Python2 uses name mangling
+ if hasattr(self, "_Thread__target"):
+ self._target = self._Thread__target
+ if hasattr(self, "_Thread__args"):
+ self._args = self._Thread__args
+ if hasattr(self, "_Thread__kwargs"):
+ self._kwargs = self._Thread__kwargs
+
+ try:
+ if self._target is not None:
+ self._target(*self._args, **self._kwargs)
+ except Exception as e:
+ # Exceptions inside a thread should be captured in the thread, that
+ # is in the `check_wrapper`.
+ # If it's not caught inside the thread, notify the main thread.
+ self.exception = e
+ finally:
+ del self._target, self._args, self._kwargs
+
+
+class ThreadManager:
+ """A class managing all threads to run individual checks.
+
+ This class starts and monitors the status of all threads for check
+ functions decorated with check_wrapper(). This stops monitoring when all
+ threads completed or when timeout expired.
+ On a memory constrained setup, it may take time to start each thread. Some
+ thread/check may complete before other threads get started. To monitor the
+ progress correctly from the beginning, the monitoring is also done in a
+ thread while the main thread is starting all threads for each check.
+ """
+ def __init__(
+ self,
+ funcs,
+ common_kwargs,
+ monitor_interval=0.5, # sec
+ monitor_timeout=600, # sec
+ callback_on_monitoring=None,
+ callback_on_start_failure=None,
+ callback_on_timeout=None,
+ ):
+ self.funcs = funcs
+ self.threads = None
+ self.common_kwargs = common_kwargs
+
+ # Not using `thread.join(timeout)` because it waits for each thread sequentially,
+ # which means the program may wait for "timeout * num of threads" at worst case.
+ self.timeout_event = threading.Event()
+ self.monitor_interval = monitor_interval
+ self.monitor_timeout = monitor_timeout
+ self._monitor = self._generate_thread(target=self._monitor_progress)
+
+ # Number of threads that were processed by `_start_thread()`, including
+ # both success and failure to start.
+ self._processed_threads_count = 0
+
+ # Custom callbacks
+ self._cb_on_monitoring = callback_on_monitoring
+ self._cb_on_start_failure = callback_on_start_failure
+ self._cb_on_start_failure_exception = None
+ self._cb_on_timeout = callback_on_timeout
+
+ def start(self):
+ if self._monitor.is_alive():
+ raise RuntimeError("Threading on going. Cannot start again.")
+
+ self.threads = [
+ self._generate_thread(target=func, kwargs=self.common_kwargs)
+ for func in self.funcs
+ ]
+
+ self._monitor.start()
+
+ for thread in self.threads:
+ self._start_thread(thread)
+
+ def join(self):
+ self._monitor.join()
+ # If the thread had an exception that was not captured and handled correctly,
+ # re-raise it in the main thread to notify the script executor about it.
+ if self._monitor.exception:
+ raise self._monitor.exception
+ for thread in self.threads:
+ if thread.exception:
+ raise thread.exception
+ # Exception in the callback means failure to update the result as error. Need to
+ # re-raise it in the main thread to notify the script excutor about the risk of
+ # some check results left with in-progress forever.
+ if self._cb_on_start_failure_exception:
+ raise self._cb_on_start_failure_exception
+
+ def is_timeout(self):
+ return self.timeout_event.is_set()
+
+ def _generate_thread(self, target, args=(), kwargs=None):
+ if kwargs is None:
+ kwargs = {}
+ thread = CustomThread(
+ target=target, name=target.__name__, args=args, kwargs=kwargs
+ )
+ thread.daemon = True
+ return thread
+
+ def _start_thread(self, thread):
+ """ Start a thread. When failed due to OOM, retry again after an interval.
+ Until one of the following conditions are met, we don't move on.
+ - successfuly started the thread
+ - exceeded the queue timeout and gave up on this thread
+ - failed to start the thread for an unknown reason
+ """
+ queue_timeout = 10 # sec
+ queue_interval = 1 # sec
+ time_elapsed = 0 # sec
+ thread_started = False
+ while not self.is_timeout():
+ try:
+ log.info("({}) Starting thread.".format(thread.name))
+ thread.start()
+ thread_started = True
+ break
+ except RuntimeError as e:
+ if str(e) != "can't start new thread":
+ log.error("({}) Unexpected error to start a thread.".format(thread.name), exc_info=True)
+ break
+
+ log_msg = "({}) Not enough memory to start a new thread. ".format(thread.name)
+ if time_elapsed >= queue_timeout:
+ log.error(log_msg + "No queue time left. Give up.")
+ break
+ else:
+ log.info(log_msg + "Try again in {} second...".format(queue_interval))
+ time.sleep(queue_interval)
+ time_elapsed += queue_interval
+ continue
+ except Exception:
+ log.error("({}) Unexpected error to start a thread.".format(thread.name), exc_info=True)
+ break
+
+ # Custom cleanup callback for a thread that couldn't start.
+ if not thread_started and not thread.is_alive():
+ log.error("({}) Failed to start thread.".format(thread.name))
+ if self._cb_on_start_failure is not None:
+ try:
+ self._cb_on_start_failure(thread.name)
+ except Exception as e:
+ log.error("({}) Failed to update the result as error.".format(thread.name), exc_info=True)
+ self._cb_on_start_failure_exception = e
+
+ self._processed_threads_count += 1
+
+ def _monitor_progress(self):
+ """Executed in a separate monitor thread"""
+ total = len(self.threads)
+ time_elapsed = 0 # sec
+ while True:
+ alive_count = sum(thread.is_alive() for thread in self.threads)
+ done = self._processed_threads_count - alive_count
+
+ # Custom monitor callback
+ if self._cb_on_monitoring is not None:
+ self._cb_on_monitoring(done, total)
+
+ if done == total:
+ break
+
+ time.sleep(self.monitor_interval)
+ time_elapsed += self.monitor_interval
+ if time_elapsed > self.monitor_timeout:
+ log.error("Timeout. Stop monitoring threads.")
+ self.timeout_event.set()
+ break
+
+ # Custom timeout callback per thread
+ if self.is_timeout() and self._cb_on_timeout is not None:
+ for thread in self.threads:
+ if thread.is_alive():
+ self._cb_on_timeout(thread.name)
+
+
+class ResultManager:
+ def __init__(self):
+ self.titles = {} # {check_id: check_title}
+ self.results = {} # {check_id: Result}
+
+ def _update_aci_result(self, check_id, check_title, result_obj=None):
+ aci_result = AciResult(check_id, check_title, result_obj)
+ filepath = self.get_result_filepath(check_id)
+ write_jsonfile(filepath, aci_result.as_dict())
+ return filepath
+
+ def init_result(self, check_id, check_title):
+ self.titles[check_id] = check_title
+ filepath = self._update_aci_result(check_id, check_title)
+ log.info("Initialized in {}".format(filepath))
+
+ def update_result(self, check_id, result_obj):
+ check_title = self.titles.get(check_id)
+ if not check_title:
+ log.error("Check {} is not initialized. Failed to update.".format(check_id))
+ return None
+ self.results[check_id] = result_obj
+ filepath = self._update_aci_result(check_id, check_title, result_obj)
+ log.info("Finalized result in {}".format(filepath))
+
+ def get_summary(self):
+ summary_headers = [PASS, FAIL_O, FAIL_UF, MANUAL, POST, NA, ERROR, 'TOTAL']
+ summary = OrderedDict([(key, 0) for key in summary_headers])
+ summary["TOTAL"] = len(self.results)
+
+ for result in self.results.values():
+ summary[result.result] += 1
+
+ return summary
+
+ def get_result_filepath(self, check_id):
+ """filename should be only alphabet, number and underscore"""
+ filename = re.sub(r'[^a-zA-Z0-9_]+|\s+', '_', check_id) + '.json'
+ filepath = os.path.join(JSON_DIR, filename)
+ return filepath
+
+
class AciResult:
"""
APIC uses an object called `syntheticMaintPValidate` to store the results of
@@ -935,10 +1226,10 @@ class AciResult:
PASS = "passed"
FAIL = "failed"
- def __init__(self, func_name, name, description):
+ def __init__(self, func_name, name, result_obj=None):
self.ruleId = func_name
self.name = name
- self.description = description
+ self.description = ""
self.reason = ""
self.sub_reason = ""
self.recommended_action = ""
@@ -948,16 +1239,20 @@ def __init__(self, func_name, name, description):
self.showValidation = True
self.failureDetails = {
"failType": "",
+ "header": [],
"data": [],
+ "unformatted_header": [],
"unformatted_data": [],
}
-
- @property
- def filename(self):
- return re.sub(r'[^a-zA-Z0-9_]+|\s+', '_', self.ruleId) + '.json'
+ if result_obj:
+ self.update_with_results(result_obj)
@staticmethod
- def craftData(column, rows):
+ def convert_data(column, rows):
+ """Convert data from `Result` data format to `AciResult` data format.
+ Result - {header: [h1, h2,,,], data: [[d11, d21,,,], [d21, d22,,,],,,]}
+ AciResult - [{h1: d11, h2: d21,,,}, {h1: d21, h2: d22,,,},,,]
+ """
if not (isinstance(rows, list) and isinstance(column, list)):
raise TypeError("Rows and column must be lists.")
data = []
@@ -966,62 +1261,58 @@ def craftData(column, rows):
r_len = len(rows[row_entry])
if r_len != c_len:
raise ValueError("Row length ({}), data: {} does not match column length ({}).".format(r_len, rows[row_entry], c_len))
- entry = {}
+ entry = OrderedDict()
for col_pos in range(c_len):
entry[column[col_pos]] = str(rows[row_entry][col_pos])
data.append(entry)
return data
- def updateWithResults(self, result, recommended_action, msg, doc_url, headers, data, unformatted_headers, unformatted_data):
- self.reason = msg
- self.recommended_action = recommended_action
- self.docUrl = doc_url
+ def update_with_results(self, result_obj):
+ self.recommended_action = result_obj.recommended_action
+ self.docUrl = result_obj.doc_url
- # Show validation
- if result in [NA, POST]:
- self.showValidation = False
+ result = result_obj.result
+
+ # Show validatio
+ self.showValidation = result not in (NA, POST)
# Severity
- if result in [FAIL_O, FAIL_UF]:
- self.severity = "critical"
- elif result in [ERROR]:
- self.severity = "major"
- elif result in [MANUAL]:
- self.severity = "warning"
-
- self.ruleStatus = AciResult.PASS
- if result not in [NA, PASS]:
- self.ruleStatus = AciResult.FAIL
- if not self.reason:
- self.reason = "See Failure Details"
+ severity_map = {FAIL_O: "critical", FAIL_UF: "critical", ERROR: "major", MANUAL: "warning"}
+ self.severity = severity_map.get(result, "informational")
+
+ # ruleStatus
+ self.ruleStatus = AciResult.PASS if result in (NA, PASS) else AciResult.FAIL
+
+ # reason
+ self.reason = result_obj.msg
+ if not self.reason and self.ruleStatus == AciResult.FAIL:
+ self.reason = "See Failure Details."
+
+ # failureDetails
+ if self.ruleStatus == AciResult.FAIL:
self.failureDetails["failType"] = result
- self.failureDetails["header"] = headers
- self.failureDetails["data"] = self.craftData(headers, data)
- if unformatted_headers and unformatted_data:
- self.failureDetails["unformatted_data"] = self.craftData(unformatted_headers, unformatted_data)
- if self.reason:
- self.reason += "\n"
- self.reason += (
- "Parse failure occurred, the provided data may not be complete. "
- "Please contact Cisco TAC to identify the missing data."
+ self.failureDetails["header"] = result_obj.headers
+ self.failureDetails["data"] = self.convert_data(result_obj.headers, result_obj.data)
+ if result_obj.unformatted_headers and result_obj.unformatted_data:
+ self.failureDetails["unformatted_header"] = result_obj.unformatted_headers
+ self.failureDetails["unformatted_data"] = self.convert_data(
+ result_obj.unformatted_headers, result_obj.unformatted_data
+ )
+ self.recommended_action += (
+ "\n "
+ "Note that the provided data in the Failure Details is not complete"
+ " due to an issue in parsing data. Contact Cisco TAC for the full details."
)
- def buildResult(self):
+ def as_dict(self):
return {slot: getattr(self, slot) for slot in self.__slots__}
- def writeResult(self, path=JSON_DIR):
- if not os.path.isdir(path):
- os.mkdir(path)
- with open(os.path.join(path, self.filename), "w") as f:
- json.dump(self.buildResult(), f, indent=2)
- return "{}/{}".format(path, self.filename)
-
class Result:
"""Class to hold the result of a check."""
- __slots__ = ("result", "msg", "headers", "data", "unformatted_headers", "unformatted_data", "recommended_action", "doc_url", "adjust_title")
+ __slots__ = ("result", "msg", "headers", "data", "unformatted_headers", "unformatted_data", "recommended_action", "doc_url")
- def __init__(self, result=PASS, msg="", headers=None, data=None, unformatted_headers=None, unformatted_data=None, recommended_action="", doc_url="", adjust_title=False):
+ def __init__(self, result=PASS, msg="", headers=None, data=None, unformatted_headers=None, unformatted_data=None, recommended_action="", doc_url=""):
self.result = result
self.msg = msg
self.headers = headers if headers is not None else []
@@ -1030,52 +1321,54 @@ def __init__(self, result=PASS, msg="", headers=None, data=None, unformatted_hea
self.unformatted_data = unformatted_data if unformatted_data is not None else []
self.recommended_action = recommended_action
self.doc_url = doc_url
- self.adjust_title = adjust_title
def as_dict(self):
return {slot: getattr(self, slot) for slot in self.__slots__}
- def as_dict_for_json_result(self):
- return {slot: getattr(self, slot) for slot in self.__slots__ if slot != "adjust_title"}
-
def check_wrapper(check_title):
- """
- Decorator to wrap a check function to handle the printing of title and results,
- and to write the results in a file in a JSON format.
+ """Decorator to wrap a check function with initializer and finalizer from `CheckManager`.
+
+ The goal is for each check function to focus only on the check logic itself and return
+ `Result` object. The rest such as initializing the result, printing the result to stdout,
+ writing the result in a file in JSON etc. are handled through this wrapper and CheckManager.
"""
def decorator(check_func):
@functools.wraps(check_func)
- def wrapper(index, total_checks, *args, **kwargs):
- # When init is True, we just initialize the result file and return
- if kwargs.get("init") is True:
- synth = AciResult(wrapper.__name__, check_title, "")
- synth.writeResult()
+ def wrapper(*args, **kwargs):
+ # Initialization
+ initialize_check = kwargs.pop("initialize_check", None)
+ if initialize_check:
+ # Using `wrapper.__name__` instead of `check_func.__name` because
+ # both show the original check func name and `wrapper.__name__` can
+ # be dynamically changed inside each check func if needed. (mainly
+ # for test or debugging)
+ initialize_check(wrapper.__name__, check_title)
return None
+ log.info("Start {}".format(wrapper.__name__))
+ # Real Run (executed inside a thread)
+ # When `finalize_check` failed even in `except`, there is nothing we can
+ # do because it is usually because of system level issues like filesystem
+ # being full. In such a case, we cannot even update the result of the check
+ # as `failed` from `in-progress`. To inform the script executor and prevent it
+ # from indefinitely waiting, we let the exception to go up to the top (`main()`)
+ # and abort the script immediately.
+ finalize_check = kwargs.pop("finalize_check")
try:
- # Print `[Check 1/81]
...`
- print_title(check_title, index, total_checks)
-
- # Run check, expecting it to return a `Result` object
r = check_func(*args, **kwargs)
-
- # Print `[Check 1/81] ... \n`
- print_result(title=check_title, **r.as_dict())
+ finalize_check(wrapper.__name__, r)
+ except MemoryError:
+ msg = "Not enough memory to complete this check."
+ r = Result(result=ERROR, msg=msg)
+ log.error(msg, exc_info=True)
+ finalize_check(wrapper.__name__, r)
except Exception as e:
- log.exception(e)
- r = Result(result=ERROR, msg='Unexpected Error: {}'.format(e))
- print_result(title=check_title, **r.as_dict())
- finally:
- # Write results in JSON
- # Using `wrapper.__name__` instead of `check_func.__name` because
- # both show the original check func name and `wrapper.__name__` can
- # be dynamically changed inside each check func if needed. (mainly
- # for test or debugging)
- synth = AciResult(wrapper.__name__, check_title, "")
- synth.updateWithResults(**r.as_dict_for_json_result())
- synth.writeResult()
- return r.result
+ msg = "Unexpected Error: {}".format(e)
+ r = Result(result=ERROR, msg=msg)
+ log.error(msg, exc_info=True)
+ finalize_check(wrapper.__name__, r)
+ return r
return wrapper
return decorator
@@ -1201,38 +1494,40 @@ def get_row(widths, values, spad=" ", lpad=""):
def prints(objects, sep=' ', end='\n'):
with open(RESULT_FILE, 'a') as f:
print(objects, sep=sep, end=end, file=sys.stdout)
+ if end == "\r":
+ end = "\n" # easier to read with \n in a log file
print(objects, sep=sep, end=end, file=f)
sys.stdout.flush()
f.flush()
-def print_title(title, index=None, total=None):
- if index and total:
- prints('[Check{:3}/{}] {}... '.format(index, total, title), end='')
+def print_progress(done, total, bar_length=100):
+ if not total:
+ progress = 1.0
else:
- prints('{:14}{}... '.format('', title), end='')
+ progress = done / float(total)
+ filled = int(bar_length * progress)
+ bar = "█" * filled + "-" * (bar_length - filled)
+ prints("Progress: |{}| {}/{} checks completed".format(bar, done, total), end="\r")
-def print_result(title, result, msg='',
+def print_result(index, total, title,
+ result, msg='',
headers=None, data=None,
unformatted_headers=None, unformatted_data=None,
recommended_action='',
- doc_url='',
- adjust_title=False):
- FULL_LEN = 138 # length of `[Check XX/YY] ... --padding-- `
- CHECK_LEN = 18 # length of `[Check XX/YY] ... `
- padding = FULL_LEN - CHECK_LEN - len(title) - len(msg)
- if adjust_title:
- # adjust padding when the result is on the second line.
- # 1st: `[Check XX/YY] ... `
- # 2nd: ` --padding-- `
- padding += len(title) + CHECK_LEN
+ doc_url=''):
+ """Print `[Check XX/YY] ... --padding-- ` + some data"""
+ idx_len = len(str(total)) + 1
+ output = "[Check{:{}}/{}] {}... {}".format(index, idx_len, total, title, msg)
+ FULL_LEN = 138 # length of `[Check XX/YY] ... --padding-- `
+ padding = FULL_LEN - len(output)
if padding < len(result):
# when `msg` is too long (ex. unknown exception), `padding` may get shorter
# than what it's padding (`result`), or worse, may get negative.
# In such a case, keep one whitespace padding even if the full length gets longer.
padding = len(result) + 1
- output = '{}{:>{}}'.format(msg, result, padding)
+ output += "{:>{}}".format(result, padding)
if data:
data.sort()
output += '\n' + format_table(headers, data)
@@ -1249,6 +1544,27 @@ def print_result(title, result, msg='',
prints(output)
+def write_jsonfile(filepath, content):
+ with open(filepath, 'w') as f:
+ json.dump(content, f, indent=2)
+
+
+def write_script_metadata(api_only, timeout, total_checks, common_data):
+ metadata = {
+ "name": "PreupgradeCheck",
+ "method": "standalone script",
+ "datetime": ts + tz,
+ "script_version": str(SCRIPT_VERSION),
+ "cversion": str(common_data["cversion"]),
+ "tversion": str(common_data["tversion"]),
+ "sw_cversion": str(common_data["sw_cversion"]),
+ "api_only": api_only,
+ "timeout": timeout,
+ "total_checks": total_checks,
+ }
+ write_jsonfile(META_FILE, metadata)
+
+
def _icurl_error_handler(imdata):
if imdata and "error" in imdata[0]:
if "not found in class" in imdata[0]['error']['attributes']['text']:
@@ -1257,6 +1573,8 @@ def _icurl_error_handler(imdata):
raise OldVerClassNotFound('Your current ACI version does not have requested class')
elif "not found" in imdata[0]['error']['attributes']['text']:
raise OldVerClassNotFound('Your current ACI version does not have requested class')
+ elif "Unable to deliver the message, Resolve timeout" in imdata[0]['error']['attributes']['text']:
+ raise TimeoutError("API Timeout. APIC may be too busy. Try again later.")
else:
raise Exception('API call failed! Check debug log')
@@ -1283,8 +1601,11 @@ def icurl(apitype, query, page_size=100000):
page = 0
while total_cnt > len(total_imdata):
data = _icurl(apitype, query, page, page_size)
- if not data['imdata']:
- break
+ # API queries may return empty even when totalCount is > 0 and the given page number
+ # should contain entries. This may happen when there are too many queries
+ # such as multiple same queries at the same time.
+ if int(data['totalCount']) > 0 and not data['imdata']:
+ raise Exception("API response empty with totalCount:{}. APIC may be too busy. Try again later.".format(data["totalCount"]))
total_imdata += data['imdata']
total_cnt = int(data['totalCount'])
page += 1
@@ -1295,7 +1616,7 @@ def run_cmd(cmd, splitlines=True):
"""
Run a shell command.
:param cmd: Command to run, can be a string or a list.
- :param splitlines: If True, splits the output into a list of lines.
+ :param splitlines: If True, splits the output into a list of lines.
If False, returns the raw text output as a single string.
Returns the output of the command.
"""
@@ -1325,31 +1646,10 @@ def get_credentials():
return usr, pwd
-def get_current_version(arg_cversion):
- """ Returns: AciVersion instance """
- if arg_cversion:
- prints("Current APIC version is overridden to %s" % arg_cversion)
- try:
- current_version = AciVersion(arg_cversion)
- except ValueError as e:
- prints(e)
- sys.exit(1)
- return current_version
- prints("Checking current APIC version...", end='')
- firmwares = icurl('class', 'firmwareCtrlrRunning.json')
- for firmware in firmwares:
- if 'node-1' in firmware['firmwareCtrlrRunning']['attributes']['dn']:
- apic1_version = firmware['firmwareCtrlrRunning']['attributes']['version']
- break
- current_version = AciVersion(apic1_version)
- prints('%s\n' % current_version)
- return current_version
-
-
def get_target_version(arg_tversion):
""" Returns: AciVersion instance """
if arg_tversion:
- prints("Target APIC version is overridden to %s" % arg_tversion)
+ prints("Target APIC version is overridden to %s\n" % arg_tversion)
try:
target_version = AciVersion(arg_tversion)
except ValueError as e:
@@ -1388,6 +1688,78 @@ def get_target_version(arg_tversion):
return None
+def get_fabric_nodes():
+ """Returns list of fabricNode objects.
+
+ Using fabricNode instead of topSystem because topSystem times out when the
+ node is inactive.
+ """
+ prints("Gathering Node Information...\n")
+ fabricNodes = icurl('class', 'fabricNode.json')
+ return fabricNodes
+
+
+def get_current_versions(fabric_nodes, arg_cversion):
+ """ Returns: AciVersion instances of APIC and lowest switch """
+ if arg_cversion:
+ prints("Current version is overridden to %s\n" % arg_cversion)
+ try:
+ current_version = AciVersion(arg_cversion)
+ except ValueError as e:
+ prints(e)
+ sys.exit(1)
+ return current_version, current_version
+
+ apic1_dn = ""
+ apic_version = "" # There can be only one APIC version
+ switch_versions = set()
+ for node in fabric_nodes:
+ version = node["fabricNode"]["attributes"]["version"]
+ if not version: # inactive nodes show empty version
+ continue
+ if node["fabricNode"]["attributes"]["role"] == "controller":
+ apic_version = version
+ if node["fabricNode"]["attributes"]["id"] == "1":
+ apic1_dn = node["fabricNode"]["attributes"]["dn"]
+ else:
+ switch_versions.add(version)
+
+ # fabricNode.version in older versions like 3.2 shows an invalid version like "A"
+ # which cannot be parsed by AciVersion. In that case, query the firmware class.
+ is_old_version = False
+ try:
+ apic_version = AciVersion(apic_version)
+ except ValueError:
+ is_old_version = True
+ apic1_firmware = icurl('mo', apic1_dn + "/sys/ctrlrfwstatuscont/ctrlrrunning.json")
+ if not apic1_firmware:
+ prints("Unable to find current APIC version.")
+ sys.exit(1)
+ apic1_version = apic1_firmware[0]['firmwareCtrlrRunning']['attributes']['version']
+ apic_version = AciVersion(apic1_version)
+
+ prints("Current APIC Version...{}".format(apic_version))
+
+ if is_old_version:
+ prints("Checking Switch Version ...")
+ firmwares = icurl('class', 'firmwareRunning.json')
+ for firmware in firmwares:
+ switch_versions.add(firmware['firmwareRunning']['attributes']['peVer'])
+
+ msg = "Lowest Switch Version...{}"
+ if not switch_versions:
+ prints(msg.format("Not Found! Join switches to the fabric then re-run this script.\n"))
+ return apic_version, None
+
+ lowest_sw_ver = AciVersion(switch_versions.pop())
+ for sw_version in switch_versions:
+ sw_version = AciVersion(sw_version)
+ if lowest_sw_ver.newer_than(sw_version):
+ lowest_sw_ver = sw_version
+ prints(msg.format(lowest_sw_ver) + "\n")
+ return apic_version, lowest_sw_ver
+
+
def get_vpc_nodes():
""" Returns list of VPC Node IDs; ['101', '102', etc...] """
prints("Collecting VPC Node IDs...", end='')
@@ -1406,26 +1778,31 @@ def get_vpc_nodes():
return vpc_nodes
-def get_switch_version():
- """ Returns lowest switch version as AciVersion instance """
- prints("Gathering Lowest Switch Version from Firmware Repository...", end='')
- firmwares = icurl('class', 'firmwareRunning.json')
- versions = set()
-
- for firmware in firmwares:
- versions.add(firmware['firmwareRunning']['attributes']['peVer'])
+def query_common_data(api_only=False, arg_cversion=None, arg_tversion=None):
+ username = password = None
+ if not api_only:
+ username, password = get_credentials()
- if versions:
- lowest_sw_ver = AciVersion(versions.pop())
- for version in versions:
- version = AciVersion(version)
- if lowest_sw_ver.newer_than(str(version)):
- lowest_sw_ver = version
- prints('%s\n' % lowest_sw_ver)
- return lowest_sw_ver
- else:
- prints("No Switches Detected! Join switches to the fabric then re-run this script.\n")
- return None
+ try:
+ fabric_nodes = get_fabric_nodes()
+ cversion, sw_cversion = get_current_versions(fabric_nodes, arg_cversion)
+ tversion = get_target_version(arg_tversion)
+ vpc_nodes = get_vpc_nodes()
+ except Exception as e:
+ prints('\n\nError: %s' % e)
+ prints("Initial query failed. Ensure APICs are healthy. Ending script run.")
+ log.exception(e)
+ sys.exit(1)
+
+ return {
+ 'username': username,
+ 'password': password,
+ 'cversion': cversion,
+ 'tversion': tversion,
+ 'sw_cversion': sw_cversion,
+ 'fabric_nodes': fabric_nodes,
+ 'vpc_node_ids': vpc_nodes,
+ }
@check_wrapper(check_title="APIC Cluster Status")
@@ -1463,29 +1840,30 @@ def apic_cluster_health_check(cversion, **kwargs):
@check_wrapper(check_title="Switch Fabric Membership Status")
-def switch_status_check(**kwargs):
+def switch_status_check(fabric_nodes, **kwargs):
result = FAIL_UF
msg = ''
headers = ['Pod-ID', 'Node-ID', 'State']
data = []
- recommended_action = 'Bring this node back to "active"'
+ recommended_action = 'Bring these nodes back to "active"'
# fabricNode.fabricSt shows `disabled` for both Decommissioned and Maintenance (GIR).
# fabricRsDecommissionNode.debug==yes is required to show `disabled (Maintenance)`.
- fabricNodes = icurl('class', 'fabricNode.json?&query-target-filter=ne(fabricNode.role,"controller")')
girNodes = icurl('class',
'fabricRsDecommissionNode.json?&query-target-filter=eq(fabricRsDecommissionNode.debug,"yes")')
- for fabricNode in fabricNodes:
- state = fabricNode['fabricNode']['attributes']['fabricSt']
+ for fabric_node in fabric_nodes:
+ if fabric_node['fabricNode']['attributes']['role'] == "controller":
+ continue
+ state = fabric_node['fabricNode']['attributes']['fabricSt']
if state == 'active':
continue
- dn = re.search(node_regex, fabricNode['fabricNode']['attributes']['dn'])
+ dn = re.search(node_regex, fabric_node['fabricNode']['attributes']['dn'])
pod_id = dn.group("pod")
node_id = dn.group("node")
for gir in girNodes:
if node_id == gir['fabricRsDecommissionNode']['attributes']['targetId']:
state = state + ' (Maintenance)'
data.append([pod_id, node_id, state])
- if not fabricNodes:
+ if not fabric_nodes:
result = MANUAL
msg = 'Switch fabricNode not found!'
elif not data:
@@ -1519,14 +1897,13 @@ def maintp_grp_crossing_4_0_check(cversion, tversion, **kwargs):
@check_wrapper(check_title="NTP Status")
-def ntp_status_check(**kargs):
+def ntp_status_check(fabric_nodes, **kargs):
result = FAIL_UF
headers = ["Pod-ID", "Node-ID"]
data = []
recommended_action = 'Not Synchronized. Check NTP config and NTP server reachability.'
doc_url = "https://datacenter.github.io/ACI-Pre-Upgrade-Validation-Script/validations/#ntp-status"
- fabricNodes = icurl('class', 'fabricNode.json')
- nodes = [fn['fabricNode']['attributes']['id'] for fn in fabricNodes]
+ nodes = [fn['fabricNode']['attributes']['id'] for fn in fabric_nodes]
apicNTPs = icurl('class', 'datetimeNtpq.json')
switchNTPs = icurl('class', 'datetimeClkPol.json')
for apicNTP in apicNTPs:
@@ -1539,7 +1916,7 @@ def ntp_status_check(**kargs):
dn = re.search(node_regex, switchNTP['datetimeClkPol']['attributes']['dn'])
if dn and dn.group('node') in nodes:
nodes.remove(dn.group('node'))
- for fn in fabricNodes:
+ for fn in fabric_nodes:
if fn['fabricNode']['attributes']['id'] in nodes:
dn = re.search(node_regex, fn['fabricNode']['attributes']['dn'])
data.append([dn.group('pod'), dn.group('node')])
@@ -1591,7 +1968,7 @@ def features_to_disable_check(cversion, tversion, **kwargs):
@check_wrapper(check_title="Switch Upgrade Group Guidelines")
-def switch_group_guideline_check(**kwargs):
+def switch_group_guideline_check(fabric_nodes, **kwargs):
result = FAIL_O
headers = ['Group Name', 'Pod-ID', 'Node-IDs', 'Failure Reason']
data = []
@@ -1610,8 +1987,7 @@ def switch_group_guideline_check(**kwargs):
reason_vpc = 'Both leaf nodes in the same vPC pair are in the same group.'
nodes = {}
- fabricNodes = icurl('class', 'fabricNode.json')
- for fn in fabricNodes:
+ for fn in fabric_nodes:
attr = fn['fabricNode']['attributes']
nodes[attr['dn']] = {'role': attr['role'], 'nodeType': attr['nodeType']}
@@ -1690,6 +2066,7 @@ def switch_group_guideline_check(**kwargs):
'pod': vpc_peer['fabricNodePEp']['attributes']['podId']
})
if len(m_vpc_peers) > 1:
+ m_vpc_peers.sort(key=lambda d: d['node'])
data.append([m_name, m_vpc_peers[0]['pod'],
','.join(x['node'] for x in m_vpc_peers),
reason_vpc])
@@ -1716,7 +2093,7 @@ def switch_bootflash_usage_check(tversion, **kwargs):
partitions = icurl('class', partitions_api)
if not partitions:
- return Result(result=ERROR, msg='bootflash objects not found', doc_url=doc_url)
+ return Result(result=MANUAL, msg='bootflash objects not found. Check switch health.', doc_url=doc_url)
predownloaded_nodes = []
try:
@@ -2211,7 +2588,7 @@ def switch_ssd_check(**kwargs):
}
doc_url = "https://datacenter.github.io/ACI-Pre-Upgrade-Validation-Script/validations/#switch-ssd-health"
- cs_regex = r'model \(New: (?P\w+)\),'
+ cs_regex = r"model:(?P\w+),"
faultInsts = icurl('class',
'faultInst.json?query-target-filter=or(eq(faultInst.code,"F3073"),eq(faultInst.code,"F3074"))')
for faultInst in faultInsts:
@@ -2241,53 +2618,87 @@ def switch_ssd_check(**kwargs):
# Connection Based Check
@check_wrapper(check_title="APIC SSD Health")
-def apic_ssd_check(cversion, username, password, **kwargs):
+def apic_ssd_check(cversion, username, password, fabric_nodes, **kwargs):
result = FAIL_UF
- headers = ["Pod", "Node", "Storage Unit", "% lifetime remaining", "Recommended Action"]
+ headers = ["APIC ID", "APIC Name", "Storage Unit", "% lifetime remaining", "Recommended Action"]
data = []
- unformatted_headers = ["Fault", "Fault DN", "% lifetime remaining", "Recommended Action"]
+ unformatted_headers = ["Fault DN", "% lifetime remaining", "Recommended Action"]
unformatted_data = []
recommended_action = "Contact TAC for replacement"
doc_url = "https://datacenter.github.io/ACI-Pre-Upgrade-Validation-Script/validations/#apic-ssd-health"
- has_error = False
dn_regex = node_regex + r'/.+p-\[(?P.+)\]-f'
- faultInsts = icurl('class', 'faultInst.json?query-target-filter=eq(faultInst.code,"F2731")')
- adjust_title = False
- if len(faultInsts) == 0 and (cversion.older_than("4.2(7f)") or cversion.older_than("5.2(1g)")):
- controller = icurl('class', 'topSystem.json?query-target-filter=eq(topSystem.role,"controller")')
- if not controller:
- return Result(result=ERROR, msg="topSystem response empty. Is the cluster healthy?", doc_url=doc_url)
-
- print('')
- adjust_title = True
+ threshold = {"F2731": "<5% (Fault F2731)", "F2732": "<1% (Fault F2732)"}
+ # Not checking F0101 because if APIC SSD is not operaitonal, the given APIC
+ # does not work at all and APIC clustering should be broken.
+ faultInsts = icurl('class', 'faultInst.json?query-target-filter=or(eq(faultInst.code,"F2731"),eq(faultInst.code,"F2732"))')
+ for faultInst in faultInsts:
+ code = faultInst["faultInst"]["attributes"]["code"]
+ lifetime_remaining = threshold.get(code, "unknown")
+ dn_match = re.search(dn_regex, faultInst['faultInst']['attributes']['dn'])
+ if dn_match:
+ apic_name = "-"
+ for node in fabric_nodes:
+ if node["fabricNode"]["attributes"]["id"] == dn_match.group("node"):
+ apic_name = node["fabricNode"]["attributes"]["name"]
+ break
+ data.append([
+ dn_match.group("node"),
+ apic_name,
+ dn_match.group("storage"),
+ lifetime_remaining,
+ recommended_action,
+ ])
+ else:
+ unformatted_data.append([
+ faultInst['faultInst']['attributes']['dn'],
+ lifetime_remaining,
+ recommended_action,
+ ])
+
+ # Versions older than 4.2(7f) or 5.x - 5.2(1g) may fail to raise F273x.
+ # Check logs for those just in case.
+ has_error = False
+ if (
+ len(faultInsts) == 0
+ and (
+ cversion.older_than("4.2(7f)")
+ or (cversion.major1 == "5" and cversion.older_than("5.2(1g)"))
+ )
+ ):
+ apics = [node for node in fabric_nodes if node["fabricNode"]["attributes"]["role"] == "controller"]
+ if not apics:
+ return Result(result=ERROR, msg="No fabricNode of APIC. Is the cluster healthy?", doc_url=doc_url)
+ # `fabricNode` in pre-4.0 does not have `address`
+ if not apics[0]["fabricNode"]["attributes"].get("address"):
+ apic1 = [apic for apic in apics if apic["fabricNode"]["attributes"]["id"] == "1"][0]
+ apic1_dn = apic1["fabricNode"]["attributes"]["dn"]
+ apics = icurl("class", "{}/infraWiNode.json".format(apic1_dn))
+
report_other = False
- checked_apics = {}
- for apic in controller:
- attr = apic['topSystem']['attributes']
- if attr['address'] in checked_apics: continue
- checked_apics[attr['address']] = 1
- pod_id = attr['podId']
- node_id = attr['id']
- node_title = 'Checking %s...' % attr['name']
- print_title(node_title)
+ for apic in apics:
+ if apic.get("fabricNode"):
+ apic_id = apic["fabricNode"]["attributes"]["id"]
+ apic_name = apic["fabricNode"]["attributes"]["name"]
+ apic_addr = apic["fabricNode"]["attributes"]["address"]
+ else:
+ apic_id = apic["infraWiNode"]["attributes"]["id"]
+ apic_name = apic["infraWiNode"]["attributes"]["nodeName"]
+ apic_addr = apic["infraWiNode"]["attributes"]["addr"]
try:
- c = Connection(attr['address'])
+ c = Connection(apic_addr)
c.username = username
c.password = password
c.log = LOG_FILE
c.connect()
except Exception as e:
- data.append([attr['id'], attr['name'], '-', '-', str(e)])
- print_result(node_title, ERROR)
+ data.append([apic_id, apic_name, '-', '-', str(e)])
has_error = True
continue
try:
- c.cmd(
- 'grep -oE "SSD Wearout Indicator is [0-9]+" /var/log/dme/log/svc_ifc_ae.bin.log | tail -1')
+ c.cmd('grep -oE "SSD Wearout Indicator is [0-9]+" /var/log/dme/log/svc_ifc_ae.bin.log | tail -1')
except Exception as e:
- data.append([attr['id'], attr['name'], '-', '-', str(e)])
- print_result(node_title, ERROR)
+ data.append([apic_id, apic_name, '-', '-', str(e)])
has_error = True
continue
@@ -2295,24 +2706,11 @@ def apic_ssd_check(cversion, username, password, **kwargs):
if wearout_ind is not None:
wearout = wearout_ind.group('wearout')
if int(wearout) < 5:
- data.append([pod_id, node_id, "Solid State Disk", wearout, recommended_action])
+ data.append([apic_id, apic_name, "Solid State Disk", wearout, recommended_action])
report_other = True
- print_result(node_title, DONE)
continue
if report_other:
- data.append([pod_id, node_id, "Solid State Disk", wearout, "No Action Required"])
- print_result(node_title, DONE)
- else:
- headers = ["Fault", "Pod", "Node", "Storage Unit", "% lifetime remaining", "Recommended Action"]
- for faultInst in faultInsts:
- dn_array = re.search(dn_regex, faultInst['faultInst']['attributes']['dn'])
- lifetime_remaining = "<5%"
- if dn_array:
- data.append(['F2731', dn_array.group("pod"), dn_array.group("node"), dn_array.group("storage"),
- lifetime_remaining, recommended_action])
- else:
- unformatted_data.append(
- ['F2731', faultInst['faultInst']['attributes']['dn'], lifetime_remaining, recommended_action])
+ data.append([apic_id, apic_name, "Solid State Disk", wearout, "No Action Required"])
if has_error:
result = ERROR
elif not data and not unformatted_data:
@@ -2324,7 +2722,6 @@ def apic_ssd_check(cversion, username, password, **kwargs):
unformatted_headers=unformatted_headers,
unformatted_data=unformatted_data,
doc_url=doc_url,
- adjust_title=adjust_title,
)
@@ -2895,9 +3292,9 @@ def lldp_with_infra_vlan_mismatch_check(**kwargs):
# Connection Based Check
@check_wrapper(check_title="APIC Target version image and MD5 hash")
-def apic_version_md5_check(tversion, username, password, **kwargs):
+def apic_version_md5_check(tversion, username, password, fabric_nodes, **kwargs):
result = FAIL_UF
- headers = ['APIC', 'Firmware', 'md5sum', 'Failure']
+ headers = ["APIC ID", "APIC Name", "Firmware", "md5sum", "Failure"]
data = []
recommended_action = 'Delete the firmware from APIC and re-download'
doc_url = "https://datacenter.github.io/ACI-Pre-Upgrade-Validation-Script/validations/#apic-target-version-image-and-md5-hash"
@@ -2912,7 +3309,7 @@ def apic_version_md5_check(tversion, username, password, **kwargs):
desc = fm_mo["firmwareFirmware"]['attributes']["description"]
md5 = fm_mo["firmwareFirmware"]['attributes']["checksum"]
if "Image signing verification failed" in desc:
- data.append(["All", str(tversion), md5, 'Target image is corrupted'])
+ data.append(["All", "-", str(tversion), md5, 'Target image is corrupted'])
image_validaton = False
if not image_validaton:
@@ -2921,24 +3318,33 @@ def apic_version_md5_check(tversion, username, password, **kwargs):
md5s = []
md5_names = []
+ apics = [node for node in fabric_nodes if node["fabricNode"]["attributes"]["role"] == "controller"]
+ if not apics:
+ return Result(result=ERROR, msg="No fabricNode of APIC. Is the cluster healthy?", doc_url=doc_url)
+ # `fabricNode` in pre-4.0 does not have `address`
+ if not apics[0]["fabricNode"]["attributes"].get("address"):
+ apic1 = [apic for apic in apics if apic["fabricNode"]["attributes"]["id"] == "1"][0]
+ apic1_dn = apic1["fabricNode"]["attributes"]["dn"]
+ apics = icurl("class", "{}/infraWiNode.json".format(apic1_dn))
+
has_error = False
- prints('')
- nodes_response_json = icurl('class', 'topSystem.json')
- for node in nodes_response_json:
- if node['topSystem']['attributes']['role'] != "controller":
- continue
- apic_name = node['topSystem']['attributes']['name']
- node_title = 'Checking %s...' % apic_name
- print_title(node_title)
+ for apic in apics:
+ if apic.get("fabricNode"):
+ apic_id = apic["fabricNode"]["attributes"]["id"]
+ apic_name = apic["fabricNode"]["attributes"]["name"]
+ apic_addr = apic["fabricNode"]["attributes"]["address"]
+ else:
+ apic_id = apic["infraWiNode"]["attributes"]["id"]
+ apic_name = apic["infraWiNode"]["attributes"]["nodeName"]
+ apic_addr = apic["infraWiNode"]["attributes"]["addr"]
try:
- c = Connection(node['topSystem']['attributes']['address'])
+ c = Connection(apic_addr)
c.username = username
c.password = password
c.log = LOG_FILE
c.connect()
except Exception as e:
- data.append([apic_name, '-', '-', str(e)])
- print_result(node_title, ERROR)
+ data.append([apic_id, apic_name, '-', '-', str(e)])
has_error = True
continue
@@ -2946,28 +3352,24 @@ def apic_version_md5_check(tversion, username, password, **kwargs):
c.cmd("ls -aslh /firmware/fwrepos/fwrepo/aci-apic-dk9.%s.bin" %
tversion.dot_version)
except Exception as e:
- data.append([apic_name, '-', '-',
+ data.append([apic_id, apic_name, '-', '-',
'ls command via ssh failed due to:{}'.format(str(e))])
- print_result(node_title, ERROR)
has_error = True
continue
if "No such file or directory" in c.output:
- data.append([apic_name, str(tversion), '-', 'image not found'])
- print_result(node_title, FAIL_UF)
+ data.append([apic_id, apic_name, str(tversion), '-', 'image not found'])
continue
try:
c.cmd("cat /firmware/fwrepos/fwrepo/md5sum/aci-apic-dk9.%s.bin" %
tversion.dot_version)
except Exception as e:
- data.append([apic_name, str(tversion), '-',
+ data.append([apic_id, apic_name, str(tversion), '-',
'failed to check md5sum via ssh due to:{}'.format(str(e))])
- print_result(node_title, ERROR)
has_error = True
continue
if "No such file or directory" in c.output:
- data.append([apic_name, str(tversion), '-', 'md5sum file not found'])
- print_result(node_title, FAIL_UF)
+ data.append([apic_id, apic_name, str(tversion), '-', 'md5sum file not found'])
continue
for line in c.output.split("\n"):
words = line.split()
@@ -2976,23 +3378,21 @@ def apic_version_md5_check(tversion, username, password, **kwargs):
words[1].startswith("/var/run/mgmt/fwrepos/fwrepo/aci-apic")
):
md5s.append(words[0])
- md5_names.append(apic_name)
+ md5_names.append([apic_id, apic_name])
break
else:
- data.append([apic_name, str(tversion), '-', 'unexpected output when checking md5sum file'])
- print_result(node_title, ERROR)
+ data.append([apic_id, apic_name, str(tversion), '-', 'unexpected output when checking md5sum file'])
has_error = True
continue
- print_result(node_title, DONE)
if len(set(md5s)) > 1:
- for name, md5 in zip(md5_names, md5s):
- data.append([name, str(tversion), md5, 'md5sum do not match on all APICs'])
+ for id_name, md5 in zip(md5_names, md5s):
+ data.append([id_name[0], id_name[1], str(tversion), md5, 'md5sum do not match on all APICs'])
if has_error:
result = ERROR
elif not data:
result = PASS
- return Result(result=result, headers=headers, data=data, recommended_action=recommended_action, doc_url=doc_url, adjust_title=True)
+ return Result(result=result, headers=headers, data=data, recommended_action=recommended_action, doc_url=doc_url)
# Connection Based Check
@@ -3052,7 +3452,7 @@ def standby_apic_disk_space_check(**kwargs):
@check_wrapper(check_title="Remote Leaf Compatibility")
-def r_leaf_compatibility_check(tversion, **kwargs):
+def r_leaf_compatibility_check(tversion, fabric_nodes, **kwargs):
result = PASS
headers = ['Target Version', 'Remote Leaf', 'Direct Traffic Forwarding']
data = []
@@ -3065,7 +3465,7 @@ def r_leaf_compatibility_check(tversion, **kwargs):
if not tversion:
return Result(result=MANUAL, msg=TVER_MISSING)
- remote_leafs = icurl('class', 'fabricNode.json?&query-target-filter=eq(fabricNode.nodeType,"remote-leaf-wan")')
+ remote_leafs = [node for node in fabric_nodes if node["fabricNode"]["attributes"]["nodeType"] == "remote-leaf-wan"]
if not remote_leafs:
return Result(result=NA, msg="No Remote Leaf Found")
@@ -3182,20 +3582,19 @@ def vmm_controller_adj_check(**kwargs):
@check_wrapper(check_title="VPC-paired Leaf switches")
-def vpc_paired_switches_check(vpc_node_ids, **kwargs):
+def vpc_paired_switches_check(vpc_node_ids, fabric_nodes, **kwargs):
result = PASS
headers = ["Node ID", "Node Name"]
data = []
recommended_action = 'Determine if dataplane redundancy is available if these nodes go down.'
doc_url = 'https://datacenter.github.io/ACI-Pre-Upgrade-Validation-Script/validations/#vpc-paired-leaf-switches'
- top_system = icurl('class', 'topSystem.json')
- for node in top_system:
- node_id = node['topSystem']['attributes']['id']
- role = node['topSystem']['attributes']['role']
+ for node in fabric_nodes:
+ node_id = node['fabricNode']['attributes']['id']
+ role = node['fabricNode']['attributes']['role']
if role == 'leaf' and (node_id not in vpc_node_ids):
result = MANUAL
- name = node['topSystem']['attributes']['name']
+ name = node['fabricNode']['attributes']['name']
data.append([node_id, name])
return Result(result=result, headers=headers, data=data, recommended_action=recommended_action, doc_url=doc_url)
@@ -3350,13 +3749,18 @@ def bgp_golf_route_target_type_check(cversion, tversion, **kwargs):
@check_wrapper(check_title="APIC Container Bridge IP Overlap with APIC TEP")
-def docker0_subnet_overlap_check(**kwargs):
+def docker0_subnet_overlap_check(cversion, **kwargs):
result = PASS
headers = ["Container Bridge IP", "APIC TEP"]
data = []
recommended_action = 'Change the container bridge IP via "Apps > Settings" on the APIC GUI'
doc_url = "https://datacenter.github.io/ACI-Pre-Upgrade-Validation-Script/validations/#apic-container-bridge-ip-overlap-with-apic-tep"
+ # AppCenter was deprecated in 6.1.2.
+ # Due to a bug the deprecated object returns totalCount:1 with empty data instead of totalCount:0.
+ if cversion.newer_than("6.1(2a)"):
+ return Result(result=NA, msg=VER_NOT_AFFECTED)
+
containerPols = icurl('mo', 'pluginPolContr/ContainerPol.json')
if not containerPols:
bip = "172.17.0.1/16"
@@ -3423,7 +3827,7 @@ def target_version_compatibility_check(cversion, tversion, **kwargs):
@check_wrapper(check_title="Gen 1 switch compatibility")
-def gen1_switch_compatibility_check(tversion, **kwargs):
+def gen1_switch_compatibility_check(tversion, fabric_nodes, **kwargs):
result = FAIL_UF
headers = ["Target Version", "Node ID", "Model", "Warning"]
gen1_models = ["N9K-C9336PQ", "N9K-X9736PQ", "N9K-C9504-FM", "N9K-C9508-FM", "N9K-C9516-FM", "N9K-C9372PX-E",
@@ -3431,13 +3835,12 @@ def gen1_switch_compatibility_check(tversion, **kwargs):
"N9K-C93128TX"]
data = []
recommended_action = 'Select supported target version or upgrade hardware'
- doc_url = 'http://cs.co/9001ydKCV'
+ doc_url = 'https://datacenter.github.io/ACI-Pre-Upgrade-Validation-Script/validations/#compatibility-switch-hardware-gen1'
if not tversion:
return Result(result=MANUAL, msg=TVER_MISSING)
if tversion.newer_than("5.0(1a)"):
- fabric_node = icurl('class', 'fabricNode.json')
- for node in fabric_node:
+ for node in fabric_nodes:
if node['fabricNode']['attributes']['model'] in gen1_models:
data.append([str(tversion), node['fabricNode']['attributes']['id'],
node['fabricNode']['attributes']['model'], 'Not supported on 5.x+'])
@@ -3678,22 +4081,26 @@ def apic_ca_cert_validation(**kwargs):
@check_wrapper(check_title="FabricDomain Name")
-def fabricdomain_name_check(cversion, tversion, **kwargs):
+def fabricdomain_name_check(cversion, tversion, fabric_nodes, **kwargs):
result = FAIL_O
headers = ["FabricDomain", "Reason"]
data = []
recommended_action = "Do not upgrade to 6.0(2)"
- doc_url = 'https://bst.cloudapps.cisco.com/bugsearch/bug/CSCwf80352'
+ doc_url = 'https://datacenter.github.io/ACI-Pre-Upgrade-Validation-Script/validations/#fabricdomain-name'
if not tversion:
return Result(result=MANUAL, msg=TVER_MISSING)
if tversion.same_as("6.0(2h)"):
- controller = icurl('class', 'topSystem.json?query-target-filter=eq(topSystem.role,"controller")')
- if not controller:
- return Result(result=ERROR, msg='topSystem response empty. Is the cluster healthy?')
-
- fabricDomain = controller[0]['topSystem']['attributes']['fabricDomain']
+ apic1 = [node for node in fabric_nodes if node["fabricNode"]["attributes"]["id"] == "1"]
+ if not apic1:
+ return Result(result=ERROR, msg='No fabricNode of APIC 1. Is the cluster healthy?')
+
+ # Using topSystem because fabricTopology.fabricDomain is not yet available prior to 5.2(6e).
+ apic1_topsys = icurl("mo", "/".join([apic1[0]["fabricNode"]["attributes"]["dn"], "sys.json"]))
+ if not apic1_topsys:
+ return Result(result=ERROR, msg='No topSystem of APIC 1. Is the cluster healthy?')
+ fabricDomain = apic1_topsys[0]['topSystem']['attributes']['fabricDomain']
if re.search(r'#|;', fabricDomain):
data.append([fabricDomain, "Contains a special character"])
@@ -3730,7 +4137,7 @@ def sup_hwrev_check(cversion, tversion, **kwargs):
sup_re = r'/.+(?Psupslot-\d+)'
sups = icurl('class', 'eqptSpCmnBlk.json?&query-target-filter=wcard(eqptSpromSupBlk.dn,"sup")')
if not sups:
- return Result(result=ERROR, msg='No sups found. This is unlikely.')
+ return Result(result=MANUAL, msg='No sups found. This is unlikely. Check switch health.')
for sup in sups:
prtNum = sup['eqptSpCmnBlk']['attributes']['prtNum']
@@ -3839,25 +4246,28 @@ def oob_mgmt_security_check(cversion, tversion, **kwargs):
@check_wrapper(check_title="Mini ACI Upgrade to 6.0(2)+")
-def mini_aci_6_0_2_check(cversion, tversion, **kwargs):
+def mini_aci_6_0_2_check(cversion, tversion, fabric_nodes, **kwargs):
result = FAIL_UF
- headers = ["Pod ID", "Node ID", "APIC Type", "Failure Reason"]
+ headers = ["Node ID", "Node Name", "APIC Type"]
data = []
recommended_action = "All virtual APICs must be removed from the cluster prior to upgrading to 6.0(2)+."
- doc_url = 'Upgrading Mini ACI - http://cs.co/9009bBTQB'
+ doc_url = 'https://datacenter.github.io/ACI-Pre-Upgrade-Validation-Script/validations/#mini-aci-upgrade-to-602-or-later'
if not tversion:
return Result(result=MANUAL, msg=TVER_MISSING)
- if cversion.older_than("6.0(2a)") and tversion.newer_than("6.0(2a)"):
- topSystem = icurl('class', 'topSystem.json?query-target-filter=wcard(topSystem.role,"controller")')
- if not topSystem:
- return Result(result=ERROR, msg='topSystem response empty. Is the cluster healthy?')
- for controller in topSystem:
- if controller['topSystem']['attributes']['nodeType'] == "virtual":
- pod_id = controller["topSystem"]["attributes"]["podId"]
- node_id = controller['topSystem']['attributes']['id']
- data.append([pod_id, node_id, "virtual", "Virtual APIC must be removed prior to upgrade to 6.0(2)+"])
+ if not (cversion.older_than("6.0(2a)") and tversion.newer_than("6.0(2a)")):
+ return Result(result=NA, msg=VER_NOT_AFFECTED, doc_url=doc_url)
+
+ apics = [node for node in fabric_nodes if node["fabricNode"]["attributes"]["role"] == "controller"]
+ if not apics:
+ return Result(result=ERROR, msg="No fabricNode of APIC. Is the cluster healthy?", doc_url=doc_url)
+
+ for apic in apics:
+ if apic['fabricNode']['attributes']['nodeType'] == "virtual":
+ node_id = apic['fabricNode']['attributes']['id']
+ node_name = apic['fabricNode']['attributes']['name']
+ data.append([node_id, node_name, "virtual"])
if not data:
result = PASS
@@ -4128,7 +4538,7 @@ def fabric_dpp_check(tversion, **kwargs):
@check_wrapper(check_title='N9K-C93108TC-FX3P/FX3H Interface Down')
-def n9k_c93108tc_fx3p_interface_down_check(tversion, **kwargs):
+def n9k_c93108tc_fx3p_interface_down_check(tversion, fabric_nodes, **kwargs):
result = PASS
headers = ["Node ID", "Node Name", "Product ID"]
data = []
@@ -4143,12 +4553,9 @@ def n9k_c93108tc_fx3p_interface_down_check(tversion, **kwargs):
or tversion.same_as("5.3(1d)")
or (tversion.major1 == "6" and tversion.older_than("6.0(4a)"))
):
- api = 'fabricNode.json'
- api += '?query-target-filter=or('
- api += 'eq(fabricNode.model,"N9K-C93108TC-FX3P"),'
- api += 'eq(fabricNode.model,"N9K-C93108TC-FX3H"))'
- nodes = icurl('class', api)
- for node in nodes:
+ for node in fabric_nodes:
+ if node["fabricNode"]["attributes"]["model"] not in ["N9K-C93108TC-FX3P", "N9K-C93108TC-FX3H"]:
+ continue
nodeid = node["fabricNode"]["attributes"]["id"]
name = node["fabricNode"]["attributes"]["name"]
pid = node["fabricNode"]["attributes"]["model"]
@@ -4802,7 +5209,7 @@ def validate_32_64_bit_image_check(cversion, tversion, **kwargs):
@check_wrapper(check_title='Fabric Link Redundancy')
-def fabric_link_redundancy_check(**kwargs):
+def fabric_link_redundancy_check(fabric_nodes, **kwargs):
result = PASS
headers = ["Leaf Name", "Fabric Link Adjacencies", "Problem"]
data = []
@@ -4811,20 +5218,18 @@ def fabric_link_redundancy_check(**kwargs):
t1_recommended_action = "Connect the tier 2 leaf switch(es) to multiple tier1 leaf switches for redundancy"
doc_url = "https://datacenter.github.io/ACI-Pre-Upgrade-Validation-Script/validations/#fabric-link-redundancy"
- fabric_nodes_api = 'fabricNode.json'
- fabric_nodes_api += '?query-target-filter=and(or(eq(fabricNode.role,"leaf"),eq(fabricNode.role,"spine")),eq(fabricNode.fabricSt,"active"))'
-
lldp_adj_api = 'lldpAdjEp.json'
lldp_adj_api += '?query-target-filter=wcard(lldpAdjEp.sysDesc,"topology/pod")'
- fabricNodes = icurl("class", fabric_nodes_api)
spines = {}
leafs = {}
t2leafs = {}
- for node in fabricNodes:
+ for node in fabric_nodes:
if node["fabricNode"]["attributes"]["nodeType"] == "remote-leaf-wan":
# Not applicable to remote leafs, skip
continue
+ if node["fabricNode"]["attributes"]["fabricSt"] != "active":
+ continue
dn = node["fabricNode"]["attributes"]["dn"]
name = node["fabricNode"]["attributes"]["name"]
if node["fabricNode"]["attributes"]["role"] == "spine":
@@ -4938,7 +5343,7 @@ def out_of_service_ports_check(**kwargs):
@check_wrapper(check_title='FC/FCOE support removed for -EX platforms')
-def fc_ex_model_check(tversion, **kwargs):
+def fc_ex_model_check(tversion, fabric_nodes, **kwargs):
result = PASS
headers = ["FC/FCOE Node ID", "Model"]
data = []
@@ -4946,8 +5351,6 @@ def fc_ex_model_check(tversion, **kwargs):
doc_url = 'https://datacenter.github.io/ACI-Pre-Upgrade-Validation-Script/validations/#fcfcoe-support-for-ex-switches'
fcEntity_api = "fcEntity.json"
- fabricNode_api = 'fabricNode.json'
- fabricNode_api += '?query-target-filter=wcard(fabricNode.model,".*EX")'
if not tversion:
return Result(result=MANUAL, msg=TVER_MISSING)
@@ -4955,13 +5358,11 @@ def fc_ex_model_check(tversion, **kwargs):
if (tversion.newer_than("6.0(7a)") and tversion.older_than("6.0(9c)")) or tversion.same_as("6.1(1f)"):
fcEntitys = icurl('class', fcEntity_api)
fc_nodes = []
- if fcEntitys:
- for fcEntity in fcEntitys:
- fc_nodes.append(fcEntity['fcEntity']['attributes']['dn'].split('/sys')[0])
+ for fcEntity in fcEntitys:
+ fc_nodes.append(fcEntity['fcEntity']['attributes']['dn'].split('/sys')[0])
if fc_nodes:
- fabricNodes = icurl('class', fabricNode_api)
- for node in fabricNodes:
+ for node in fabric_nodes:
node_dn = node['fabricNode']['attributes']['dn']
if node_dn in fc_nodes:
model = node['fabricNode']['attributes']['model']
@@ -5040,7 +5441,7 @@ def clock_signal_component_failure_check(**kwargs):
@check_wrapper(check_title='Stale Decomissioned Spine')
-def stale_decomissioned_spine_check(tversion, **kwargs):
+def stale_decomissioned_spine_check(tversion, fabric_nodes, **kwargs):
result = PASS
headers = ["Susceptible Spine Node Id", "Spine Name", "Current Node State"]
data = []
@@ -5048,8 +5449,6 @@ def stale_decomissioned_spine_check(tversion, **kwargs):
doc_url = 'https://datacenter.github.io/ACI-Pre-Upgrade-Validation-Script/validations/#stale-decommissioned-spine'
decomissioned_api = 'fabricRsDecommissionNode.json'
- active_spine_api = 'topSystem.json'
- active_spine_api += '?query-target-filter=eq(topSystem.role,"spine")'
if not tversion:
return Result(result=MANUAL, msg=TVER_MISSING)
@@ -5059,13 +5458,16 @@ def stale_decomissioned_spine_check(tversion, **kwargs):
if decomissioned_switches:
decommissioned_node_ids = [node['fabricRsDecommissionNode']['attributes']['targetId'] for node in decomissioned_switches]
- active_spine_mo = icurl('class', active_spine_api)
- for spine in active_spine_mo:
- node_id = spine['topSystem']['attributes']['id']
- name = spine['topSystem']['attributes']['name']
- state = spine['topSystem']['attributes']['state']
+ for node in fabric_nodes:
+ if node["fabricNode"]["attributes"]["role"] != "spine":
+ continue
+ if node["fabricNode"]["attributes"]["fabricSt"] != "active":
+ continue
+ node_id = node["fabricNode"]["attributes"]["id"]
+ name = node["fabricNode"]["attributes"]["name"]
+ fabricSt = node["fabricNode"]["attributes"]["fabricSt"]
if node_id in decommissioned_node_ids:
- data.append([node_id, name, state])
+ data.append([node_id, name, fabricSt])
if data:
result = FAIL_O
return Result(result=result, headers=headers, data=data, recommended_action=recommended_action, doc_url=doc_url)
@@ -5334,43 +5736,47 @@ def service_bd_forceful_routing_check(cversion, tversion, **kwargs):
# Connection Base Check
@check_wrapper(check_title='Observer Database Size')
-def observer_db_size_check(username, password, **kwargs):
+def observer_db_size_check(username, password, fabric_nodes, **kwargs):
result = PASS
- headers = ["Node", "File Location", "Size (GB)"]
+ headers = ["APIC ID", "APIC Name", "File Location", "Size (GB)"]
data = []
recommended_action = 'Contact TAC to analyze and truncate large DB files'
doc_url = 'https://datacenter.github.io/ACI-Pre-Upgrade-Validation-Script/validations#observer-database-size'
- topSystem_api = 'topSystem.json'
- topSystem_api += '?query-target-filter=eq(topSystem.role,"controller")'
-
- controllers = icurl('class', topSystem_api)
- if not controllers:
- return Result(result=ERROR, msg='topSystem response empty. Is the cluster healthy?')
+ apics = [node for node in fabric_nodes if node["fabricNode"]["attributes"]["role"] == "controller"]
+ if not apics:
+ return Result(result=ERROR, msg="No fabricNode of APIC. Is the cluster healthy?", doc_url=doc_url)
+ # `fabricNode` in pre-4.0 does not have `address`
+ if not apics[0]["fabricNode"]["attributes"].get("address"):
+ apic1 = [apic for apic in apics if apic["fabricNode"]["attributes"]["id"] == "1"][0]
+ apic1_dn = apic1["fabricNode"]["attributes"]["dn"]
+ apics = icurl("class", "{}/infraWiNode.json".format(apic1_dn))
has_error = False
- prints('')
- for apic in controllers:
- attr = apic['topSystem']['attributes']
- node_title = 'Checking %s...' % attr['name']
- print_title(node_title)
+ for apic in apics:
+ if apic.get("fabricNode"):
+ apic_id = apic["fabricNode"]["attributes"]["id"]
+ apic_name = apic["fabricNode"]["attributes"]["name"]
+ apic_addr = apic["fabricNode"]["attributes"]["address"]
+ else:
+ apic_id = apic["infraWiNode"]["attributes"]["id"]
+ apic_name = apic["infraWiNode"]["attributes"]["nodeName"]
+ apic_addr = apic["infraWiNode"]["attributes"]["addr"]
try:
- c = Connection(attr['address'])
+ c = Connection(apic_addr)
c.username = username
c.password = password
c.log = LOG_FILE
c.connect()
except Exception as e:
- data.append([attr['id'], attr['name'], str(e)])
- print_result(node_title, ERROR)
+ data.append([apic_id, apic_name, "-", str(e)])
has_error = True
continue
try:
cmd = r"ls -lh /data2/dbstats | awk '{print $5, $9}'"
c.cmd(cmd)
if "No such file or directory" in c.output:
- data.append([attr['id'], '/data2/dbstats/ not found', "Check user permissions or retry as 'apic#fallback\\\\admin'"])
- print_result(node_title, ERROR)
+ data.append([apic_id, apic_name, '/data2/dbstats/ not found', "Check user permissions or retry as 'apic#fallback\\\\admin'"])
has_error = True
continue
dbstats = c.output.split("\n")
@@ -5380,18 +5786,16 @@ def observer_db_size_check(username, password, **kwargs):
if size_match:
file_size = size_match.group("size")
file_name = "/data2/dbstats/" + size_match.group("file")
- data.append([attr['id'], file_name, file_size])
- print_result(node_title, DONE)
+ data.append([apic_id, apic_name, file_name, file_size])
except Exception as e:
- data.append([attr['id'], attr['name'], str(e)])
- print_result(node_title, ERROR)
+ data.append([apic_id, apic_name, "-", str(e)])
has_error = True
continue
if has_error:
result = ERROR
elif data:
result = FAIL_UF
- return Result(result=result, headers=headers, data=data, recommended_action=recommended_action, doc_url=doc_url, adjust_title=True)
+ return Result(result=result, headers=headers, data=data, recommended_action=recommended_action, doc_url=doc_url)
@check_wrapper(check_title='AVE End-of-Life')
@@ -5539,7 +5943,7 @@ def configpush_shard_check(tversion, **kwargs):
doc_url = 'https://datacenter.github.io/ACI-Pre-Upgrade-Validation-Script/validations/#policydist-configpushshardcont-crash'
if not tversion:
- return Result(result=MANUAL, msg=TVER_MISSING)
+ return Result(result=MANUAL, msg=TVER_MISSING)
if tversion.older_than("6.1(4a)"):
result = PASS
@@ -5570,11 +5974,12 @@ def parse_args(args):
parser.add_argument("-n", "--no-cleanup", action="store_true", help="Skip all file cleanup after script execution.")
parser.add_argument("-v", "--version", action="store_true", help="Only show the script version, then end.")
parser.add_argument("--total-checks", action="store_true", help="Only show the total number of checks, then end.")
+ parser.add_argument("--timeout", action="store", nargs="?", type=int, const=-1, default=DEFAULT_TIMEOUT, help="Show default script timeout (sec) or overwrite it when a number is provided (e.g. --timeout 1200).")
parsed_args = parser.parse_args(args)
return parsed_args
-def initialize():
+def init_system():
"""
Initialize the script environment, create necessary directories and set up log.
Not required for some options such as `--version` or `--total-checks`.
@@ -5585,51 +5990,49 @@ def initialize():
log.info("Creating directories %s and %s", DIR, JSON_DIR)
os.mkdir(DIR)
os.mkdir(JSON_DIR)
- fmt = '[%(asctime)s.%(msecs)03d{} %(levelname)-8s %(funcName)20s:%(lineno)-4d] %(message)s'.format(tz)
+ fmt = '[%(asctime)s.%(msecs)03d{} %(levelname)-8s %(funcName)s:%(lineno)-4d(%(threadName)s)] %(message)s'.format(tz)
logging.basicConfig(level=logging.DEBUG, filename=LOG_FILE, format=fmt, datefmt='%Y-%m-%d %H:%M:%S')
-def prepare(api_only, arg_tversion, arg_cversion, checks):
- prints(' ==== %s%s, Script Version %s ====\n' % (ts, tz, SCRIPT_VERSION))
- prints('!!!! Check https://github.com/datacenter/ACI-Pre-Upgrade-Validation-Script for Latest Release !!!!\n')
+def wrapup_system(no_cleanup):
+ subprocess.check_output(['tar', '-czf', BUNDLE_NAME, DIR])
+ bundle_loc = '/'.join([os.getcwd(), BUNDLE_NAME])
+ prints("""
+ Pre-Upgrade Check Complete.
+ Next Steps: Address all checks flagged as FAIL, ERROR or MANUAL CHECK REQUIRED
- # Create empty result files for all checks
- for idx, check in enumerate(checks):
- check(idx + 1, len(checks), init=True)
+ Result output and debug info saved to below bundle for later reference.
+ Attach this bundle to Cisco TAC SRs opened to address the flagged checks.
- username = password = None
- if not api_only:
- username, password = get_credentials()
- try:
- cversion = get_current_version(arg_cversion)
- tversion = get_target_version(arg_tversion)
- vpc_nodes = get_vpc_nodes()
- sw_cversion = get_switch_version()
- except Exception as e:
- prints('\n\nError: %s' % e)
- prints("Initial query failed. Ensure APICs are healthy. Ending script run.")
- log.exception(e)
- sys.exit()
- inputs = {'username': username, 'password': password,
- 'cversion': cversion, 'tversion': tversion,
- 'vpc_node_ids': vpc_nodes, 'sw_cversion': sw_cversion}
- metadata = {
- "name": "PreupgradeCheck",
- "method": "standalone script",
- "datetime": ts + tz,
- "script_version": str(SCRIPT_VERSION),
- "cversion": str(cversion),
- "tversion": str(tversion),
- "sw_cversion": str(sw_cversion),
- "api_only": api_only,
- "total_checks": len(checks),
- }
- with open(META_FILE, "w") as f:
- json.dump(metadata, f, indent=2)
- return inputs
+ Result Bundle: {bundle}
+""".format(bundle=bundle_loc))
+ prints('==== Script Version %s FIN ====' % (SCRIPT_VERSION))
+
+ # puv integration needs to keep reading files from `JSON_DIR` under `DIR`.
+ if not no_cleanup and os.path.isdir(DIR):
+ log.info('Cleaning up temporary files and directories...')
+ shutil.rmtree(DIR)
-def get_checks(api_only, debug_function):
+class CheckManager:
+ """Central managing point of all checks.
+ Highlevel flows:
+ 1. Initialize checks
+ Through `intialize_check()` in the decorator `check_wrapper` for
+ each check, this does two things:
+ 1. get the mapping of check_title to check_id which is a check function name.
+ 2. write empty `AciResult` of each check into a JSON result file.
+ which is automatically done via decorator `check_wrapper`.
+ 2. Run checks in thread
+ Monitor the progress with timeout
+ 3. Finalize check results
+ When checks completed within the time limit (`self.monitor_timeout`),
+ `finalize_check()` is called through `check_wrapper`.
+ This does two things:
+ 1. get the mapping of `Result` to check_id
+ 2. update the JSON result file with the new `AciResult`
+ 4. Print the result to stdout
+ """
api_checks = [
# General Checks
target_version_compatibility_check,
@@ -5721,10 +6124,9 @@ def get_checks(api_only, debug_function):
configpush_shard_check,
]
- conn_checks = [
+ ssh_checks = [
# General
apic_version_md5_check,
- apic_database_size_check,
# Faults
standby_apic_disk_space_check,
@@ -5732,62 +6134,97 @@ def get_checks(api_only, debug_function):
# Bugs
observer_db_size_check,
- apic_ca_cert_validation,
-
]
- if debug_function:
- return [check for check in api_checks + conn_checks if check.__name__ == debug_function]
- if api_only:
- return api_checks
- return conn_checks + api_checks
-
-
-def run_checks(checks, inputs):
- summary_headers = [PASS, FAIL_O, FAIL_UF, MANUAL, POST, NA, ERROR, 'TOTAL']
- summary = {key: 0 if key != 'TOTAL' else len(checks) for key in summary_headers}
- for idx, check in enumerate(checks):
- try:
- r = check(idx + 1, len(checks), **inputs)
- summary[r] += 1
- except KeyboardInterrupt:
- prints('\n\n!!! KeyboardInterrupt !!!\n')
- break
- except Exception as e:
- prints('')
- err = 'Wrapper Error: %s' % e
- print_title(err)
- print_result(title=err, result=ERROR)
- summary[ERROR] += 1
- logging.exception(e)
+ cli_checks = [
+ # General
+ apic_database_size_check,
- prints('\n=== Summary Result ===\n')
- res = max(summary_headers, key=len)
- max_header_len = len(res)
- for key in summary_headers:
- prints('{:{}} : {:2}'.format(key, max_header_len, summary[key]))
+ # Bugs
+ apic_ca_cert_validation,
+ ]
- with open(SUMMARY_FILE, 'w') as f:
- json.dump(summary, f, indent=2)
+ def __init__(self, api_only=False, debug_function="", timeout=600, monitor_interval=0.5):
+ self.api_only = api_only
+ self.debug_function = debug_function
+ self.monitor_interval = monitor_interval # sec
+ self.monitor_timeout = timeout # sec
+ self.timeout_event = None
+ self.check_funcs = self.get_check_funcs()
-def wrapup(no_cleanup):
- subprocess.check_output(['tar', '-czf', BUNDLE_NAME, DIR])
- bundle_loc = '/'.join([os.getcwd(), BUNDLE_NAME])
- prints("""
- Pre-Upgrade Check Complete.
- Next Steps: Address all checks flagged as FAIL, ERROR or MANUAL CHECK REQUIRED
+ self.rm = ResultManager()
- Result output and debug info saved to below bundle for later reference.
- Attach this bundle to Cisco TAC SRs opened to address the flagged checks.
-
- Result Bundle: {bundle}
- """.format(bundle=bundle_loc))
- prints('==== Script Version %s FIN ====' % (SCRIPT_VERSION))
+ @property
+ def total_checks(self):
+ return len(self.check_funcs)
- # puv integration needs to keep reading files from `JSON_DIR` under `DIR`.
- if not no_cleanup and os.path.isdir(DIR):
- log.info('Cleaning up temporary files and directories...')
- shutil.rmtree(DIR)
+ @property
+ def check_ids(self):
+ return [check_func.__name__ for check_func in self.check_funcs]
+
+ def get_check_funcs(self):
+ all_checks = [] + self.api_checks # must be a new list to avoid changing api_checks
+ if not self.api_only:
+ all_checks += self.ssh_checks + self.cli_checks
+ if self.debug_function:
+ return [check for check in all_checks if check.__name__ == self.debug_function]
+ return all_checks
+
+ def get_check_title(self, check_id):
+ title = self.rm.titles.get(check_id, "")
+ if not title:
+ log.error("Failed to find title for {}".format(check_id))
+ return title
+
+ def get_check_result(self, check_id):
+ result_obj = self.rm.results.get(check_id)
+ if not result_obj:
+ log.error("Failed to find result for {}".format(check_id))
+ return result_obj
+
+ def get_result_summary(self):
+ return self.rm.get_summary()
+
+ def initialize_check(self, check_id, check_title):
+ self.rm.init_result(check_id, check_title)
+
+ def finalize_check(self, check_id, result_obj):
+ # We do not update the result from here in the case of timeout.
+ if self.timeout_event and self.timeout_event.is_set():
+ return None
+ if not isinstance(result_obj, Result):
+ raise TypeError("The result of {} is not a `Result` object".format(check_id))
+ return None
+ self.rm.update_result(check_id, result_obj)
+
+ def finalize_check_on_thread_failure(self, check_id):
+ """Update the result of a check that couldn't start as ERROR"""
+ r = Result(result=ERROR, msg="Skipped due to a failure in starting a thread for this check.")
+ self.rm.update_result(check_id, r)
+
+ def finalize_check_on_thread_timeout(self, check_id):
+ """Update the result of a check that couldn't finish in time as ERROR"""
+ msg = "Timeout. Unable to finish in time ({} sec).".format(self.monitor_timeout)
+ r = Result(result=ERROR, msg=msg)
+ self.rm.update_result(check_id, r)
+
+ def initialize_checks(self):
+ for check_func in self.check_funcs:
+ check_func(initialize_check=self.initialize_check)
+
+ def run_checks(self, common_data):
+ tm = ThreadManager(
+ funcs=self.check_funcs,
+ common_kwargs=dict({"finalize_check": self.finalize_check}, **common_data),
+ monitor_interval=self.monitor_interval,
+ monitor_timeout=self.monitor_timeout,
+ callback_on_monitoring=print_progress,
+ callback_on_start_failure=self.finalize_check_on_thread_failure,
+ callback_on_timeout=self.finalize_check_on_thread_timeout,
+ )
+ self.timeout_event = tm.timeout_event
+ tm.start()
+ tm.join()
def main(_args=None):
@@ -5795,16 +6232,63 @@ def main(_args=None):
if args.version:
print(SCRIPT_VERSION)
return
- checks = get_checks(args.api_only, args.debug_function)
+
+ if args.timeout == -1:
+ print("Timeout(sec): {}".format(DEFAULT_TIMEOUT))
+ return
+
+ cm = CheckManager(args.api_only, args.debug_function, args.timeout)
+
if args.total_checks:
- print("Total Number of Checks: {}".format(len(checks)))
+ print("Total Number of Checks: {}".format(cm.total_checks))
return
- initialize()
- inputs = prepare(args.api_only, args.tversion, args.cversion, checks)
- run_checks(checks, inputs)
- wrapup(args.no_cleanup)
+ init_system()
+
+ # Initialize checks with empty results
+ cm.initialize_checks()
+
+ prints(' ==== %s%s, Script Version %s ====\n' % (ts, tz, SCRIPT_VERSION))
+ prints('!!!! Check https://github.com/datacenter/ACI-Pre-Upgrade-Validation-Script for Latest Release !!!!\n')
+
+ common_data = query_common_data(args.api_only, args.cversion, args.tversion)
+ write_script_metadata(args.api_only, args.timeout, cm.total_checks, common_data)
+
+ cm.run_checks(common_data)
+
+ # Print result reports
+ prints("\n")
+ if cm.timeout_event.is_set():
+ prints("Timeout !!! Abort and printing the results...\n")
+
+ prints("\n=== Check Result (failed only) ===\n")
+
+ # Print result of each failed check
+ for index, check_id in enumerate(cm.check_ids):
+ result_obj = cm.get_check_result(check_id)
+ if not result_obj or result_obj.result in (NA, PASS):
+ continue
+ check_title = cm.get_check_title(check_id)
+ print_result(index + 1, cm.total_checks, check_title, **result_obj.as_dict())
+
+ # Print summary
+ summary = cm.get_result_summary()
+ prints('\n=== Summary Result ===\n')
+ longest_header = max(summary.keys(), key=len)
+ max_header_len = len(longest_header)
+ for key in summary:
+ prints('{:{}} : {:2}'.format(key, max_header_len, summary[key]))
+
+ write_jsonfile(SUMMARY_FILE, summary)
+
+ wrapup_system(args.no_cleanup)
if __name__ == "__main__":
- main()
+ try:
+ main()
+ except Exception as e:
+ msg = "Abort due to unexpected error - {}".format(e)
+ prints(msg)
+ log.error(msg, exc_info=True)
+ sys.exit(1)
diff --git a/pytest.ini b/pytest.ini
index 015777f4..aac0d267 100644
--- a/pytest.ini
+++ b/pytest.ini
@@ -1,4 +1,4 @@
[pytest]
log_cli = true
log_cli_level = DEBUG
-log_cli_format = [%(asctime)s.%(msecs)03d %(levelname)-8s %(funcName)20s:%(lineno)-4d] %(message)s
+log_cli_format = [%(asctime)s.%(msecs)03d %(levelname)-8s %(funcName)s:%(lineno)-4d(%(threadName)s)] %(message)s
diff --git a/tests/apic_version_md5_check/topSystem.json b/tests/apic_version_md5_check/topSystem.json
deleted file mode 100644
index 65dca386..00000000
--- a/tests/apic_version_md5_check/topSystem.json
+++ /dev/null
@@ -1,1023 +0,0 @@
-{
- "totalCount": "17",
- "imdata": [
- {
- "topSystem": {
- "attributes": {
- "address": "10.2.0.1",
- "bootstrapState": "none",
- "childAction": "",
- "clusterTimeDiff": "0",
- "configIssues": "",
- "controlPlaneMTU": "1400",
- "currentTime": "2025-04-05T12:11:04.529-07:00",
- "dn": "topology/pod-1/node-1/sys",
- "enforceSubnetCheck": "no",
- "etepAddr": "0.0.0.0",
- "fabricDomain": "S2-Fabric",
- "fabricId": "1",
- "fabricMAC": "00:22:BD:F8:19:FF",
- "id": "1",
- "inbMgmtAddr": "192.168.12.1",
- "inbMgmtAddr6": "fc00::1",
- "inbMgmtAddr6Mask": "0",
- "inbMgmtAddrMask": "24",
- "inbMgmtGateway": "192.168.12.254",
- "inbMgmtGateway6": "::",
- "lastRebootTime": "2025-03-26T22:37:38.265-07:00",
- "lastResetReason": "unknown",
- "lcOwn": "local",
- "modTs": "2025-03-26T23:17:06.831-07:00",
- "mode": "unspecified",
- "monPolDn": "uni/fabric/monfab-default",
- "name": "S2-APIC-1",
- "nameAlias": "",
- "nodeType": "unspecified",
- "oobMgmtAddr": "192.168.100.12",
- "oobMgmtAddr6": "fe80::86b2:61ff:fe91:932e",
- "oobMgmtAddr6Mask": "0",
- "oobMgmtAddrMask": "22",
- "oobMgmtGateway": "192.168.100.1",
- "oobMgmtGateway6": "fc00::ffff",
- "podId": "1",
- "remoteNetworkId": "0",
- "remoteNode": "no",
- "rlAutoMode": "no",
- "rlGroupId": "0",
- "rlOperPodId": "0",
- "rlRoutableMode": "no",
- "rldirectMode": "no",
- "role": "controller",
- "serial": "ABC1234DEFG",
- "serverType": "unspecified",
- "siteId": "0",
- "state": "in-service",
- "status": "",
- "systemUpTime": "09:13:33:26.000",
- "tepPool": "0.0.0.0",
- "unicastXrEpLearnDisable": "no",
- "version": "6.1(2.145a)",
- "virtualMode": "no"
- }
- }
- },
- {
- "topSystem": {
- "attributes": {
- "address": "10.2.0.2",
- "bootstrapState": "none",
- "childAction": "",
- "clusterTimeDiff": "108403",
- "configIssues": "",
- "controlPlaneMTU": "1400",
- "currentTime": "2025-04-05T12:11:09.229-07:00",
- "dn": "topology/pod-1/node-2/sys",
- "enforceSubnetCheck": "no",
- "etepAddr": "0.0.0.0",
- "fabricDomain": "S2-Fabric",
- "fabricId": "1",
- "fabricMAC": "00:22:BD:F8:19:FF",
- "id": "2",
- "inbMgmtAddr": "192.168.12.2",
- "inbMgmtAddr6": "fc00::1",
- "inbMgmtAddr6Mask": "0",
- "inbMgmtAddrMask": "24",
- "inbMgmtGateway": "192.168.12.254",
- "inbMgmtGateway6": "::",
- "lastRebootTime": "2025-03-27T20:07:42.002-07:00",
- "lastResetReason": "unknown",
- "lcOwn": "local",
- "modTs": "2025-03-27T20:37:50.245-07:00",
- "mode": "unspecified",
- "monPolDn": "uni/fabric/monfab-default",
- "name": "S2-APIC-2",
- "nameAlias": "",
- "nodeType": "unspecified",
- "oobMgmtAddr": "192.168.100.13",
- "oobMgmtAddr6": "fe80::86b2:61ff:fe70:9b3e",
- "oobMgmtAddr6Mask": "0",
- "oobMgmtAddrMask": "22",
- "oobMgmtGateway": "192.168.100.1",
- "oobMgmtGateway6": "fc00::ffff",
- "podId": "1",
- "remoteNetworkId": "0",
- "remoteNode": "no",
- "rlAutoMode": "no",
- "rlGroupId": "0",
- "rlOperPodId": "0",
- "rlRoutableMode": "no",
- "rldirectMode": "no",
- "role": "controller",
- "serial": "ABC1235DEFG",
- "serverType": "unspecified",
- "siteId": "0",
- "state": "in-service",
- "status": "",
- "systemUpTime": "08:16:03:27.000",
- "tepPool": "0.0.0.0",
- "unicastXrEpLearnDisable": "no",
- "version": "6.1(2.145a)",
- "virtualMode": "no"
- }
- }
- },
- {
- "topSystem": {
- "attributes": {
- "address": "10.2.160.66",
- "bootstrapState": "done",
- "childAction": "",
- "clusterTimeDiff": "109356",
- "configIssues": "",
- "controlPlaneMTU": "1400",
- "currentTime": "2025-04-05T12:11:08.219-07:00",
- "dn": "topology/pod-1/node-101/sys",
- "enforceSubnetCheck": "no",
- "etepAddr": "192.168.201.1",
- "fabricDomain": "S2-Fabric",
- "fabricId": "1",
- "fabricMAC": "00:22:BD:F8:19:FF",
- "id": "101",
- "inbMgmtAddr": "192.168.12.101",
- "inbMgmtAddr6": "::",
- "inbMgmtAddr6Mask": "0",
- "inbMgmtAddrMask": "24",
- "inbMgmtGateway": "192.168.12.254",
- "inbMgmtGateway6": "0.0.0.0",
- "lastRebootTime": "2025-04-04T12:03:47.240-07:00",
- "lastResetReason": "unknown",
- "lcOwn": "local",
- "modTs": "2025-04-04T12:53:13.476-07:00",
- "mode": "unspecified",
- "monPolDn": "uni/fabric/monfab-default",
- "name": "S2-Leaf-101",
- "nameAlias": "",
- "nodeType": "unspecified",
- "oobMgmtAddr": "0.0.0.0",
- "oobMgmtAddr6": "::",
- "oobMgmtAddr6Mask": "0",
- "oobMgmtAddrMask": "0",
- "oobMgmtGateway": "0.0.0.0",
- "oobMgmtGateway6": "::",
- "podId": "1",
- "remoteNetworkId": "0",
- "remoteNode": "no",
- "rlOperPodId": "1",
- "rlRoutableMode": "no",
- "rldirectMode": "no",
- "role": "leaf",
- "serial": "ABC1236DEFG",
- "serverType": "unspecified",
- "siteId": "2",
- "state": "in-service",
- "status": "",
- "systemUpTime": "01:00:07:21.000",
- "tepPool": "10.2.0.0/16",
- "unicastXrEpLearnDisable": "yes",
- "version": "n9000-15.3(1d)",
- "virtualMode": "no"
- }
- }
- },
- {
- "topSystem": {
- "attributes": {
- "address": "10.2.0.3",
- "bootstrapState": "none",
- "childAction": "",
- "clusterTimeDiff": "166085",
- "configIssues": "",
- "controlPlaneMTU": "1400",
- "currentTime": "2025-04-05T12:10:03.540-07:00",
- "dn": "topology/pod-1/node-3/sys",
- "enforceSubnetCheck": "no",
- "etepAddr": "0.0.0.0",
- "fabricDomain": "S2-Fabric",
- "fabricId": "1",
- "fabricMAC": "00:22:BD:F8:19:FF",
- "id": "3",
- "inbMgmtAddr": "192.168.12.3",
- "inbMgmtAddr6": "fc00::1",
- "inbMgmtAddr6Mask": "0",
- "inbMgmtAddrMask": "24",
- "inbMgmtGateway": "192.168.12.254",
- "inbMgmtGateway6": "::",
- "lastRebootTime": "2025-03-19T23:01:21.686-07:00",
- "lastResetReason": "unknown",
- "lcOwn": "local",
- "modTs": "2025-03-19T23:06:46.170-07:00",
- "mode": "unspecified",
- "monPolDn": "uni/fabric/monfab-default",
- "name": "S2-APIC-3",
- "nameAlias": "",
- "nodeType": "unspecified",
- "oobMgmtAddr": "192.168.100.14",
- "oobMgmtAddr6": "fe80::86b2:61ff:fe70:7dde",
- "oobMgmtAddr6Mask": "0",
- "oobMgmtAddrMask": "22",
- "oobMgmtGateway": "192.168.100.1",
- "oobMgmtGateway6": "fc00::ffff",
- "podId": "1",
- "remoteNetworkId": "0",
- "remoteNode": "no",
- "rlAutoMode": "no",
- "rlGroupId": "0",
- "rlOperPodId": "0",
- "rlRoutableMode": "no",
- "rldirectMode": "no",
- "role": "controller",
- "serial": "ABC1237DEFG",
- "serverType": "unspecified",
- "siteId": "0",
- "state": "in-service",
- "status": "",
- "systemUpTime": "16:13:11:28.000",
- "tepPool": "0.0.0.0",
- "unicastXrEpLearnDisable": "no",
- "version": "6.1(2.145a)",
- "virtualMode": "no"
- }
- }
- },
- {
- "topSystem": {
- "attributes": {
- "address": "10.2.160.96",
- "bootstrapState": "done",
- "childAction": "",
- "clusterTimeDiff": "167238",
- "configIssues": "",
- "controlPlaneMTU": "1400",
- "currentTime": "2025-04-05T12:10:10.361-07:00",
- "dn": "topology/pod-1/node-103/sys",
- "enforceSubnetCheck": "no",
- "etepAddr": "192.168.201.1",
- "fabricDomain": "S2-Fabric",
- "fabricId": "1",
- "fabricMAC": "00:22:BD:F8:19:FF",
- "id": "103",
- "inbMgmtAddr": "192.168.12.103",
- "inbMgmtAddr6": "::",
- "inbMgmtAddr6Mask": "0",
- "inbMgmtAddrMask": "24",
- "inbMgmtGateway": "192.168.12.254",
- "inbMgmtGateway6": "::",
- "lastRebootTime": "2025-03-20T00:36:23.357-07:00",
- "lastResetReason": "installer",
- "lcOwn": "local",
- "modTs": "2025-03-27T19:21:12.187-07:00",
- "mode": "unspecified",
- "monPolDn": "uni/fabric/monfab-default",
- "name": "S2-Leaf-103",
- "nameAlias": "",
- "nodeType": "unspecified",
- "oobMgmtAddr": "0.0.0.0",
- "oobMgmtAddr6": "::",
- "oobMgmtAddr6Mask": "0",
- "oobMgmtAddrMask": "0",
- "oobMgmtGateway": "0.0.0.0",
- "oobMgmtGateway6": "::",
- "podId": "1",
- "remoteNetworkId": "0",
- "remoteNode": "no",
- "rlAutoMode": "no",
- "rlGroupId": "0",
- "rlOperPodId": "1",
- "rlRoutableMode": "no",
- "rldirectMode": "no",
- "role": "leaf",
- "serial": "ABC1238DEFG",
- "serverType": "unspecified",
- "siteId": "2",
- "state": "in-service",
- "status": "",
- "systemUpTime": "16:11:33:47.000",
- "tepPool": "10.2.0.0/16",
- "unicastXrEpLearnDisable": "yes",
- "version": "n9000-16.1(2.158)",
- "virtualMode": "no"
- }
- }
- },
- {
- "topSystem": {
- "attributes": {
- "address": "10.2.160.64",
- "bootstrapState": "done",
- "childAction": "",
- "clusterTimeDiff": "155713",
- "configIssues": "",
- "controlPlaneMTU": "1400",
- "currentTime": "2025-04-05T12:10:21.887-07:00",
- "dn": "topology/pod-1/node-104/sys",
- "enforceSubnetCheck": "no",
- "etepAddr": "192.168.201.1",
- "fabricDomain": "S2-Fabric",
- "fabricId": "1",
- "fabricMAC": "00:22:BD:F8:19:FF",
- "id": "104",
- "inbMgmtAddr": "192.168.12.104",
- "inbMgmtAddr6": "::",
- "inbMgmtAddr6Mask": "0",
- "inbMgmtAddrMask": "24",
- "inbMgmtGateway": "192.168.12.254",
- "inbMgmtGateway6": "::",
- "lastRebootTime": "2025-03-20T01:04:51.864-07:00",
- "lastResetReason": "installer",
- "lcOwn": "local",
- "modTs": "2025-03-27T19:59:43.390-07:00",
- "mode": "unspecified",
- "monPolDn": "uni/fabric/monfab-default",
- "name": "S2-Leaf-104",
- "nameAlias": "",
- "nodeType": "unspecified",
- "oobMgmtAddr": "0.0.0.0",
- "oobMgmtAddr6": "::",
- "oobMgmtAddr6Mask": "0",
- "oobMgmtAddrMask": "0",
- "oobMgmtGateway": "0.0.0.0",
- "oobMgmtGateway6": "::",
- "podId": "1",
- "remoteNetworkId": "0",
- "remoteNode": "no",
- "rlAutoMode": "no",
- "rlGroupId": "0",
- "rlOperPodId": "1",
- "rlRoutableMode": "no",
- "rldirectMode": "no",
- "role": "leaf",
- "serial": "ABC1239DEFG",
- "serverType": "unspecified",
- "siteId": "2",
- "state": "in-service",
- "status": "",
- "systemUpTime": "16:11:05:30.000",
- "tepPool": "10.2.0.0/16",
- "unicastXrEpLearnDisable": "yes",
- "version": "n9000-16.1(2.158)",
- "virtualMode": "no"
- }
- }
- },
- {
- "topSystem": {
- "attributes": {
- "address": "10.2.160.99",
- "bootstrapState": "done",
- "childAction": "",
- "clusterTimeDiff": "41285",
- "configIssues": "",
- "controlPlaneMTU": "1400",
- "currentTime": "2025-04-05T12:12:16.321-07:00",
- "dn": "topology/pod-1/node-106/sys",
- "enforceSubnetCheck": "no",
- "etepAddr": "192.168.201.1",
- "fabricDomain": "S2-Fabric",
- "fabricId": "1",
- "fabricMAC": "00:22:BD:F8:19:FF",
- "id": "106",
- "inbMgmtAddr": "192.168.12.106",
- "inbMgmtAddr6": "::",
- "inbMgmtAddr6Mask": "0",
- "inbMgmtAddrMask": "24",
- "inbMgmtGateway": "192.168.12.254",
- "inbMgmtGateway6": "::",
- "lastRebootTime": "2025-03-20T00:40:00.676-07:00",
- "lastResetReason": "installer",
- "lcOwn": "local",
- "modTs": "2025-03-20T00:52:58.843-07:00",
- "mode": "unspecified",
- "monPolDn": "uni/fabric/monfab-default",
- "name": "S2-Leaf-106",
- "nameAlias": "",
- "nodeType": "unspecified",
- "oobMgmtAddr": "0.0.0.0",
- "oobMgmtAddr6": "::",
- "oobMgmtAddr6Mask": "0",
- "oobMgmtAddrMask": "0",
- "oobMgmtGateway": "0.0.0.0",
- "oobMgmtGateway6": "::",
- "podId": "1",
- "remoteNetworkId": "0",
- "remoteNode": "no",
- "rlAutoMode": "no",
- "rlGroupId": "0",
- "rlOperPodId": "1",
- "rlRoutableMode": "no",
- "rldirectMode": "no",
- "role": "leaf",
- "serial": "ABC1240DEFG",
- "serverType": "unspecified",
- "siteId": "2",
- "state": "in-service",
- "status": "",
- "systemUpTime": "16:11:32:15.000",
- "tepPool": "10.2.0.0/16",
- "unicastXrEpLearnDisable": "yes",
- "version": "n9000-16.1(2.158)",
- "virtualMode": "no"
- }
- }
- },
- {
- "topSystem": {
- "attributes": {
- "address": "192.168.221.45",
- "bootstrapState": "done",
- "childAction": "",
- "clusterTimeDiff": "160428",
- "configIssues": "",
- "controlPlaneMTU": "1400",
- "currentTime": "2025-04-05T12:10:17.178-07:00",
- "dn": "topology/pod-1/node-213/sys",
- "enforceSubnetCheck": "no",
- "etepAddr": "192.168.201.1",
- "fabricDomain": "S2-Fabric",
- "fabricId": "1",
- "fabricMAC": "00:22:BD:F8:19:FF",
- "id": "213",
- "inbMgmtAddr": "0.0.0.0",
- "inbMgmtAddr6": "::",
- "inbMgmtAddr6Mask": "0",
- "inbMgmtAddrMask": "0",
- "inbMgmtGateway": "0.0.0.0",
- "inbMgmtGateway6": "::",
- "lastRebootTime": "2025-03-20T00:36:57.724-07:00",
- "lastResetReason": "installer",
- "lcOwn": "local",
- "modTs": "2025-03-20T00:50:56.201-07:00",
- "mode": "unspecified",
- "monPolDn": "uni/fabric/monfab-default",
- "name": "RL-213",
- "nameAlias": "",
- "nodeType": "remote-leaf-wan",
- "oobMgmtAddr": "0.0.0.0",
- "oobMgmtAddr6": "::",
- "oobMgmtAddr6Mask": "0",
- "oobMgmtAddrMask": "0",
- "oobMgmtGateway": "0.0.0.0",
- "oobMgmtGateway6": "::",
- "podId": "1",
- "remoteNetworkId": "0",
- "remoteNode": "yes",
- "rlAutoMode": "yes",
- "rlGroupId": "1",
- "rlOperPodId": "1",
- "rlRoutableMode": "yes",
- "rldirectMode": "yes",
- "role": "leaf",
- "serial": "ABC1241DEFG",
- "serverType": "unspecified",
- "siteId": "2",
- "state": "in-service",
- "status": "",
- "systemUpTime": "16:11:33:19.000",
- "tepPool": "192.168.221.0/25",
- "unicastXrEpLearnDisable": "yes",
- "version": "n9000-16.1(2.158)",
- "virtualMode": "no"
- }
- }
- },
- {
- "topSystem": {
- "attributes": {
- "address": "10.3.96.64",
- "bootstrapState": "done",
- "childAction": "",
- "clusterTimeDiff": "-46384",
- "configIssues": "",
- "controlPlaneMTU": "1400",
- "currentTime": "2025-04-05T12:13:43.988-07:00",
- "dn": "topology/pod-2/node-202/sys",
- "enforceSubnetCheck": "no",
- "etepAddr": "192.168.202.1",
- "fabricDomain": "S2-Fabric",
- "fabricId": "1",
- "fabricMAC": "00:22:BD:F8:19:FF",
- "id": "202",
- "inbMgmtAddr": "192.168.12.212",
- "inbMgmtAddr6": "::",
- "inbMgmtAddr6Mask": "0",
- "inbMgmtAddrMask": "24",
- "inbMgmtGateway": "192.168.12.254",
- "inbMgmtGateway6": "::",
- "lastRebootTime": "2025-03-20T00:39:48.586-07:00",
- "lastResetReason": "installer",
- "lcOwn": "local",
- "modTs": "2025-03-26T13:08:21.651-07:00",
- "mode": "unspecified",
- "monPolDn": "uni/fabric/monfab-default",
- "name": "S2-Leaf-202",
- "nameAlias": "",
- "nodeType": "unspecified",
- "oobMgmtAddr": "0.0.0.0",
- "oobMgmtAddr6": "::",
- "oobMgmtAddr6Mask": "0",
- "oobMgmtAddrMask": "0",
- "oobMgmtGateway": "0.0.0.0",
- "oobMgmtGateway6": "::",
- "podId": "2",
- "remoteNetworkId": "0",
- "remoteNode": "no",
- "rlAutoMode": "no",
- "rlGroupId": "0",
- "rlOperPodId": "2",
- "rlRoutableMode": "no",
- "rldirectMode": "no",
- "role": "leaf",
- "serial": "ABC1242DEFG",
- "serverType": "unspecified",
- "siteId": "2",
- "state": "in-service",
- "status": "",
- "systemUpTime": "16:11:33:55.000",
- "tepPool": "10.3.0.0/16",
- "unicastXrEpLearnDisable": "yes",
- "version": "n9000-16.1(2.158)",
- "virtualMode": "no"
- }
- }
- },
- {
- "topSystem": {
- "attributes": {
- "address": "192.168.221.89",
- "bootstrapState": "done",
- "childAction": "",
- "clusterTimeDiff": "1265729",
- "configIssues": "",
- "controlPlaneMTU": "1400",
- "currentTime": "2025-04-05T11:51:51.876-07:00",
- "dn": "topology/pod-1/node-212/sys",
- "enforceSubnetCheck": "no",
- "etepAddr": "192.168.201.1",
- "fabricDomain": "S2-Fabric",
- "fabricId": "1",
- "fabricMAC": "00:22:BD:F8:19:FF",
- "id": "212",
- "inbMgmtAddr": "0.0.0.0",
- "inbMgmtAddr6": "::",
- "inbMgmtAddr6Mask": "0",
- "inbMgmtAddrMask": "0",
- "inbMgmtGateway": "0.0.0.0",
- "inbMgmtGateway6": "::",
- "lastRebootTime": "2025-03-20T00:18:05.141-07:00",
- "lastResetReason": "installer",
- "lcOwn": "local",
- "modTs": "2025-03-20T00:49:25.483-07:00",
- "mode": "unspecified",
- "monPolDn": "uni/fabric/monfab-default",
- "name": "RL-212",
- "nameAlias": "",
- "nodeType": "remote-leaf-wan",
- "oobMgmtAddr": "0.0.0.0",
- "oobMgmtAddr6": "::",
- "oobMgmtAddr6Mask": "0",
- "oobMgmtAddrMask": "0",
- "oobMgmtGateway": "0.0.0.0",
- "oobMgmtGateway6": "::",
- "podId": "1",
- "remoteNetworkId": "0",
- "remoteNode": "yes",
- "rlAutoMode": "yes",
- "rlGroupId": "1",
- "rlOperPodId": "1",
- "rlRoutableMode": "yes",
- "rldirectMode": "yes",
- "role": "leaf",
- "serial": "ABC1243DEFG",
- "serverType": "unspecified",
- "siteId": "2",
- "state": "in-service",
- "status": "",
- "systemUpTime": "16:11:33:47.000",
- "tepPool": "192.168.221.0/25",
- "unicastXrEpLearnDisable": "yes",
- "version": "n9000-16.1(2.158)",
- "virtualMode": "no"
- }
- }
- },
- {
- "topSystem": {
- "attributes": {
- "address": "10.2.160.65",
- "bootstrapState": "done",
- "childAction": "",
- "clusterTimeDiff": "94487",
- "configIssues": "",
- "controlPlaneMTU": "1400",
- "currentTime": "2025-04-05T12:11:23.132-07:00",
- "dn": "topology/pod-1/node-1001/sys",
- "enforceSubnetCheck": "no",
- "etepAddr": "0.0.0.0",
- "fabricDomain": "S2-Fabric",
- "fabricId": "1",
- "fabricMAC": "00:22:BD:F8:19:FF",
- "id": "1001",
- "inbMgmtAddr": "192.168.12.201",
- "inbMgmtAddr6": "::",
- "inbMgmtAddr6Mask": "0",
- "inbMgmtAddrMask": "32",
- "inbMgmtGateway": "192.168.12.254",
- "inbMgmtGateway6": "::",
- "lastRebootTime": "2025-03-20T00:38:20.689-07:00",
- "lastResetReason": "installer",
- "lcOwn": "local",
- "modTs": "2025-03-20T00:52:16.469-07:00",
- "mode": "unspecified",
- "monPolDn": "uni/fabric/monfab-default",
- "name": "S2-Spine-1001",
- "nameAlias": "",
- "nodeType": "unspecified",
- "oobMgmtAddr": "0.0.0.0",
- "oobMgmtAddr6": "::",
- "oobMgmtAddr6Mask": "0",
- "oobMgmtAddrMask": "0",
- "oobMgmtGateway": "0.0.0.0",
- "oobMgmtGateway6": "::",
- "podId": "1",
- "remoteNetworkId": "0",
- "remoteNode": "no",
- "rlAutoMode": "no",
- "rlGroupId": "0",
- "rlOperPodId": "1",
- "rlRoutableMode": "yes",
- "rldirectMode": "yes",
- "role": "spine",
- "serial": "ABC1244DEFG",
- "serverType": "unspecified",
- "siteId": "2",
- "state": "in-service",
- "status": "",
- "systemUpTime": "16:11:33:03.000",
- "tepPool": "10.2.0.0/16",
- "unicastXrEpLearnDisable": "no",
- "version": "n9000-16.1(2.158)",
- "virtualMode": "no"
- }
- }
- },
- {
- "topSystem": {
- "attributes": {
- "address": "192.168.221.87",
- "bootstrapState": "done",
- "childAction": "",
- "clusterTimeDiff": "518949",
- "configIssues": "",
- "controlPlaneMTU": "1400",
- "currentTime": "2025-04-05T12:04:18.655-07:00",
- "dn": "topology/pod-1/node-214/sys",
- "enforceSubnetCheck": "no",
- "etepAddr": "192.168.201.1",
- "fabricDomain": "S2-Fabric",
- "fabricId": "1",
- "fabricMAC": "00:22:BD:F8:19:FF",
- "id": "214",
- "inbMgmtAddr": "0.0.0.0",
- "inbMgmtAddr6": "::",
- "inbMgmtAddr6Mask": "0",
- "inbMgmtAddrMask": "0",
- "inbMgmtGateway": "0.0.0.0",
- "inbMgmtGateway6": "::",
- "lastRebootTime": "2025-03-20T00:31:20.546-07:00",
- "lastResetReason": "installer",
- "lcOwn": "local",
- "modTs": "2025-03-20T00:52:15.931-07:00",
- "mode": "unspecified",
- "monPolDn": "uni/fabric/monfab-default",
- "name": "RL-214",
- "nameAlias": "",
- "nodeType": "remote-leaf-wan",
- "oobMgmtAddr": "0.0.0.0",
- "oobMgmtAddr6": "::",
- "oobMgmtAddr6Mask": "0",
- "oobMgmtAddrMask": "0",
- "oobMgmtGateway": "0.0.0.0",
- "oobMgmtGateway6": "::",
- "podId": "1",
- "remoteNetworkId": "0",
- "remoteNode": "yes",
- "rlAutoMode": "yes",
- "rlGroupId": "1",
- "rlOperPodId": "1",
- "rlRoutableMode": "yes",
- "rldirectMode": "yes",
- "role": "leaf",
- "serial": "ABC1245DEFG",
- "serverType": "unspecified",
- "siteId": "2",
- "state": "in-service",
- "status": "",
- "systemUpTime": "16:11:32:58.000",
- "tepPool": "192.168.221.0/25",
- "unicastXrEpLearnDisable": "yes",
- "version": "n9000-16.1(2.158)",
- "virtualMode": "no"
- }
- }
- },
- {
- "topSystem": {
- "attributes": {
- "address": "10.3.32.65",
- "bootstrapState": "done",
- "childAction": "",
- "clusterTimeDiff": "393685",
- "configIssues": "",
- "controlPlaneMTU": "1400",
- "currentTime": "2025-04-05T12:06:23.918-07:00",
- "dn": "topology/pod-2/node-201/sys",
- "enforceSubnetCheck": "no",
- "etepAddr": "192.168.202.1",
- "fabricDomain": "S2-Fabric",
- "fabricId": "1",
- "fabricMAC": "00:22:BD:F8:19:FF",
- "id": "201",
- "inbMgmtAddr": "192.168.12.211",
- "inbMgmtAddr6": "::",
- "inbMgmtAddr6Mask": "0",
- "inbMgmtAddrMask": "24",
- "inbMgmtGateway": "192.168.12.254",
- "inbMgmtGateway6": "::",
- "lastRebootTime": "2025-03-20T01:01:52.297-07:00",
- "lastResetReason": "installer",
- "lcOwn": "local",
- "modTs": "2025-03-20T01:22:03.406-07:00",
- "mode": "unspecified",
- "monPolDn": "uni/fabric/monfab-default",
- "name": "S2-Leaf-201",
- "nameAlias": "",
- "nodeType": "unspecified",
- "oobMgmtAddr": "0.0.0.0",
- "oobMgmtAddr6": "::",
- "oobMgmtAddr6Mask": "0",
- "oobMgmtAddrMask": "0",
- "oobMgmtGateway": "0.0.0.0",
- "oobMgmtGateway6": "::",
- "podId": "2",
- "remoteNetworkId": "0",
- "remoteNode": "no",
- "rlAutoMode": "no",
- "rlGroupId": "0",
- "rlOperPodId": "2",
- "rlRoutableMode": "no",
- "rldirectMode": "no",
- "role": "leaf",
- "serial": "ABC1246DEFG",
- "serverType": "unspecified",
- "siteId": "2",
- "state": "in-service",
- "status": "",
- "systemUpTime": "16:11:04:31.000",
- "tepPool": "10.3.0.0/16",
- "unicastXrEpLearnDisable": "yes",
- "version": "n9000-16.1(2.158)",
- "virtualMode": "no"
- }
- }
- },
- {
- "topSystem": {
- "attributes": {
- "address": "10.2.160.97",
- "bootstrapState": "done",
- "childAction": "",
- "clusterTimeDiff": "67158",
- "configIssues": "",
- "controlPlaneMTU": "1400",
- "currentTime": "2025-04-05T12:11:50.428-07:00",
- "dn": "topology/pod-1/node-1002/sys",
- "enforceSubnetCheck": "no",
- "etepAddr": "0.0.0.0",
- "fabricDomain": "S2-Fabric",
- "fabricId": "1",
- "fabricMAC": "00:22:BD:F8:19:FF",
- "id": "1002",
- "inbMgmtAddr": "192.168.12.202",
- "inbMgmtAddr6": "::",
- "inbMgmtAddr6Mask": "0",
- "inbMgmtAddrMask": "32",
- "inbMgmtGateway": "192.168.12.254",
- "inbMgmtGateway6": "::",
- "lastRebootTime": "2025-03-11T12:54:41.632-07:00",
- "lastResetReason": "reload",
- "lcOwn": "local",
- "modTs": "2025-03-11T13:12:33.668-07:00",
- "mode": "unspecified",
- "monPolDn": "uni/fabric/monfab-default",
- "name": "S2-Spine-1002",
- "nameAlias": "",
- "nodeType": "unspecified",
- "oobMgmtAddr": "0.0.0.0",
- "oobMgmtAddr6": "::",
- "oobMgmtAddr6Mask": "0",
- "oobMgmtAddrMask": "0",
- "oobMgmtGateway": "0.0.0.0",
- "oobMgmtGateway6": "::",
- "podId": "1",
- "remoteNetworkId": "0",
- "remoteNode": "no",
- "rlAutoMode": "no",
- "rlGroupId": "0",
- "rlOperPodId": "1",
- "rlRoutableMode": "yes",
- "rldirectMode": "yes",
- "role": "spine",
- "serial": "ABC1247DEFG",
- "serverType": "unspecified",
- "siteId": "2",
- "state": "in-service",
- "status": "",
- "systemUpTime": "24:23:17:08.000",
- "tepPool": "10.2.0.0/16",
- "unicastXrEpLearnDisable": "no",
- "version": "n9000-16.1(2.145)",
- "virtualMode": "no"
- }
- }
- },
- {
- "topSystem": {
- "attributes": {
- "address": "10.3.32.64",
- "bootstrapState": "done",
- "childAction": "",
- "clusterTimeDiff": "532782",
- "configIssues": "",
- "controlPlaneMTU": "1400",
- "currentTime": "2025-04-05T12:04:04.823-07:00",
- "dn": "topology/pod-2/node-2001/sys",
- "enforceSubnetCheck": "no",
- "etepAddr": "0.0.0.0",
- "fabricDomain": "S2-Fabric",
- "fabricId": "1",
- "fabricMAC": "00:22:BD:F8:19:FF",
- "id": "2001",
- "inbMgmtAddr": "0.0.0.0",
- "inbMgmtAddr6": "::",
- "inbMgmtAddr6Mask": "0",
- "inbMgmtAddrMask": "0",
- "inbMgmtGateway": "0.0.0.0",
- "inbMgmtGateway6": "::",
- "lastRebootTime": "2025-03-20T00:30:53.399-07:00",
- "lastResetReason": "installer",
- "lcOwn": "local",
- "modTs": "2025-03-20T00:50:33.631-07:00",
- "mode": "unspecified",
- "monPolDn": "uni/fabric/monfab-default",
- "name": "S2-Spine-2001",
- "nameAlias": "",
- "nodeType": "unspecified",
- "oobMgmtAddr": "0.0.0.0",
- "oobMgmtAddr6": "::",
- "oobMgmtAddr6Mask": "0",
- "oobMgmtAddrMask": "0",
- "oobMgmtGateway": "0.0.0.0",
- "oobMgmtGateway6": "::",
- "podId": "2",
- "remoteNetworkId": "0",
- "remoteNode": "no",
- "rlAutoMode": "no",
- "rlGroupId": "0",
- "rlOperPodId": "2",
- "rlRoutableMode": "yes",
- "rldirectMode": "yes",
- "role": "spine",
- "serial": "ABC1248DEFG",
- "serverType": "unspecified",
- "siteId": "2",
- "state": "in-service",
- "status": "",
- "systemUpTime": "16:11:33:11.000",
- "tepPool": "10.3.0.0/16",
- "unicastXrEpLearnDisable": "no",
- "version": "n9000-16.1(2.158)",
- "virtualMode": "no"
- }
- }
- },
- {
- "topSystem": {
- "attributes": {
- "address": "10.2.160.98",
- "bootstrapState": "done",
- "childAction": "",
- "clusterTimeDiff": "179134",
- "configIssues": "",
- "controlPlaneMTU": "1400",
- "currentTime": "2025-04-05T12:09:58.511-07:00",
- "dn": "topology/pod-1/node-102/sys",
- "enforceSubnetCheck": "no",
- "etepAddr": "192.168.201.1",
- "fabricDomain": "S2-Fabric",
- "fabricId": "1",
- "fabricMAC": "00:22:BD:F8:19:FF",
- "id": "102",
- "inbMgmtAddr": "192.168.12.102",
- "inbMgmtAddr6": "::",
- "inbMgmtAddr6Mask": "0",
- "inbMgmtAddrMask": "24",
- "inbMgmtGateway": "192.168.12.254",
- "inbMgmtGateway6": "::",
- "lastRebootTime": "2025-03-20T00:36:18.789-07:00",
- "lastResetReason": "installer",
- "lcOwn": "local",
- "modTs": "2025-03-20T00:51:25.528-07:00",
- "mode": "unspecified",
- "monPolDn": "uni/fabric/monfab-default",
- "name": "S2-Leaf-102",
- "nameAlias": "",
- "nodeType": "unspecified",
- "oobMgmtAddr": "0.0.0.0",
- "oobMgmtAddr6": "::",
- "oobMgmtAddr6Mask": "0",
- "oobMgmtAddrMask": "0",
- "oobMgmtGateway": "0.0.0.0",
- "oobMgmtGateway6": "::",
- "podId": "1",
- "remoteNetworkId": "0",
- "remoteNode": "no",
- "rlAutoMode": "no",
- "rlGroupId": "0",
- "rlOperPodId": "1",
- "rlRoutableMode": "no",
- "rldirectMode": "no",
- "role": "leaf",
- "serial": "ABC1249DEFG",
- "serverType": "unspecified",
- "siteId": "2",
- "state": "in-service",
- "status": "",
- "systemUpTime": "16:11:33:40.000",
- "tepPool": "10.2.0.0/16",
- "unicastXrEpLearnDisable": "yes",
- "version": "n9000-16.1(2.158)",
- "virtualMode": "no"
- }
- }
- },
- {
- "topSystem": {
- "attributes": {
- "address": "192.168.221.91",
- "bootstrapState": "done",
- "childAction": "",
- "clusterTimeDiff": "-529288",
- "configIssues": "",
- "controlPlaneMTU": "1400",
- "currentTime": "2025-04-05T12:21:46.934-07:00",
- "dn": "topology/pod-1/node-211/sys",
- "enforceSubnetCheck": "no",
- "etepAddr": "192.168.201.1",
- "fabricDomain": "S2-Fabric",
- "fabricId": "1",
- "fabricMAC": "00:22:BD:F8:19:FF",
- "id": "211",
- "inbMgmtAddr": "0.0.0.0",
- "inbMgmtAddr6": "::",
- "inbMgmtAddr6Mask": "0",
- "inbMgmtAddrMask": "0",
- "inbMgmtGateway": "0.0.0.0",
- "inbMgmtGateway6": "::",
- "lastRebootTime": "2025-03-20T00:47:47.935-07:00",
- "lastResetReason": "installer",
- "lcOwn": "local",
- "modTs": "2025-03-20T00:52:13.287-07:00",
- "mode": "unspecified",
- "monPolDn": "uni/fabric/monfab-default",
- "name": "RL-211",
- "nameAlias": "",
- "nodeType": "remote-leaf-wan",
- "oobMgmtAddr": "0.0.0.0",
- "oobMgmtAddr6": "::",
- "oobMgmtAddr6Mask": "0",
- "oobMgmtAddrMask": "0",
- "oobMgmtGateway": "0.0.0.0",
- "oobMgmtGateway6": "::",
- "podId": "1",
- "remoteNetworkId": "0",
- "remoteNode": "yes",
- "rlAutoMode": "yes",
- "rlGroupId": "1",
- "rlOperPodId": "1",
- "rlRoutableMode": "yes",
- "rldirectMode": "yes",
- "role": "leaf",
- "serial": "ABC1250DEFG",
- "serverType": "unspecified",
- "siteId": "2",
- "state": "in-service",
- "status": "",
- "systemUpTime": "16:11:33:59.000",
- "tepPool": "192.168.221.0/25",
- "unicastXrEpLearnDisable": "yes",
- "version": "n9000-16.1(2.158)",
- "virtualMode": "no"
- }
- }
- }
- ]
-}
diff --git a/tests/access_untagged_check/faultInst_NEG.json b/tests/checks/access_untagged_check/faultInst_NEG.json
similarity index 100%
rename from tests/access_untagged_check/faultInst_NEG.json
rename to tests/checks/access_untagged_check/faultInst_NEG.json
diff --git a/tests/access_untagged_check/faultInst_POS.json b/tests/checks/access_untagged_check/faultInst_POS.json
similarity index 100%
rename from tests/access_untagged_check/faultInst_POS.json
rename to tests/checks/access_untagged_check/faultInst_POS.json
diff --git a/tests/access_untagged_check/test_access_untagged_check.py b/tests/checks/access_untagged_check/test_access_untagged_check.py
similarity index 80%
rename from tests/access_untagged_check/test_access_untagged_check.py
rename to tests/checks/access_untagged_check/test_access_untagged_check.py
index 21851c71..f7a6d610 100644
--- a/tests/access_untagged_check/test_access_untagged_check.py
+++ b/tests/checks/access_untagged_check/test_access_untagged_check.py
@@ -9,6 +9,7 @@
log = logging.getLogger(__name__)
dir = os.path.dirname(os.path.abspath(__file__))
+test_function = "access_untagged_check"
# icurl queries
faultInsts = 'faultInst.json?&query-target-filter=wcard(faultInst.changeSet,"native-or-untagged-encap-failure")'
@@ -27,6 +28,6 @@
)
],
)
-def test_logic(mock_icurl,expected_result):
- result = script.access_untagged_check(1, 1)
- assert result == expected_result
+def test_logic(run_check, mock_icurl, expected_result):
+ result = run_check()
+ assert result.result == expected_result
diff --git a/tests/aes_encryption_check/exportcryptkey.json b/tests/checks/aes_encryption_check/exportcryptkey.json
similarity index 100%
rename from tests/aes_encryption_check/exportcryptkey.json
rename to tests/checks/aes_encryption_check/exportcryptkey.json
diff --git a/tests/aes_encryption_check/exportcryptkey_disabled.json b/tests/checks/aes_encryption_check/exportcryptkey_disabled.json
similarity index 100%
rename from tests/aes_encryption_check/exportcryptkey_disabled.json
rename to tests/checks/aes_encryption_check/exportcryptkey_disabled.json
diff --git a/tests/aes_encryption_check/test_aes_encryption_check.py b/tests/checks/aes_encryption_check/test_aes_encryption_check.py
similarity index 87%
rename from tests/aes_encryption_check/test_aes_encryption_check.py
rename to tests/checks/aes_encryption_check/test_aes_encryption_check.py
index acd6e064..440a8603 100644
--- a/tests/aes_encryption_check/test_aes_encryption_check.py
+++ b/tests/checks/aes_encryption_check/test_aes_encryption_check.py
@@ -9,6 +9,8 @@
log = logging.getLogger(__name__)
dir = os.path.dirname(os.path.abspath(__file__))
+test_function = "aes_encryption_check"
+
# icurl queries
exportcryptkey = "uni/exportcryptkey.json"
@@ -55,6 +57,6 @@
),
],
)
-def test_logic(mock_icurl, tversion, expected_result):
- result = script.aes_encryption_check(1, 1, script.AciVersion(tversion))
- assert result == expected_result
+def test_logic(run_check, mock_icurl, tversion, expected_result):
+ result = run_check(tversion=script.AciVersion(tversion))
+ assert result.result == expected_result
diff --git a/tests/apic_ca_cert_validation/NEG_certreq.txt b/tests/checks/apic_ca_cert_validation/NEG_certreq.txt
similarity index 100%
rename from tests/apic_ca_cert_validation/NEG_certreq.txt
rename to tests/checks/apic_ca_cert_validation/NEG_certreq.txt
diff --git a/tests/apic_ca_cert_validation/POS_certreq.txt b/tests/checks/apic_ca_cert_validation/POS_certreq.txt
similarity index 100%
rename from tests/apic_ca_cert_validation/POS_certreq.txt
rename to tests/checks/apic_ca_cert_validation/POS_certreq.txt
diff --git a/tests/apic_ca_cert_validation/test_apic_ca_cert_validation.py b/tests/checks/apic_ca_cert_validation/test_apic_ca_cert_validation.py
similarity index 69%
rename from tests/apic_ca_cert_validation/test_apic_ca_cert_validation.py
rename to tests/checks/apic_ca_cert_validation/test_apic_ca_cert_validation.py
index d76d9555..338a66c4 100644
--- a/tests/apic_ca_cert_validation/test_apic_ca_cert_validation.py
+++ b/tests/checks/apic_ca_cert_validation/test_apic_ca_cert_validation.py
@@ -8,6 +8,8 @@
log = logging.getLogger(__name__)
dir = os.path.dirname(os.path.abspath(__file__))
+test_function = "apic_ca_cert_validation"
+
@pytest.mark.parametrize(
"certreq_out_file, expected_result",
@@ -24,9 +26,9 @@
),
],
)
-def test_logic(certreq_out_file, expected_result):
- data_path = os.path.join("tests", dir, certreq_out_file)
+def test_logic(run_check, certreq_out_file, expected_result):
+ data_path = os.path.join("tests", "checks", dir, certreq_out_file)
with open(data_path, "r") as file:
certreq_out = file.read()
- result = script.apic_ca_cert_validation(1, 1, certreq_out=certreq_out)
- assert result == expected_result
+ result = run_check(certreq_out=certreq_out)
+ assert result.result == expected_result
diff --git a/tests/apic_database_size_check/infraWiNode_3.json b/tests/checks/apic_database_size_check/infraWiNode_3.json
similarity index 100%
rename from tests/apic_database_size_check/infraWiNode_3.json
rename to tests/checks/apic_database_size_check/infraWiNode_3.json
diff --git a/tests/apic_database_size_check/infraWiNode_4.json b/tests/checks/apic_database_size_check/infraWiNode_4.json
similarity index 100%
rename from tests/apic_database_size_check/infraWiNode_4.json
rename to tests/checks/apic_database_size_check/infraWiNode_4.json
diff --git a/tests/apic_database_size_check/test_apic_database_size_check.py b/tests/checks/apic_database_size_check/test_apic_database_size_check.py
similarity index 96%
rename from tests/apic_database_size_check/test_apic_database_size_check.py
rename to tests/checks/apic_database_size_check/test_apic_database_size_check.py
index 73eb62ca..8a651bc7 100644
--- a/tests/apic_database_size_check/test_apic_database_size_check.py
+++ b/tests/checks/apic_database_size_check/test_apic_database_size_check.py
@@ -2,7 +2,6 @@
import pytest
import logging
import importlib
-import json
from helpers.utils import read_data
script = importlib.import_module("aci-preupgrade-validation-script")
@@ -10,6 +9,8 @@
log = logging.getLogger(__name__)
dir = os.path.dirname(os.path.abspath(__file__))
+test_function = "apic_database_size_check"
+
apic_node_api = 'infraWiNode.json'
apic1_pm_cat = "cat /debug/apic1/policymgr/mitmocounters/mo | grep -v ALL | sort -rn -k3"
@@ -166,6 +167,7 @@
]
}"""
+
@pytest.mark.parametrize(
"icurl_outputs, cmd_outputs, cversion, expected_result",
[
@@ -292,10 +294,11 @@
),
],
)
-def test_logic(mock_icurl, mock_run_cmd, cversion, expected_result):
- cver = script.AciVersion(cversion) if cversion else None
- result = script.apic_database_size_check(1, 1, cver)
- assert result == expected_result
+def test_logic(run_check, mock_icurl, mock_run_cmd, cversion, expected_result):
+ result = run_check(
+ cversion=script.AciVersion(cversion) if cversion else None
+ )
+ assert result.result == expected_result
@pytest.mark.parametrize(
@@ -326,7 +329,8 @@ def test_logic(mock_icurl, mock_run_cmd, cversion, expected_result):
),
],
)
-def test_permission_logic(mock_icurl, mock_run_cmd, cversion, expected_result):
- cver = script.AciVersion(cversion) if cversion else None
- result = script.apic_database_size_check(1, 1, cver)
- assert result == expected_result
+def test_permission_logic(run_check, mock_icurl, mock_run_cmd, cversion, expected_result):
+ result = run_check(
+ cversion=script.AciVersion(cversion) if cversion else None
+ )
+ assert result.result == expected_result
diff --git a/tests/checks/apic_ssd_check/fabricNode.json b/tests/checks/apic_ssd_check/fabricNode.json
new file mode 100644
index 00000000..962a4ad9
--- /dev/null
+++ b/tests/checks/apic_ssd_check/fabricNode.json
@@ -0,0 +1,93 @@
+[
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.1",
+ "dn": "topology/pod-1/node-1",
+ "fabricSt": "commissioned",
+ "id": "1",
+ "model": "APIC-SERVER-L2",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "apic1",
+ "nodeType": "unspecified",
+ "role": "controller"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.2",
+ "dn": "topology/pod-1/node-2",
+ "fabricSt": "commissioned",
+ "id": "2",
+ "model": "APIC-SERVER-L2",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "apic2",
+ "nodeType": "unspecified",
+ "role": "controller"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.3",
+ "dn": "topology/pod-2/node-3",
+ "fabricSt": "commissioned",
+ "id": "3",
+ "model": "APIC-SERVER-L2",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "apic3",
+ "nodeType": "unspecified",
+ "role": "controller"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.101",
+ "dn": "topology/pod-1/node-101",
+ "fabricSt": "active",
+ "id": "101",
+ "model": "N9K-C93180YC-FX",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "leaf101",
+ "nodeType": "unspecified",
+ "role": "leaf"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.102",
+ "dn": "topology/pod-1/node-102",
+ "fabricSt": "active",
+ "id": "102",
+ "model": "N9K-C93180YC-FX",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "leaf102",
+ "nodeType": "unspecified",
+ "role": "leaf"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.201",
+ "dn": "topology/pod-1/node-201",
+ "fabricSt": "active",
+ "id": "201",
+ "model": "N9K-C9504",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "spine201",
+ "nodeType": "unspecified",
+ "role": "spine"
+ }
+ }
+ }
+]
+
diff --git a/tests/checks/apic_ssd_check/fabricNode_no_apic.json b/tests/checks/apic_ssd_check/fabricNode_no_apic.json
new file mode 100644
index 00000000..254f40d0
--- /dev/null
+++ b/tests/checks/apic_ssd_check/fabricNode_no_apic.json
@@ -0,0 +1,48 @@
+[
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.101",
+ "dn": "topology/pod-1/node-101",
+ "fabricSt": "active",
+ "id": "101",
+ "model": "N9K-C93180YC-FX",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "leaf101",
+ "nodeType": "unspecified",
+ "role": "leaf"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.102",
+ "dn": "topology/pod-1/node-102",
+ "fabricSt": "active",
+ "id": "102",
+ "model": "N9K-C93180YC-FX",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "leaf102",
+ "nodeType": "unspecified",
+ "role": "leaf"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.201",
+ "dn": "topology/pod-1/node-201",
+ "fabricSt": "active",
+ "id": "201",
+ "model": "N9K-C9504",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "spine201",
+ "nodeType": "unspecified",
+ "role": "spine"
+ }
+ }
+ }
+]
+
diff --git a/tests/checks/apic_ssd_check/fabricNode_old.json b/tests/checks/apic_ssd_check/fabricNode_old.json
new file mode 100644
index 00000000..f71fb9fc
--- /dev/null
+++ b/tests/checks/apic_ssd_check/fabricNode_old.json
@@ -0,0 +1,62 @@
+[
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-1/node-1",
+ "fabricSt": "unknown",
+ "nodeType": "unspecified",
+ "id": "1",
+ "version": "A",
+ "role": "controller",
+ "adSt": "on",
+ "name": "apic1",
+ "model": "APIC-SERVER-M1"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-1/node-2",
+ "fabricSt": "unknown",
+ "nodeType": "unspecified",
+ "id": "2",
+ "version": "A",
+ "role": "controller",
+ "adSt": "on",
+ "name": "apic2",
+ "model": "APIC-SERVER-M1"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-2/node-3",
+ "fabricSt": "unknown",
+ "nodeType": "unspecified",
+ "id": "3",
+ "version": "A",
+ "role": "controller",
+ "adSt": "on",
+ "name": "apic3",
+ "model": "APIC-SERVER-M1"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-1/node-101",
+ "fabricSt": "active",
+ "nodeType": "unspecified",
+ "id": "101",
+ "version": "",
+ "role": "leaf",
+ "adSt": "on",
+ "name": "leaf1",
+ "model": "N9K-C9396PX"
+ }
+ }
+ }
+]
diff --git a/tests/checks/apic_ssd_check/fault_F2731.json b/tests/checks/apic_ssd_check/fault_F2731.json
new file mode 100644
index 00000000..578f6827
--- /dev/null
+++ b/tests/checks/apic_ssd_check/fault_F2731.json
@@ -0,0 +1,12 @@
+[
+ {
+ "faultInst": {
+ "attributes": {
+ "code": "F2731",
+ "changeSet": "mediaWearout (Old: 2, New: 1)",
+ "description": "Storage unit /dev/sdb on Node 3 mounted at /dev/sdb has 1% life remaining",
+ "dn": "topology/pod-2/node-3/sys/ch/p-[/dev/sdb]-f-[/dev/sdb]/fault-F2731"
+ }
+ }
+ }
+]
diff --git a/tests/checks/apic_ssd_check/infraWiNode_apic1.json b/tests/checks/apic_ssd_check/infraWiNode_apic1.json
new file mode 100644
index 00000000..b6626d02
--- /dev/null
+++ b/tests/checks/apic_ssd_check/infraWiNode_apic1.json
@@ -0,0 +1,62 @@
+[
+ {
+ "infraWiNode": {
+ "attributes": {
+ "addr": "10.0.0.1",
+ "adminSt": "in-service",
+ "apicMode": "active",
+ "cntrlSbstState": "approved",
+ "dn": "topology/pod-1/node-1/av/node-1",
+ "failoverStatus": "idle",
+ "health": "fully-fit",
+ "id": "1",
+ "mbSn": "FCH1234ABCD",
+ "name": "",
+ "nodeName": "apic1",
+ "operSt": "available",
+ "podId": "0",
+ "targetMbSn": ""
+ }
+ }
+ },
+ {
+ "infraWiNode": {
+ "attributes": {
+ "addr": "10.0.0.2",
+ "adminSt": "in-service",
+ "apicMode": "active",
+ "cntrlSbstState": "approved",
+ "dn": "topology/pod-1/node-1/av/node-2",
+ "failoverStatus": "idle",
+ "health": "fully-fit",
+ "id": "2",
+ "mbSn": "FCH1235ABCD",
+ "name": "",
+ "nodeName": "apic2",
+ "operSt": "available",
+ "podId": "0",
+ "targetMbSn": ""
+ }
+ }
+ },
+ {
+ "infraWiNode": {
+ "attributes": {
+ "addr": "10.0.0.3",
+ "adminSt": "in-service",
+ "apicMode": "active",
+ "cntrlSbstState": "approved",
+ "dn": "topology/pod-1/node-1/av/node-3",
+ "failoverStatus": "idle",
+ "health": "fully-fit",
+ "id": "3",
+ "mbSn": "FCH1236ABCD",
+ "name": "",
+ "nodeName": "apic3",
+ "operSt": "available",
+ "podId": "1",
+ "targetMbSn": ""
+ }
+ }
+ }
+]
diff --git a/tests/checks/apic_ssd_check/test_apic_ssd_check.py b/tests/checks/apic_ssd_check/test_apic_ssd_check.py
new file mode 100644
index 00000000..b0cc678f
--- /dev/null
+++ b/tests/checks/apic_ssd_check/test_apic_ssd_check.py
@@ -0,0 +1,214 @@
+import os
+import pytest
+import logging
+import importlib
+from helpers.utils import read_data
+
+script = importlib.import_module("aci-preupgrade-validation-script")
+
+log = logging.getLogger(__name__)
+dir = os.path.dirname(os.path.abspath(__file__))
+
+test_function = "apic_ssd_check"
+
+
+faultInst = 'faultInst.json?query-target-filter=or(eq(faultInst.code,"F2731"),eq(faultInst.code,"F2732"))'
+infraWiNode = "topology/pod-1/node-1/infraWiNode.json"
+
+apic_ips = [
+ node["fabricNode"]["attributes"]["address"]
+ for node in read_data(dir, "fabricNode.json")
+ if node["fabricNode"]["attributes"]["role"] == "controller"
+]
+
+grep_cmd = 'grep -oE "SSD Wearout Indicator is [0-9]+" /var/log/dme/log/svc_ifc_ae.bin.log | tail -1'
+grep_output_hit = "5504||2023-01-11T22:11:26.851446656+00:00||ifc_ae||DBG4||fn=[getWearout]||SSD Wearout Indicator is 4||../svc/ae/src/gen/ifc/beh/imp/./eqpt/StorageBI.cc||395"
+grep_output_no_hit = "5504||2023-01-11T22:11:26.851446656+00:00||ifc_ae||DBG4||fn=[getWearout]||SSD Wearout Indicator is 5||../svc/ae/src/gen/ifc/beh/imp/./eqpt/StorageBI.cc||395"
+
+
+@pytest.mark.parametrize(
+ "icurl_outputs, conn_failure, conn_cmds, cversion, fabric_nodes, expected_result, expected_data",
+ [
+ # New Versions, F273x are effective and raised
+ (
+ {faultInst: read_data(dir, "fault_F2731.json")},
+ False,
+ [],
+ "4.2(7w)",
+ read_data(dir, "fabricNode.json"),
+ script.FAIL_UF,
+ [["3", "apic3", "/dev/sdb", "<5% (Fault F2731)", "Contact TAC for replacement"]],
+ ),
+ (
+ {faultInst: read_data(dir, "fault_F2731.json")},
+ False,
+ [],
+ "5.2(1h)",
+ read_data(dir, "fabricNode.json"),
+ script.FAIL_UF,
+ [["3", "apic3", "/dev/sdb", "<5% (Fault F2731)", "Contact TAC for replacement"]],
+ ),
+ # New Versions, F273x are effective and NOT raised
+ (
+ {faultInst: []},
+ False,
+ [],
+ "4.2(7w)",
+ read_data(dir, "fabricNode.json"),
+ script.PASS,
+ [],
+ ),
+ (
+ {faultInst: []},
+ False,
+ [],
+ "5.2(1h)",
+ read_data(dir, "fabricNode.json"),
+ script.PASS,
+ [],
+ ),
+ # Old Versions, but F273x was still raised.
+ (
+ {faultInst: read_data(dir, "fault_F2731.json")},
+ False,
+ [],
+ "4.2(6o)",
+ read_data(dir, "fabricNode.json"),
+ script.FAIL_UF,
+ [["3", "apic3", "/dev/sdb", "<5% (Fault F2731)", "Contact TAC for replacement"]],
+ ),
+
+ # --- Old Versions, no F273x was raised. ---
+
+ # No fabricNode for APICs
+ (
+ {faultInst: []},
+ False,
+ [],
+ "4.2(6o)",
+ read_data(dir, "fabricNode_no_apic.json"),
+ script.ERROR,
+ [],
+ ),
+ # Exception failure at the very first connection()
+ (
+ {faultInst: []},
+ True,
+ [],
+ "4.2(6o)",
+ read_data(dir, "fabricNode.json"),
+ script.ERROR,
+ [
+ ["1", "apic1", "-", "-", "Simulated exception at connect()"],
+ ["2", "apic2", "-", "-", "Simulated exception at connect()"],
+ ["3", "apic3", "-", "-", "Simulated exception at connect()"],
+ ],
+ ),
+ # Exception failure at the grep command
+ (
+ {faultInst: []},
+ False,
+ {
+ apic_ip: [
+ {
+ "cmd": grep_cmd,
+ "output": "",
+ "exception": Exception("Simulated exception at `grep` command"),
+ }
+ ]
+ for apic_ip in apic_ips
+ },
+ "4.2(6o)",
+ read_data(dir, "fabricNode.json"),
+ script.ERROR,
+ [
+ ["1", "apic1", "-", "-", "Simulated exception at `grep` command"],
+ ["2", "apic2", "-", "-", "Simulated exception at `grep` command"],
+ ["3", "apic3", "-", "-", "Simulated exception at `grep` command"],
+ ],
+ ),
+ # SSD Wearout Indicator is less than 5
+ (
+ {faultInst: []},
+ False,
+ {
+ apic_ips[0]: [
+ {
+ "cmd": grep_cmd,
+ "output": "\n".join([grep_cmd, grep_output_hit]),
+ "exception": None,
+ },
+ ],
+ apic_ips[1]: [
+ {
+ "cmd": grep_cmd,
+ "output": "\n".join([grep_cmd, grep_output_no_hit]),
+ "exception": None,
+ },
+ ],
+ apic_ips[2]: [
+ {
+ "cmd": grep_cmd,
+ "output": "\n".join([grep_cmd, grep_output_hit]),
+ "exception": None,
+ },
+ ],
+ },
+ "4.2(6o)",
+ read_data(dir, "fabricNode.json"),
+ script.FAIL_UF,
+ [
+ ["1", "apic1", "Solid State Disk", "4", "Contact TAC for replacement"],
+ ["2", "apic2", "Solid State Disk", "5", "No Action Required"],
+ ["3", "apic3", "Solid State Disk", "4", "Contact TAC for replacement"],
+ ],
+ ),
+ # Pass
+ (
+ {faultInst: []},
+ False,
+ {
+ apic_ip: [
+ {
+ "cmd": grep_cmd,
+ "output": "\n".join([grep_cmd, grep_output_no_hit]),
+ "exception": None,
+ },
+ ]
+ for apic_ip in apic_ips
+ },
+ "4.2(6o)",
+ read_data(dir, "fabricNode.json"),
+ script.PASS,
+ [],
+ ),
+ # Pass (pre-4.0 with infraWiNode)
+ (
+ {faultInst: [], infraWiNode: read_data(dir, "infraWiNode_apic1.json")},
+ False,
+ {
+ apic_ip: [
+ {
+ "cmd": grep_cmd,
+ "output": "\n".join([grep_cmd, grep_output_no_hit]),
+ "exception": None,
+ },
+ ]
+ for apic_ip in apic_ips
+ },
+ "4.2(6o)",
+ read_data(dir, "fabricNode_old.json"),
+ script.PASS,
+ [],
+ ),
+ ],
+)
+def test_logic(run_check, mock_icurl, mock_conn, cversion, fabric_nodes, expected_result, expected_data):
+ result = run_check(
+ cversion=script.AciVersion(cversion),
+ username="fake_username",
+ password="fake_password",
+ fabric_nodes=fabric_nodes,
+ )
+ assert result.result == expected_result
+ assert result.data == expected_data
diff --git a/tests/checks/apic_version_md5_check/fabricNode.json b/tests/checks/apic_version_md5_check/fabricNode.json
new file mode 100644
index 00000000..962a4ad9
--- /dev/null
+++ b/tests/checks/apic_version_md5_check/fabricNode.json
@@ -0,0 +1,93 @@
+[
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.1",
+ "dn": "topology/pod-1/node-1",
+ "fabricSt": "commissioned",
+ "id": "1",
+ "model": "APIC-SERVER-L2",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "apic1",
+ "nodeType": "unspecified",
+ "role": "controller"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.2",
+ "dn": "topology/pod-1/node-2",
+ "fabricSt": "commissioned",
+ "id": "2",
+ "model": "APIC-SERVER-L2",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "apic2",
+ "nodeType": "unspecified",
+ "role": "controller"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.3",
+ "dn": "topology/pod-2/node-3",
+ "fabricSt": "commissioned",
+ "id": "3",
+ "model": "APIC-SERVER-L2",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "apic3",
+ "nodeType": "unspecified",
+ "role": "controller"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.101",
+ "dn": "topology/pod-1/node-101",
+ "fabricSt": "active",
+ "id": "101",
+ "model": "N9K-C93180YC-FX",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "leaf101",
+ "nodeType": "unspecified",
+ "role": "leaf"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.102",
+ "dn": "topology/pod-1/node-102",
+ "fabricSt": "active",
+ "id": "102",
+ "model": "N9K-C93180YC-FX",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "leaf102",
+ "nodeType": "unspecified",
+ "role": "leaf"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.201",
+ "dn": "topology/pod-1/node-201",
+ "fabricSt": "active",
+ "id": "201",
+ "model": "N9K-C9504",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "spine201",
+ "nodeType": "unspecified",
+ "role": "spine"
+ }
+ }
+ }
+]
+
diff --git a/tests/checks/apic_version_md5_check/fabricNode_no_apic.json b/tests/checks/apic_version_md5_check/fabricNode_no_apic.json
new file mode 100644
index 00000000..254f40d0
--- /dev/null
+++ b/tests/checks/apic_version_md5_check/fabricNode_no_apic.json
@@ -0,0 +1,48 @@
+[
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.101",
+ "dn": "topology/pod-1/node-101",
+ "fabricSt": "active",
+ "id": "101",
+ "model": "N9K-C93180YC-FX",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "leaf101",
+ "nodeType": "unspecified",
+ "role": "leaf"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.102",
+ "dn": "topology/pod-1/node-102",
+ "fabricSt": "active",
+ "id": "102",
+ "model": "N9K-C93180YC-FX",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "leaf102",
+ "nodeType": "unspecified",
+ "role": "leaf"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.201",
+ "dn": "topology/pod-1/node-201",
+ "fabricSt": "active",
+ "id": "201",
+ "model": "N9K-C9504",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "spine201",
+ "nodeType": "unspecified",
+ "role": "spine"
+ }
+ }
+ }
+]
+
diff --git a/tests/checks/apic_version_md5_check/fabricNode_old.json b/tests/checks/apic_version_md5_check/fabricNode_old.json
new file mode 100644
index 00000000..f71fb9fc
--- /dev/null
+++ b/tests/checks/apic_version_md5_check/fabricNode_old.json
@@ -0,0 +1,62 @@
+[
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-1/node-1",
+ "fabricSt": "unknown",
+ "nodeType": "unspecified",
+ "id": "1",
+ "version": "A",
+ "role": "controller",
+ "adSt": "on",
+ "name": "apic1",
+ "model": "APIC-SERVER-M1"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-1/node-2",
+ "fabricSt": "unknown",
+ "nodeType": "unspecified",
+ "id": "2",
+ "version": "A",
+ "role": "controller",
+ "adSt": "on",
+ "name": "apic2",
+ "model": "APIC-SERVER-M1"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-2/node-3",
+ "fabricSt": "unknown",
+ "nodeType": "unspecified",
+ "id": "3",
+ "version": "A",
+ "role": "controller",
+ "adSt": "on",
+ "name": "apic3",
+ "model": "APIC-SERVER-M1"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-1/node-101",
+ "fabricSt": "active",
+ "nodeType": "unspecified",
+ "id": "101",
+ "version": "",
+ "role": "leaf",
+ "adSt": "on",
+ "name": "leaf1",
+ "model": "N9K-C9396PX"
+ }
+ }
+ }
+]
diff --git a/tests/apic_version_md5_check/firmwareFirmware_6.0.5h.json b/tests/checks/apic_version_md5_check/firmwareFirmware_6.0.5h.json
similarity index 100%
rename from tests/apic_version_md5_check/firmwareFirmware_6.0.5h.json
rename to tests/checks/apic_version_md5_check/firmwareFirmware_6.0.5h.json
diff --git a/tests/apic_version_md5_check/firmwareFirmware_6.0.5h_image_sign_fail.json b/tests/checks/apic_version_md5_check/firmwareFirmware_6.0.5h_image_sign_fail.json
similarity index 100%
rename from tests/apic_version_md5_check/firmwareFirmware_6.0.5h_image_sign_fail.json
rename to tests/checks/apic_version_md5_check/firmwareFirmware_6.0.5h_image_sign_fail.json
diff --git a/tests/checks/apic_version_md5_check/infraWiNode_apic1.json b/tests/checks/apic_version_md5_check/infraWiNode_apic1.json
new file mode 100644
index 00000000..b6626d02
--- /dev/null
+++ b/tests/checks/apic_version_md5_check/infraWiNode_apic1.json
@@ -0,0 +1,62 @@
+[
+ {
+ "infraWiNode": {
+ "attributes": {
+ "addr": "10.0.0.1",
+ "adminSt": "in-service",
+ "apicMode": "active",
+ "cntrlSbstState": "approved",
+ "dn": "topology/pod-1/node-1/av/node-1",
+ "failoverStatus": "idle",
+ "health": "fully-fit",
+ "id": "1",
+ "mbSn": "FCH1234ABCD",
+ "name": "",
+ "nodeName": "apic1",
+ "operSt": "available",
+ "podId": "0",
+ "targetMbSn": ""
+ }
+ }
+ },
+ {
+ "infraWiNode": {
+ "attributes": {
+ "addr": "10.0.0.2",
+ "adminSt": "in-service",
+ "apicMode": "active",
+ "cntrlSbstState": "approved",
+ "dn": "topology/pod-1/node-1/av/node-2",
+ "failoverStatus": "idle",
+ "health": "fully-fit",
+ "id": "2",
+ "mbSn": "FCH1235ABCD",
+ "name": "",
+ "nodeName": "apic2",
+ "operSt": "available",
+ "podId": "0",
+ "targetMbSn": ""
+ }
+ }
+ },
+ {
+ "infraWiNode": {
+ "attributes": {
+ "addr": "10.0.0.3",
+ "adminSt": "in-service",
+ "apicMode": "active",
+ "cntrlSbstState": "approved",
+ "dn": "topology/pod-1/node-1/av/node-3",
+ "failoverStatus": "idle",
+ "health": "fully-fit",
+ "id": "3",
+ "mbSn": "FCH1236ABCD",
+ "name": "",
+ "nodeName": "apic3",
+ "operSt": "available",
+ "podId": "1",
+ "targetMbSn": ""
+ }
+ }
+ }
+]
diff --git a/tests/apic_version_md5_check/test_apic_version_md5_check.py b/tests/checks/apic_version_md5_check/test_apic_version_md5_check.py
similarity index 56%
rename from tests/apic_version_md5_check/test_apic_version_md5_check.py
rename to tests/checks/apic_version_md5_check/test_apic_version_md5_check.py
index 19ea2630..32605ad7 100644
--- a/tests/apic_version_md5_check/test_apic_version_md5_check.py
+++ b/tests/checks/apic_version_md5_check/test_apic_version_md5_check.py
@@ -9,14 +9,16 @@
log = logging.getLogger(__name__)
dir = os.path.dirname(os.path.abspath(__file__))
+test_function = "apic_version_md5_check"
+
+
api_firmware = "fwrepo/fw-aci-apic-dk9.6.0.5h.json"
-api_topSystem = "topSystem.json"
+api_infraWiNode = "topology/pod-1/node-1/infraWiNode.json"
-topSystems = read_data(dir, "topSystem.json")
apic_ips = [
- mo["topSystem"]["attributes"]["address"]
- for mo in topSystems["imdata"]
- if mo["topSystem"]["attributes"]["role"] == "controller"
+ node["fabricNode"]["attributes"]["address"]
+ for node in read_data(dir, "fabricNode.json")
+ if node["fabricNode"]["attributes"]["role"] == "controller"
]
ls_cmd = "ls -aslh /firmware/fwrepos/fwrepo/aci-apic-dk9.6.0.5h.bin"
@@ -49,38 +51,47 @@
@pytest.mark.parametrize(
- "icurl_outputs, conn_failure, conn_cmds, tversion, expected_result",
+ "icurl_outputs, conn_failure, conn_cmds, tversion, fabric_nodes, expected_result, expected_data",
[
# tversion missing
- ({}, False, [], None, script.MANUAL),
+ ({}, False, [], None, read_data(dir, "fabricNode.json"), script.MANUAL, []),
# Image signing failure shown in firmwareFirmware
(
- {
- api_firmware: read_data(dir, "firmwareFirmware_6.0.5h_image_sign_fail.json"),
- api_topSystem: read_data(dir, "topSystem.json"),
- },
+ {api_firmware: read_data(dir, "firmwareFirmware_6.0.5h_image_sign_fail.json")},
False,
[],
"6.0(5h)",
+ read_data(dir, "fabricNode.json"),
script.FAIL_UF,
+ [["All", "-", "6.0(5h)", "d5afca58fce2018495d068c000000000", "Target image is corrupted"]],
+ ),
+ # No fabricNode for APICs
+ (
+ {api_firmware: read_data(dir, "firmwareFirmware_6.0.5h.json")},
+ False,
+ [],
+ "6.0(5h)",
+ read_data(dir, "fabricNode_no_apic.json"),
+ script.ERROR,
+ [],
),
# Exception failure at the very first connection()
(
- {
- api_firmware: read_data(dir, "firmwareFirmware_6.0.5h.json"),
- api_topSystem: read_data(dir, "topSystem.json"),
- },
+ {api_firmware: read_data(dir, "firmwareFirmware_6.0.5h.json")},
True,
[],
"6.0(5h)",
+ read_data(dir, "fabricNode.json"),
script.ERROR,
+ [
+ ["1", "apic1", "-", "-", "Simulated exception at connect()"],
+ ["2", "apic2", "-", "-", "Simulated exception at connect()"],
+ ["3", "apic3", "-", "-", "Simulated exception at connect()"],
+ ],
),
# Exception failure at the ls command
(
- {
- api_firmware: read_data(dir, "firmwareFirmware_6.0.5h.json"),
- api_topSystem: read_data(dir, "topSystem.json"),
- },
+ {api_firmware: read_data(dir, "firmwareFirmware_6.0.5h.json")},
False,
{
apic_ip: [
@@ -93,14 +104,17 @@
for apic_ip in apic_ips
},
"6.0(5h)",
+ read_data(dir, "fabricNode.json"),
script.ERROR,
+ [
+ ["1", "apic1", "-", "-", "ls command via ssh failed due to:Simulated exception at `ls` command"],
+ ["2", "apic2", "-", "-", "ls command via ssh failed due to:Simulated exception at `ls` command"],
+ ["3", "apic3", "-", "-", "ls command via ssh failed due to:Simulated exception at `ls` command"],
+ ],
),
# No such file output from the ls command
(
- {
- api_firmware: read_data(dir, "firmwareFirmware_6.0.5h.json"),
- api_topSystem: read_data(dir, "topSystem.json"),
- },
+ {api_firmware: read_data(dir, "firmwareFirmware_6.0.5h.json")},
False,
{
apic_ip: [
@@ -113,14 +127,17 @@
for apic_ip in apic_ips
},
"6.0(5h)",
+ read_data(dir, "fabricNode.json"),
script.FAIL_UF,
+ [
+ ["1", "apic1", "6.0(5h)", "-", "image not found"],
+ ["2", "apic2", "6.0(5h)", "-", "image not found"],
+ ["3", "apic3", "6.0(5h)", "-", "image not found"],
+ ],
),
# Exception failure at the cat command
(
- {
- api_firmware: read_data(dir, "firmwareFirmware_6.0.5h.json"),
- api_topSystem: read_data(dir, "topSystem.json"),
- },
+ {api_firmware: read_data(dir, "firmwareFirmware_6.0.5h.json")},
False,
{
apic_ip: [
@@ -138,14 +155,17 @@
for apic_ip in apic_ips
},
"6.0(5h)",
+ read_data(dir, "fabricNode.json"),
script.ERROR,
+ [
+ ["1", "apic1", "6.0(5h)", "-", "failed to check md5sum via ssh due to:Simulated exception at `cat` command"],
+ ["2", "apic2", "6.0(5h)", "-", "failed to check md5sum via ssh due to:Simulated exception at `cat` command"],
+ ["3", "apic3", "6.0(5h)", "-", "failed to check md5sum via ssh due to:Simulated exception at `cat` command"],
+ ],
),
# No such file output from the cat command
(
- {
- api_firmware: read_data(dir, "firmwareFirmware_6.0.5h.json"),
- api_topSystem: read_data(dir, "topSystem.json"),
- },
+ {api_firmware: read_data(dir, "firmwareFirmware_6.0.5h.json")},
False,
{
apic_ip: [
@@ -163,14 +183,17 @@
for apic_ip in apic_ips
},
"6.0(5h)",
+ read_data(dir, "fabricNode.json"),
script.FAIL_UF,
+ [
+ ["1", "apic1", "6.0(5h)", "-", "md5sum file not found"],
+ ["2", "apic2", "6.0(5h)", "-", "md5sum file not found"],
+ ["3", "apic3", "6.0(5h)", "-", "md5sum file not found"],
+ ],
),
# Unexpected output from the cat command
(
- {
- api_firmware: read_data(dir, "firmwareFirmware_6.0.5h.json"),
- api_topSystem: read_data(dir, "topSystem.json"),
- },
+ {api_firmware: read_data(dir, "firmwareFirmware_6.0.5h.json")},
False,
{
apic_ip: [
@@ -188,14 +211,17 @@
for apic_ip in apic_ips
},
"6.0(5h)",
+ read_data(dir, "fabricNode.json"),
script.ERROR,
+ [
+ ["1", "apic1", "6.0(5h)", "-", "unexpected output when checking md5sum file"],
+ ["2", "apic2", "6.0(5h)", "-", "unexpected output when checking md5sum file"],
+ ["3", "apic3", "6.0(5h)", "-", "unexpected output when checking md5sum file"],
+ ],
),
# Failure because md5sum on each APIC do not match
(
- {
- api_firmware: read_data(dir, "firmwareFirmware_6.0.5h.json"),
- api_topSystem: read_data(dir, "topSystem.json"),
- },
+ {api_firmware: read_data(dir, "firmwareFirmware_6.0.5h.json")},
False,
{
apic_ips[0]: [
@@ -236,13 +262,43 @@
],
},
"6.0(5h)",
+ read_data(dir, "fabricNode.json"),
script.FAIL_UF,
+ [
+ ["1", "apic1", "6.0(5h)", "d5afca58fce2018495d068c44eb4a547", "md5sum do not match on all APICs"],
+ ["2", "apic2", "6.0(5h)", "d5afca58fce2018495d068c000000000", "md5sum do not match on all APICs"],
+ ["3", "apic3", "6.0(5h)", "d5afca58fce2018495d068c44eb4a547", "md5sum do not match on all APICs"],
+ ],
),
# Pass
+ (
+ {api_firmware: read_data(dir, "firmwareFirmware_6.0.5h.json")},
+ False,
+ {
+ apic_ip: [
+ {
+ "cmd": ls_cmd,
+ "output": "\n".join([ls_cmd, ls_output]),
+ "exception": None,
+ },
+ {
+ "cmd": cat_cmd,
+ "output": "\n".join([cat_cmd, cat_output]),
+ "exception": None,
+ },
+ ]
+ for apic_ip in apic_ips
+ },
+ "6.0(5h)",
+ read_data(dir, "fabricNode.json"),
+ script.PASS,
+ [],
+ ),
+ # Pass (pre-4.0 with infraWiNode)
(
{
api_firmware: read_data(dir, "firmwareFirmware_6.0.5h.json"),
- api_topSystem: read_data(dir, "topSystem.json"),
+ api_infraWiNode: read_data(dir, "infraWiNode_apic1.json"),
},
False,
{
@@ -261,11 +317,18 @@
for apic_ip in apic_ips
},
"6.0(5h)",
+ read_data(dir, "fabricNode_old.json"),
script.PASS,
+ [],
),
],
)
-def test_logic(mock_icurl, mock_conn, tversion, expected_result):
- tver = script.AciVersion(tversion) if tversion else None
- result = script.apic_version_md5_check(1, 1, tver, "fake_username", "fake_password")
- assert result == expected_result
+def test_logic(run_check, mock_icurl, mock_conn, tversion, fabric_nodes, expected_result, expected_data):
+ result = run_check(
+ tversion=script.AciVersion(tversion) if tversion else None,
+ username="fake_username",
+ password="fake_password",
+ fabric_nodes=fabric_nodes,
+ )
+ assert result.result == expected_result
+ assert result.data == expected_data
diff --git a/tests/ave_eol_check/test_ave_eol_check.py b/tests/checks/ave_eol_check/test_ave_eol_check.py
similarity index 81%
rename from tests/ave_eol_check/test_ave_eol_check.py
rename to tests/checks/ave_eol_check/test_ave_eol_check.py
index 38db4384..0bf67c06 100644
--- a/tests/ave_eol_check/test_ave_eol_check.py
+++ b/tests/checks/ave_eol_check/test_ave_eol_check.py
@@ -9,12 +9,13 @@
log = logging.getLogger(__name__)
dir = os.path.dirname(os.path.abspath(__file__))
+test_function = "ave_eol_check"
# icurl queries
-
ave_api = 'vmmDomP.json'
ave_api += '?query-target-filter=eq(vmmDomP.enableAVE,"true")'
+
@pytest.mark.parametrize(
"icurl_outputs, tversion, expected_result",
[
@@ -44,6 +45,8 @@
),
],
)
-def test_logic(mock_icurl, tversion, expected_result):
- result = script.ave_eol_check(1, 1, script.AciVersion(tversion) if tversion else None)
- assert result == expected_result
+def test_logic(run_check, mock_icurl, tversion, expected_result):
+ result = run_check(
+ tversion=script.AciVersion(tversion) if tversion else None,
+ )
+ assert result.result == expected_result
diff --git a/tests/ave_eol_check/vmmDomP_POS.json b/tests/checks/ave_eol_check/vmmDomP_POS.json
similarity index 100%
rename from tests/ave_eol_check/vmmDomP_POS.json
rename to tests/checks/ave_eol_check/vmmDomP_POS.json
diff --git a/tests/bgp_golf_route_target_type_check/fvCtx_pos.json b/tests/checks/bgp_golf_route_target_type_check/fvCtx_pos.json
similarity index 100%
rename from tests/bgp_golf_route_target_type_check/fvCtx_pos.json
rename to tests/checks/bgp_golf_route_target_type_check/fvCtx_pos.json
diff --git a/tests/bgp_golf_route_target_type_check/test_bgp_golf_route_target_type_check.py b/tests/checks/bgp_golf_route_target_type_check/test_bgp_golf_route_target_type_check.py
similarity index 83%
rename from tests/bgp_golf_route_target_type_check/test_bgp_golf_route_target_type_check.py
rename to tests/checks/bgp_golf_route_target_type_check/test_bgp_golf_route_target_type_check.py
index 2fc2e58d..56e71457 100644
--- a/tests/bgp_golf_route_target_type_check/test_bgp_golf_route_target_type_check.py
+++ b/tests/checks/bgp_golf_route_target_type_check/test_bgp_golf_route_target_type_check.py
@@ -9,6 +9,7 @@
log = logging.getLogger(__name__)
dir = os.path.dirname(os.path.abspath(__file__))
+test_function = "bgp_golf_route_target_type_check"
# icurl queries
fvCtxs = 'fvCtx.json?rsp-subtree=full&rsp-subtree-class=l3extGlobalCtxName,bgpRtTarget&rsp-subtree-include=required'
@@ -61,11 +62,9 @@
),
],
)
-def test_logic(mock_icurl, cversion, tversion, expected_result):
- result = script.bgp_golf_route_target_type_check(
- 1,
- 1,
- script.AciVersion(cversion),
- script.AciVersion(tversion) if tversion else None,
+def test_logic(run_check, mock_icurl, cversion, tversion, expected_result):
+ result = run_check(
+ cversion=script.AciVersion(cversion),
+ tversion=script.AciVersion(tversion) if tversion else None,
)
- assert result == expected_result
+ assert result.result == expected_result
diff --git a/tests/bgp_peer_loopback_check/l3extRsNodeL3OutAtt_neg.json b/tests/checks/bgp_peer_loopback_check/l3extRsNodeL3OutAtt_neg.json
similarity index 100%
rename from tests/bgp_peer_loopback_check/l3extRsNodeL3OutAtt_neg.json
rename to tests/checks/bgp_peer_loopback_check/l3extRsNodeL3OutAtt_neg.json
diff --git a/tests/bgp_peer_loopback_check/l3extRsNodeL3OutAtt_pos.json b/tests/checks/bgp_peer_loopback_check/l3extRsNodeL3OutAtt_pos.json
similarity index 100%
rename from tests/bgp_peer_loopback_check/l3extRsNodeL3OutAtt_pos.json
rename to tests/checks/bgp_peer_loopback_check/l3extRsNodeL3OutAtt_pos.json
diff --git a/tests/bgp_peer_loopback_check/l3extRsNodeL3OutAtt_pos1.json b/tests/checks/bgp_peer_loopback_check/l3extRsNodeL3OutAtt_pos1.json
similarity index 100%
rename from tests/bgp_peer_loopback_check/l3extRsNodeL3OutAtt_pos1.json
rename to tests/checks/bgp_peer_loopback_check/l3extRsNodeL3OutAtt_pos1.json
diff --git a/tests/bgp_peer_loopback_check/l3extRsNodeL3OutAtt_pos2.json b/tests/checks/bgp_peer_loopback_check/l3extRsNodeL3OutAtt_pos2.json
similarity index 100%
rename from tests/bgp_peer_loopback_check/l3extRsNodeL3OutAtt_pos2.json
rename to tests/checks/bgp_peer_loopback_check/l3extRsNodeL3OutAtt_pos2.json
diff --git a/tests/bgp_peer_loopback_check/test_bgp_peer_loopback_check.py b/tests/checks/bgp_peer_loopback_check/test_bgp_peer_loopback_check.py
similarity index 87%
rename from tests/bgp_peer_loopback_check/test_bgp_peer_loopback_check.py
rename to tests/checks/bgp_peer_loopback_check/test_bgp_peer_loopback_check.py
index a9e5d360..d08522ee 100644
--- a/tests/bgp_peer_loopback_check/test_bgp_peer_loopback_check.py
+++ b/tests/checks/bgp_peer_loopback_check/test_bgp_peer_loopback_check.py
@@ -9,6 +9,7 @@
log = logging.getLogger(__name__)
dir = os.path.dirname(os.path.abspath(__file__))
+test_function = "bgp_peer_loopback_check"
# icurl queries
l3extLNodePs = "l3extLNodeP.json?rsp-subtree=full&rsp-subtree-class=bgpPeerP,l3extRsNodeL3OutAtt,l3extLoopBackIfP"
@@ -39,6 +40,6 @@
),
],
)
-def test_logic(mock_icurl, expected_result):
- result = script.bgp_peer_loopback_check(1, 1)
- assert result == expected_result
+def test_logic(run_check, mock_icurl, expected_result):
+ result = run_check()
+ assert result.result == expected_result
diff --git a/tests/cimc_compatibilty_check/compatRsSuppHw_605_L2.json b/tests/checks/cimc_compatibilty_check/compatRsSuppHw_605_L2.json
similarity index 100%
rename from tests/cimc_compatibilty_check/compatRsSuppHw_605_L2.json
rename to tests/checks/cimc_compatibilty_check/compatRsSuppHw_605_L2.json
diff --git a/tests/cimc_compatibilty_check/compatRsSuppHw_605_M1.json b/tests/checks/cimc_compatibilty_check/compatRsSuppHw_605_M1.json
similarity index 100%
rename from tests/cimc_compatibilty_check/compatRsSuppHw_605_M1.json
rename to tests/checks/cimc_compatibilty_check/compatRsSuppHw_605_M1.json
diff --git a/tests/cimc_compatibilty_check/compatRsSuppHw_empty.json b/tests/checks/cimc_compatibilty_check/compatRsSuppHw_empty.json
similarity index 100%
rename from tests/cimc_compatibilty_check/compatRsSuppHw_empty.json
rename to tests/checks/cimc_compatibilty_check/compatRsSuppHw_empty.json
diff --git a/tests/cimc_compatibilty_check/eqptCh_newver.json b/tests/checks/cimc_compatibilty_check/eqptCh_newver.json
similarity index 100%
rename from tests/cimc_compatibilty_check/eqptCh_newver.json
rename to tests/checks/cimc_compatibilty_check/eqptCh_newver.json
diff --git a/tests/cimc_compatibilty_check/eqptCh_oldver.json b/tests/checks/cimc_compatibilty_check/eqptCh_oldver.json
similarity index 100%
rename from tests/cimc_compatibilty_check/eqptCh_oldver.json
rename to tests/checks/cimc_compatibilty_check/eqptCh_oldver.json
diff --git a/tests/cimc_compatibilty_check/eqptCh_reallyoldver.json b/tests/checks/cimc_compatibilty_check/eqptCh_reallyoldver.json
similarity index 100%
rename from tests/cimc_compatibilty_check/eqptCh_reallyoldver.json
rename to tests/checks/cimc_compatibilty_check/eqptCh_reallyoldver.json
diff --git a/tests/cimc_compatibilty_check/test_cimc_compatibilty_check.py b/tests/checks/cimc_compatibilty_check/test_cimc_compatibilty_check.py
similarity index 60%
rename from tests/cimc_compatibilty_check/test_cimc_compatibilty_check.py
rename to tests/checks/cimc_compatibilty_check/test_cimc_compatibilty_check.py
index 9ce8af6f..cb587fb3 100644
--- a/tests/cimc_compatibilty_check/test_cimc_compatibilty_check.py
+++ b/tests/checks/cimc_compatibilty_check/test_cimc_compatibilty_check.py
@@ -9,6 +9,7 @@
log = logging.getLogger(__name__)
dir = os.path.dirname(os.path.abspath(__file__))
+test_function = "cimc_compatibilty_check"
# icurl queries
eqptCh_api = 'eqptCh.json?query-target-filter=wcard(eqptCh.descr,"APIC")'
@@ -16,40 +17,41 @@
compatRsSuppHwL2_api = 'uni/fabric/compcat-default/ctlrfw-apic-6.0(5)/rssuppHw-[uni/fabric/compcat-default/ctlrhw-apicl2].json'
compatRsSuppHwM1_api = 'uni/fabric/compcat-default/ctlrfw-apic-6.0(5)/rssuppHw-[uni/fabric/compcat-default/ctlrhw-apicm1].json'
+
@pytest.mark.parametrize(
"icurl_outputs, tversion, expected_result",
[
(
{eqptCh_api: read_data(dir, "eqptCh_reallyoldver.json"),
- compatRsSuppHwL2_api: read_data(dir, "compatRsSuppHw_605_L2.json"),
- compatRsSuppHwM1_api: read_data(dir, "compatRsSuppHw_605_M1.json")},
+ compatRsSuppHwL2_api: read_data(dir, "compatRsSuppHw_605_L2.json"),
+ compatRsSuppHwM1_api: read_data(dir, "compatRsSuppHw_605_M1.json")},
"6.0(5a)",
script.FAIL_UF,
),
(
{eqptCh_api: read_data(dir, "eqptCh_oldver.json"),
- compatRsSuppHwL2_api: read_data(dir, "compatRsSuppHw_605_L2.json"),
- compatRsSuppHwM1_api: read_data(dir, "compatRsSuppHw_605_M1.json")},
+ compatRsSuppHwL2_api: read_data(dir, "compatRsSuppHw_605_L2.json"),
+ compatRsSuppHwM1_api: read_data(dir, "compatRsSuppHw_605_M1.json")},
"6.0(5a)",
script.FAIL_UF,
),
(
{eqptCh_api: read_data(dir, "eqptCh_newver.json"),
- compatRsSuppHwL2_api: read_data(dir, "compatRsSuppHw_605_L2.json"),
- compatRsSuppHwM1_api: read_data(dir, "compatRsSuppHw_605_M1.json")},
+ compatRsSuppHwL2_api: read_data(dir, "compatRsSuppHw_605_L2.json"),
+ compatRsSuppHwM1_api: read_data(dir, "compatRsSuppHw_605_M1.json")},
"6.0(5a)",
script.PASS,
),
# Seen in QA testing where version + model does not have catalog entry
(
{eqptCh_api: read_data(dir, "eqptCh_newver.json"),
- compatRsSuppHwL2_api: read_data(dir, "compatRsSuppHw_605_L2.json"),
- compatRsSuppHwM1_api: read_data(dir, "compatRsSuppHw_empty.json")},
+ compatRsSuppHwL2_api: read_data(dir, "compatRsSuppHw_605_L2.json"),
+ compatRsSuppHwM1_api: read_data(dir, "compatRsSuppHw_empty.json")},
"6.0(5a)",
script.MANUAL,
),
],
)
-def test_logic(mock_icurl, tversion, expected_result):
- result = script.cimc_compatibilty_check(1, 1, script.AciVersion(tversion))
- assert result == expected_result
+def test_logic(run_check, mock_icurl, tversion, expected_result):
+ result = run_check(tversion=script.AciVersion(tversion))
+ assert result.result == expected_result
diff --git a/tests/clock_signal_component_failure_check/eqptFC_NEG.json b/tests/checks/clock_signal_component_failure_check/eqptFC_NEG.json
similarity index 100%
rename from tests/clock_signal_component_failure_check/eqptFC_NEG.json
rename to tests/checks/clock_signal_component_failure_check/eqptFC_NEG.json
diff --git a/tests/clock_signal_component_failure_check/eqptFC_POS.json b/tests/checks/clock_signal_component_failure_check/eqptFC_POS.json
similarity index 100%
rename from tests/clock_signal_component_failure_check/eqptFC_POS.json
rename to tests/checks/clock_signal_component_failure_check/eqptFC_POS.json
diff --git a/tests/clock_signal_component_failure_check/eqptLC_NEG.json b/tests/checks/clock_signal_component_failure_check/eqptLC_NEG.json
similarity index 100%
rename from tests/clock_signal_component_failure_check/eqptLC_NEG.json
rename to tests/checks/clock_signal_component_failure_check/eqptLC_NEG.json
diff --git a/tests/clock_signal_component_failure_check/eqptLC_POS.json b/tests/checks/clock_signal_component_failure_check/eqptLC_POS.json
similarity index 100%
rename from tests/clock_signal_component_failure_check/eqptLC_POS.json
rename to tests/checks/clock_signal_component_failure_check/eqptLC_POS.json
diff --git a/tests/clock_signal_component_failure_check/test_clock_signal_component_failure_check.py b/tests/checks/clock_signal_component_failure_check/test_clock_signal_component_failure_check.py
similarity index 88%
rename from tests/clock_signal_component_failure_check/test_clock_signal_component_failure_check.py
rename to tests/checks/clock_signal_component_failure_check/test_clock_signal_component_failure_check.py
index bf1b6683..a1b017e5 100644
--- a/tests/clock_signal_component_failure_check/test_clock_signal_component_failure_check.py
+++ b/tests/checks/clock_signal_component_failure_check/test_clock_signal_component_failure_check.py
@@ -9,6 +9,7 @@
log = logging.getLogger(__name__)
dir = os.path.dirname(os.path.abspath(__file__))
+test_function = "clock_signal_component_failure_check"
eqptFC_api = 'eqptFC.json'
eqptFC_api += '?query-target-filter=or(eq(eqptFC.model,"N9K-C9504-FM-E"),eq(eqptFC.model,"N9K-C9508-FM-E"))'
@@ -52,6 +53,6 @@
)
],
)
-def test_logic(mock_icurl,expected_result):
- result = script.clock_signal_component_failure_check(1, 1)
- assert result == expected_result
+def test_logic(run_check, mock_icurl, expected_result):
+ result = run_check()
+ assert result.result == expected_result
diff --git a/tests/cloudsec_encryption_depr_check/cloudsecPreSharedKey_err.json b/tests/checks/cloudsec_encryption_depr_check/cloudsecPreSharedKey_err.json
similarity index 100%
rename from tests/cloudsec_encryption_depr_check/cloudsecPreSharedKey_err.json
rename to tests/checks/cloudsec_encryption_depr_check/cloudsecPreSharedKey_err.json
diff --git a/tests/cloudsec_encryption_depr_check/cloudsecPreSharedKey_neg.json b/tests/checks/cloudsec_encryption_depr_check/cloudsecPreSharedKey_neg.json
similarity index 100%
rename from tests/cloudsec_encryption_depr_check/cloudsecPreSharedKey_neg.json
rename to tests/checks/cloudsec_encryption_depr_check/cloudsecPreSharedKey_neg.json
diff --git a/tests/cloudsec_encryption_depr_check/cloudsecPreSharedKey_pos.json b/tests/checks/cloudsec_encryption_depr_check/cloudsecPreSharedKey_pos.json
similarity index 100%
rename from tests/cloudsec_encryption_depr_check/cloudsecPreSharedKey_pos.json
rename to tests/checks/cloudsec_encryption_depr_check/cloudsecPreSharedKey_pos.json
diff --git a/tests/cloudsec_encryption_depr_check/cloudsecPreSharedKey_pos2.json b/tests/checks/cloudsec_encryption_depr_check/cloudsecPreSharedKey_pos2.json
similarity index 100%
rename from tests/cloudsec_encryption_depr_check/cloudsecPreSharedKey_pos2.json
rename to tests/checks/cloudsec_encryption_depr_check/cloudsecPreSharedKey_pos2.json
diff --git a/tests/cloudsec_encryption_depr_check/test_cloudsec_encryption_depr_check.py b/tests/checks/cloudsec_encryption_depr_check/test_cloudsec_encryption_depr_check.py
similarity index 89%
rename from tests/cloudsec_encryption_depr_check/test_cloudsec_encryption_depr_check.py
rename to tests/checks/cloudsec_encryption_depr_check/test_cloudsec_encryption_depr_check.py
index 13789148..16678e32 100644
--- a/tests/cloudsec_encryption_depr_check/test_cloudsec_encryption_depr_check.py
+++ b/tests/checks/cloudsec_encryption_depr_check/test_cloudsec_encryption_depr_check.py
@@ -10,6 +10,7 @@
log = logging.getLogger(__name__)
dir = os.path.dirname(os.path.abspath(__file__))
+test_function = "cloudsec_encryption_depr_check"
# icurl queries
cloudsecPreSharedKey = 'cloudsecPreSharedKey.json'
@@ -56,6 +57,6 @@
),
],
)
-def test_logic(mock_icurl, tversion, expected_result):
- result = script.cloudsec_encryption_depr_check(1, 1, script.AciVersion(tversion))
- assert result == expected_result
+def test_logic(run_check, mock_icurl, tversion, expected_result):
+ result = run_check(tversion=script.AciVersion(tversion))
+ assert result.result == expected_result
diff --git a/tests/configpush_shard_check/configpushShardCont_pos.json b/tests/checks/configpush_shard_check/configpushShardCont_pos.json
similarity index 100%
rename from tests/configpush_shard_check/configpushShardCont_pos.json
rename to tests/checks/configpush_shard_check/configpushShardCont_pos.json
diff --git a/tests/configpush_shard_check/test_configpush_shard_check.py b/tests/checks/configpush_shard_check/test_configpush_shard_check.py
similarity index 86%
rename from tests/configpush_shard_check/test_configpush_shard_check.py
rename to tests/checks/configpush_shard_check/test_configpush_shard_check.py
index 341f5665..3e482247 100644
--- a/tests/configpush_shard_check/test_configpush_shard_check.py
+++ b/tests/checks/configpush_shard_check/test_configpush_shard_check.py
@@ -9,10 +9,13 @@
log = logging.getLogger(__name__)
dir = os.path.dirname(os.path.abspath(__file__))
+test_function = "configpush_shard_check"
+
# icurl queries
configpushShardCont_api = 'configpushShardCont.json'
configpushShardCont_api += '?query-target-filter=and(eq(configpushShardCont.tailTx,"0"),ne(configpushShardCont.headTx,"0"))'
+
@pytest.mark.parametrize(
"icurl_outputs, tversion, expected_result",
[
@@ -50,7 +53,8 @@
),
],
)
-def test_logic(mock_icurl, tversion, expected_result):
- tversion = script.AciVersion(tversion) if tversion else None
- result = script.configpush_shard_check(1, 1, tversion)
- assert result == expected_result
+def test_logic(run_check, mock_icurl, tversion, expected_result):
+ result = run_check(
+ tversion=script.AciVersion(tversion) if tversion else None,
+ )
+ assert result.result == expected_result
diff --git a/tests/checks/conftest.py b/tests/checks/conftest.py
new file mode 100644
index 00000000..01cd8870
--- /dev/null
+++ b/tests/checks/conftest.py
@@ -0,0 +1,137 @@
+import pytest
+import logging
+import importlib
+from subprocess import CalledProcessError
+
+script = importlib.import_module("aci-preupgrade-validation-script")
+
+log = logging.getLogger()
+
+
+@pytest.fixture
+def run_check(request):
+ def _run_check(**kwargs):
+ test_function = getattr(request.module, "test_function")
+ cm = script.CheckManager(debug_function=test_function, monitor_interval=0.01)
+ cm.initialize_checks()
+
+ err = "Unable to find test_function ({}) in CheckManager".format(test_function)
+ assert test_function in cm.check_ids, err
+
+ cm.run_checks(common_data=kwargs)
+
+ result = cm.get_check_result(test_function)
+ assert isinstance(result, script.Result)
+
+ return result
+
+ return _run_check
+
+
+@pytest.fixture
+def conn_failure():
+ return False
+
+
+@pytest.fixture
+def conn_cmds():
+ '''
+ Set of test parameters for mocked `Connection.cmd()`.
+
+ ex)
+ ```
+ {
+ : [{
+ "cmd": "ls -aslh /firmware/fwrepos/fwrepo/aci-apic-dk9.6.0.5h.bin",
+ "output": """\
+ ls -aslh /firmware/fwrepos/fwrepo/aci-apic-dk9.6.0.5h.bin
+ 6.1G -rwxr-xr-x 1 root root 6.1G Apr 3 16:36 /firmware/fwrepos/fwrepo/aci-apic-dk9.6.0.5h.bin
+ f2-apic1#
+ """,
+ "exception": None
+ }]
+ }
+ ```
+
+ The real output from `Connection.cmd()` (i.e. `Connection.output`) contains many ANSI characters. In this fixture, those characters are not considered.
+ '''
+ return {}
+
+
+class MockConnection(script.Connection):
+ conn_failure = False
+ conn_cmds = None
+
+ def connect(self):
+ """
+ `Connection.connect()` is just instantiating `pexepect.spawn()` which does not
+ initiate the SSH connection yet. Not exception likely happens here.
+ """
+ if self.conn_failure:
+ raise Exception("Simulated exception at connect()")
+
+ def cmd(self, command, **kargs):
+ """
+ `Connection.cmd()` initiates the SSH connection (if not done yet) and sends the command.
+ Each check typically has multiple `cmd()` with different commands.
+ To cover that, this mock func uses a dictionary `conn_cmds` as the test data.
+ """
+ _conn_cmds = self.conn_cmds[self.hostname]
+ for conn_cmd in _conn_cmds:
+ if command == conn_cmd["cmd"]:
+ if conn_cmd["exception"]:
+ raise conn_cmd["exception"]
+ self.output = conn_cmd["output"]
+ break
+ else:
+ log.error("Command `%s` not found in test data `conn_cmds`", command)
+ raise Exception("FAILURE IN PYTEST")
+
+
+@pytest.fixture
+def mock_conn(monkeypatch, conn_failure, conn_cmds):
+ MockConnection.conn_failure = conn_failure
+ MockConnection.conn_cmds = conn_cmds
+ monkeypatch.setattr(script, "Connection", MockConnection)
+
+
+@pytest.fixture
+def cmd_outputs():
+ """
+ Mocked output for `run_cmd` function.
+ This is used to avoid executing real commands in tests.
+ """
+ return {
+ "ls -aslh /firmware/fwrepos/fwrepo/aci-apic-dk9.6.0.5h.bin": {
+ "splitlines": False,
+ "output": "6.1G -rwxr-xr-x 1 root root 6.1G Apr 3 16:36 /firmware/fwrepos/fwrepo/aci-apic-dk9.6.0.5h.bin\napic1#",
+ }
+ }
+
+
+@pytest.fixture
+def mock_run_cmd(monkeypatch, cmd_outputs):
+ """
+ Mock the `run_cmd` function to avoid executing real commands.
+ This is useful for tests that do not require actual command execution.
+ """
+ def _mock_run_cmd(cmd, splitlines=False):
+ details = cmd_outputs.get(cmd)
+ if details is None:
+ log.error("Command `%s` not found in test data", cmd)
+ return ""
+ if details.get("CalledProcessError"):
+ raise CalledProcessError(127, cmd)
+
+ splitlines = details.get("splitlines", False)
+ output = details.get("output")
+ if output is None:
+ log.error("Output for cmd `%s` not found in test data", cmd)
+ output = ""
+
+ log.debug("Mocked run_cmd called with args: %s, kwargs: %s", cmd, splitlines)
+ if splitlines:
+ return output.splitlines()
+ else:
+ return output
+ monkeypatch.setattr(script, "run_cmd", _mock_run_cmd)
diff --git a/tests/consumer_vzany_shared_services_check/epg_epg2_unmatched.json b/tests/checks/consumer_vzany_shared_services_check/epg_epg2_unmatched.json
similarity index 100%
rename from tests/consumer_vzany_shared_services_check/epg_epg2_unmatched.json
rename to tests/checks/consumer_vzany_shared_services_check/epg_epg2_unmatched.json
diff --git a/tests/consumer_vzany_shared_services_check/esg_esg2.json b/tests/checks/consumer_vzany_shared_services_check/esg_esg2.json
similarity index 100%
rename from tests/consumer_vzany_shared_services_check/esg_esg2.json
rename to tests/checks/consumer_vzany_shared_services_check/esg_esg2.json
diff --git a/tests/consumer_vzany_shared_services_check/fvCtx_consumer_same_vrf.json b/tests/checks/consumer_vzany_shared_services_check/fvCtx_consumer_same_vrf.json
similarity index 100%
rename from tests/consumer_vzany_shared_services_check/fvCtx_consumer_same_vrf.json
rename to tests/checks/consumer_vzany_shared_services_check/fvCtx_consumer_same_vrf.json
diff --git a/tests/consumer_vzany_shared_services_check/fvCtx_consumer_shared.json b/tests/checks/consumer_vzany_shared_services_check/fvCtx_consumer_shared.json
similarity index 100%
rename from tests/consumer_vzany_shared_services_check/fvCtx_consumer_shared.json
rename to tests/checks/consumer_vzany_shared_services_check/fvCtx_consumer_shared.json
diff --git a/tests/consumer_vzany_shared_services_check/fvCtx_no_consumers.json b/tests/checks/consumer_vzany_shared_services_check/fvCtx_no_consumers.json
similarity index 100%
rename from tests/consumer_vzany_shared_services_check/fvCtx_no_consumers.json
rename to tests/checks/consumer_vzany_shared_services_check/fvCtx_no_consumers.json
diff --git a/tests/consumer_vzany_shared_services_check/global_contracts_epg_only.json b/tests/checks/consumer_vzany_shared_services_check/global_contracts_epg_only.json
similarity index 100%
rename from tests/consumer_vzany_shared_services_check/global_contracts_epg_only.json
rename to tests/checks/consumer_vzany_shared_services_check/global_contracts_epg_only.json
diff --git a/tests/consumer_vzany_shared_services_check/global_contracts_esg_only.json b/tests/checks/consumer_vzany_shared_services_check/global_contracts_esg_only.json
similarity index 100%
rename from tests/consumer_vzany_shared_services_check/global_contracts_esg_only.json
rename to tests/checks/consumer_vzany_shared_services_check/global_contracts_esg_only.json
diff --git a/tests/consumer_vzany_shared_services_check/global_contracts_same_vrf.json b/tests/checks/consumer_vzany_shared_services_check/global_contracts_same_vrf.json
similarity index 100%
rename from tests/consumer_vzany_shared_services_check/global_contracts_same_vrf.json
rename to tests/checks/consumer_vzany_shared_services_check/global_contracts_same_vrf.json
diff --git a/tests/consumer_vzany_shared_services_check/global_contracts_shared.json b/tests/checks/consumer_vzany_shared_services_check/global_contracts_shared.json
similarity index 100%
rename from tests/consumer_vzany_shared_services_check/global_contracts_shared.json
rename to tests/checks/consumer_vzany_shared_services_check/global_contracts_shared.json
diff --git a/tests/consumer_vzany_shared_services_check/instp_l3instp2.json b/tests/checks/consumer_vzany_shared_services_check/instp_l3instp2.json
similarity index 100%
rename from tests/consumer_vzany_shared_services_check/instp_l3instp2.json
rename to tests/checks/consumer_vzany_shared_services_check/instp_l3instp2.json
diff --git a/tests/consumer_vzany_shared_services_check/test_consumer_vzany_shared_services_check.py b/tests/checks/consumer_vzany_shared_services_check/test_consumer_vzany_shared_services_check.py
similarity index 97%
rename from tests/consumer_vzany_shared_services_check/test_consumer_vzany_shared_services_check.py
rename to tests/checks/consumer_vzany_shared_services_check/test_consumer_vzany_shared_services_check.py
index 92019d2d..628dff95 100644
--- a/tests/consumer_vzany_shared_services_check/test_consumer_vzany_shared_services_check.py
+++ b/tests/checks/consumer_vzany_shared_services_check/test_consumer_vzany_shared_services_check.py
@@ -9,6 +9,7 @@
log = logging.getLogger(__name__)
dir = os.path.dirname(os.path.abspath(__file__))
+test_function = "consumer_vzany_shared_services_check"
# icurl queries
fvCtx_query = "fvCtx.json?rsp-subtree=full&rsp-subtree-class=vzRsAnyToCons"
@@ -262,11 +263,9 @@
),
],
)
-def test_logic(mock_icurl, cversion, tversion, expected_result):
- result = script.consumer_vzany_shared_services_check(
- 1,
- 1,
- script.AciVersion(cversion),
- script.AciVersion(tversion) if tversion else None,
+def test_logic(run_check, mock_icurl, cversion, tversion, expected_result):
+ result = run_check(
+ cversion=script.AciVersion(cversion),
+ tversion=script.AciVersion(tversion) if tversion else None,
)
- assert result == expected_result
+ assert result.result == expected_result
diff --git a/tests/consumer_vzany_shared_services_check/vnsGraphInst_redirect.json b/tests/checks/consumer_vzany_shared_services_check/vnsGraphInst_redirect.json
similarity index 100%
rename from tests/consumer_vzany_shared_services_check/vnsGraphInst_redirect.json
rename to tests/checks/consumer_vzany_shared_services_check/vnsGraphInst_redirect.json
diff --git a/tests/contract_22_defect_check/test_contract_22_defect_check.py b/tests/checks/contract_22_defect_check/test_contract_22_defect_check.py
similarity index 65%
rename from tests/contract_22_defect_check/test_contract_22_defect_check.py
rename to tests/checks/contract_22_defect_check/test_contract_22_defect_check.py
index 1c9e174d..26b036c5 100644
--- a/tests/contract_22_defect_check/test_contract_22_defect_check.py
+++ b/tests/checks/contract_22_defect_check/test_contract_22_defect_check.py
@@ -8,6 +8,8 @@
log = logging.getLogger(__name__)
dir = os.path.dirname(os.path.abspath(__file__))
+test_function = "contract_22_defect_check"
+
@pytest.mark.parametrize(
"cversion, tversion, expected_result",
@@ -20,11 +22,9 @@
("5.2(1a)", None, script.MANUAL),
],
)
-def test_logic(mock_icurl, cversion, tversion, expected_result):
- result = script.contract_22_defect_check(
- 1,
- 1,
- script.AciVersion(cversion),
- script.AciVersion(tversion) if tversion else None,
+def test_logic(run_check, mock_icurl, cversion, tversion, expected_result):
+ result = run_check(
+ cversion=script.AciVersion(cversion),
+ tversion=script.AciVersion(tversion) if tversion else None,
)
- assert result == expected_result
+ assert result.result == expected_result
diff --git a/tests/docker0_subent_overlap_check/apContainerPol_10_0_0_1__16.json b/tests/checks/docker0_subent_overlap_check/apContainerPol_10_0_0_1__16.json
similarity index 100%
rename from tests/docker0_subent_overlap_check/apContainerPol_10_0_0_1__16.json
rename to tests/checks/docker0_subent_overlap_check/apContainerPol_10_0_0_1__16.json
diff --git a/tests/docker0_subent_overlap_check/apContainerPol_172_16_0_1__15.json b/tests/checks/docker0_subent_overlap_check/apContainerPol_172_16_0_1__15.json
similarity index 100%
rename from tests/docker0_subent_overlap_check/apContainerPol_172_16_0_1__15.json
rename to tests/checks/docker0_subent_overlap_check/apContainerPol_172_16_0_1__15.json
diff --git a/tests/docker0_subent_overlap_check/apContainerPol_172_17_0_10__16.json b/tests/checks/docker0_subent_overlap_check/apContainerPol_172_17_0_10__16.json
similarity index 100%
rename from tests/docker0_subent_overlap_check/apContainerPol_172_17_0_10__16.json
rename to tests/checks/docker0_subent_overlap_check/apContainerPol_172_17_0_10__16.json
diff --git a/tests/docker0_subent_overlap_check/apContainerPol_172_17_0_1__16.json b/tests/checks/docker0_subent_overlap_check/apContainerPol_172_17_0_1__16.json
similarity index 100%
rename from tests/docker0_subent_overlap_check/apContainerPol_172_17_0_1__16.json
rename to tests/checks/docker0_subent_overlap_check/apContainerPol_172_17_0_1__16.json
diff --git a/tests/docker0_subent_overlap_check/apContainerPol_172_17_0_1__17.json b/tests/checks/docker0_subent_overlap_check/apContainerPol_172_17_0_1__17.json
similarity index 100%
rename from tests/docker0_subent_overlap_check/apContainerPol_172_17_0_1__17.json
rename to tests/checks/docker0_subent_overlap_check/apContainerPol_172_17_0_1__17.json
diff --git a/tests/docker0_subent_overlap_check/apContainerPol_172_18_0_1__16.json b/tests/checks/docker0_subent_overlap_check/apContainerPol_172_18_0_1__16.json
similarity index 100%
rename from tests/docker0_subent_overlap_check/apContainerPol_172_18_0_1__16.json
rename to tests/checks/docker0_subent_overlap_check/apContainerPol_172_18_0_1__16.json
diff --git a/tests/docker0_subent_overlap_check/infraWiNode_10_0_0_0__16.json b/tests/checks/docker0_subent_overlap_check/infraWiNode_10_0_0_0__16.json
similarity index 100%
rename from tests/docker0_subent_overlap_check/infraWiNode_10_0_0_0__16.json
rename to tests/checks/docker0_subent_overlap_check/infraWiNode_10_0_0_0__16.json
diff --git a/tests/docker0_subent_overlap_check/infraWiNode_10_0_x_0__24_remote_apic.json b/tests/checks/docker0_subent_overlap_check/infraWiNode_10_0_x_0__24_remote_apic.json
similarity index 100%
rename from tests/docker0_subent_overlap_check/infraWiNode_10_0_x_0__24_remote_apic.json
rename to tests/checks/docker0_subent_overlap_check/infraWiNode_10_0_x_0__24_remote_apic.json
diff --git a/tests/docker0_subent_overlap_check/infraWiNode_172_17_0_0__16.json b/tests/checks/docker0_subent_overlap_check/infraWiNode_172_17_0_0__16.json
similarity index 100%
rename from tests/docker0_subent_overlap_check/infraWiNode_172_17_0_0__16.json
rename to tests/checks/docker0_subent_overlap_check/infraWiNode_172_17_0_0__16.json
diff --git a/tests/docker0_subent_overlap_check/infraWiNode_172_17_x_0__24_remote_apic.json b/tests/checks/docker0_subent_overlap_check/infraWiNode_172_17_x_0__24_remote_apic.json
similarity index 100%
rename from tests/docker0_subent_overlap_check/infraWiNode_172_17_x_0__24_remote_apic.json
rename to tests/checks/docker0_subent_overlap_check/infraWiNode_172_17_x_0__24_remote_apic.json
diff --git a/tests/docker0_subent_overlap_check/test_docker0_subent_overlap_check.py b/tests/checks/docker0_subent_overlap_check/test_docker0_subent_overlap_check.py
similarity index 84%
rename from tests/docker0_subent_overlap_check/test_docker0_subent_overlap_check.py
rename to tests/checks/docker0_subent_overlap_check/test_docker0_subent_overlap_check.py
index 89cd737a..be655c62 100644
--- a/tests/docker0_subent_overlap_check/test_docker0_subent_overlap_check.py
+++ b/tests/checks/docker0_subent_overlap_check/test_docker0_subent_overlap_check.py
@@ -3,11 +3,13 @@
import logging
import importlib
from helpers.utils import read_data
+
script = importlib.import_module("aci-preupgrade-validation-script")
log = logging.getLogger(__name__)
dir = os.path.dirname(os.path.abspath(__file__))
+test_function = "docker0_subnet_overlap_check"
# icurl queries
infraWiNode = "infraWiNode.json"
@@ -15,13 +17,22 @@
@pytest.mark.parametrize(
- "icurl_outputs, expected_result",
+ "icurl_outputs, cversion, expected_result",
[
(
{
infraWiNode: read_data(dir, "infraWiNode_10_0_0_0__16.json"),
apContainerPol: [],
},
+ "6.1(2b)",
+ script.NA,
+ ),
+ (
+ {
+ infraWiNode: read_data(dir, "infraWiNode_10_0_0_0__16.json"),
+ apContainerPol: [],
+ },
+ "5.3(2b)",
script.PASS,
),
(
@@ -29,6 +40,7 @@
infraWiNode: read_data(dir, "infraWiNode_10_0_0_0__16.json"),
apContainerPol: read_data(dir, "apContainerPol_172_17_0_1__16.json"),
},
+ "5.3(2b)",
script.PASS,
),
(
@@ -36,6 +48,7 @@
infraWiNode: read_data(dir, "infraWiNode_10_0_0_0__16.json"),
apContainerPol: read_data(dir, "apContainerPol_10_0_0_1__16.json"),
},
+ "5.3(2b)",
script.FAIL_UF,
),
(
@@ -43,6 +56,7 @@
infraWiNode: read_data(dir, "infraWiNode_10_0_x_0__24_remote_apic.json"),
apContainerPol: [],
},
+ "5.3(2b)",
script.PASS,
),
(
@@ -50,6 +64,7 @@
infraWiNode: read_data(dir, "infraWiNode_10_0_x_0__24_remote_apic.json"),
apContainerPol: read_data(dir, "apContainerPol_172_17_0_1__16.json"),
},
+ "5.3(2b)",
script.PASS,
),
(
@@ -57,6 +72,7 @@
infraWiNode: read_data(dir, "infraWiNode_10_0_x_0__24_remote_apic.json"),
apContainerPol: read_data(dir, "apContainerPol_10_0_0_1__16.json"),
},
+ "5.3(2b)",
script.FAIL_UF,
),
# This scenario is the most likely one where, prior to the upgrade,
@@ -68,6 +84,7 @@
infraWiNode: read_data(dir, "infraWiNode_172_17_0_0__16.json"),
apContainerPol: [],
},
+ "5.3(2b)",
script.FAIL_UF,
),
(
@@ -75,6 +92,7 @@
infraWiNode: read_data(dir, "infraWiNode_172_17_0_0__16.json"),
apContainerPol: read_data(dir, "apContainerPol_172_17_0_1__16.json"),
},
+ "5.3(2b)",
script.FAIL_UF,
),
(
@@ -82,6 +100,7 @@
infraWiNode: read_data(dir, "infraWiNode_172_17_0_0__16.json"),
apContainerPol: read_data(dir, "apContainerPol_172_17_0_10__16.json"),
},
+ "5.3(2b)",
script.FAIL_UF,
),
(
@@ -89,6 +108,7 @@
infraWiNode: read_data(dir, "infraWiNode_172_17_0_0__16.json"),
apContainerPol: read_data(dir, "apContainerPol_172_17_0_1__17.json"),
},
+ "5.3(2b)",
script.FAIL_UF,
),
(
@@ -96,6 +116,7 @@
infraWiNode: read_data(dir, "infraWiNode_172_17_0_0__16.json"),
apContainerPol: read_data(dir, "apContainerPol_172_16_0_1__15.json"),
},
+ "5.3(2b)",
script.FAIL_UF,
),
(
@@ -103,6 +124,7 @@
infraWiNode: read_data(dir, "infraWiNode_172_17_0_0__16.json"),
apContainerPol: read_data(dir, "apContainerPol_172_18_0_1__16.json"),
},
+ "5.3(2b)",
script.PASS,
),
(
@@ -110,6 +132,7 @@
infraWiNode: read_data(dir, "infraWiNode_172_17_x_0__24_remote_apic.json"),
apContainerPol: [],
},
+ "5.3(2b)",
script.FAIL_UF,
),
(
@@ -117,6 +140,7 @@
infraWiNode: read_data(dir, "infraWiNode_172_17_x_0__24_remote_apic.json"),
apContainerPol: read_data(dir, "apContainerPol_172_17_0_1__16.json"),
},
+ "5.3(2b)",
script.FAIL_UF,
),
(
@@ -124,6 +148,7 @@
infraWiNode: read_data(dir, "infraWiNode_172_17_x_0__24_remote_apic.json"),
apContainerPol: read_data(dir, "apContainerPol_172_17_0_10__16.json"),
},
+ "5.3(2b)",
script.FAIL_UF,
),
(
@@ -131,6 +156,7 @@
infraWiNode: read_data(dir, "infraWiNode_172_17_x_0__24_remote_apic.json"),
apContainerPol: read_data(dir, "apContainerPol_172_17_0_1__17.json"),
},
+ "5.3(2b)",
script.FAIL_UF,
),
(
@@ -138,6 +164,7 @@
infraWiNode: read_data(dir, "infraWiNode_172_17_x_0__24_remote_apic.json"),
apContainerPol: read_data(dir, "apContainerPol_172_16_0_1__15.json"),
},
+ "5.3(2b)",
script.FAIL_UF,
),
(
@@ -145,10 +172,13 @@
infraWiNode: read_data(dir, "infraWiNode_172_17_x_0__24_remote_apic.json"),
apContainerPol: read_data(dir, "apContainerPol_172_18_0_1__16.json"),
},
+ "5.3(2b)",
script.PASS,
),
],
)
-def test_logic(mock_icurl, expected_result):
- result = script.docker0_subnet_overlap_check(1, 1)
- assert result == expected_result
+def test_logic(run_check, mock_icurl, cversion, expected_result):
+ result = run_check(
+ cversion=script.AciVersion(cversion),
+ )
+ assert result.result == expected_result
diff --git a/tests/eecdh_cipher_check/commCipher_neg.json b/tests/checks/eecdh_cipher_check/commCipher_neg.json
similarity index 100%
rename from tests/eecdh_cipher_check/commCipher_neg.json
rename to tests/checks/eecdh_cipher_check/commCipher_neg.json
diff --git a/tests/eecdh_cipher_check/commCipher_neg2.json b/tests/checks/eecdh_cipher_check/commCipher_neg2.json
similarity index 100%
rename from tests/eecdh_cipher_check/commCipher_neg2.json
rename to tests/checks/eecdh_cipher_check/commCipher_neg2.json
diff --git a/tests/eecdh_cipher_check/commCipher_pos.json b/tests/checks/eecdh_cipher_check/commCipher_pos.json
similarity index 100%
rename from tests/eecdh_cipher_check/commCipher_pos.json
rename to tests/checks/eecdh_cipher_check/commCipher_pos.json
diff --git a/tests/eecdh_cipher_check/test_eecdh_cipher_check.py b/tests/checks/eecdh_cipher_check/test_eecdh_cipher_check.py
similarity index 80%
rename from tests/eecdh_cipher_check/test_eecdh_cipher_check.py
rename to tests/checks/eecdh_cipher_check/test_eecdh_cipher_check.py
index 297d3c90..f6a19865 100644
--- a/tests/eecdh_cipher_check/test_eecdh_cipher_check.py
+++ b/tests/checks/eecdh_cipher_check/test_eecdh_cipher_check.py
@@ -9,6 +9,7 @@
log = logging.getLogger(__name__)
dir = os.path.dirname(os.path.abspath(__file__))
+test_function = "eecdh_cipher_check"
# icurl queries
commCiphers = 'commCipher.json'
@@ -39,10 +40,8 @@
),
],
)
-def test_logic(mock_icurl, cversion, expected_result):
- result = script.eecdh_cipher_check(
- 1,
- 1,
- script.AciVersion(cversion),
+def test_logic(run_check, mock_icurl, cversion, expected_result):
+ result = run_check(
+ cversion=script.AciVersion(cversion),
)
- assert result == expected_result
\ No newline at end of file
+ assert result.result == expected_result
diff --git a/tests/encap_already_in_use_check/faultInst-encap-pos.json b/tests/checks/encap_already_in_use_check/faultInst-encap-pos.json
similarity index 100%
rename from tests/encap_already_in_use_check/faultInst-encap-pos.json
rename to tests/checks/encap_already_in_use_check/faultInst-encap-pos.json
diff --git a/tests/encap_already_in_use_check/faultInst-new-version.json b/tests/checks/encap_already_in_use_check/faultInst-new-version.json
similarity index 100%
rename from tests/encap_already_in_use_check/faultInst-new-version.json
rename to tests/checks/encap_already_in_use_check/faultInst-new-version.json
diff --git a/tests/encap_already_in_use_check/fvIfConn.json b/tests/checks/encap_already_in_use_check/fvIfConn.json
similarity index 100%
rename from tests/encap_already_in_use_check/fvIfConn.json
rename to tests/checks/encap_already_in_use_check/fvIfConn.json
diff --git a/tests/encap_already_in_use_check/test_encap_already_in_use_check.py b/tests/checks/encap_already_in_use_check/test_encap_already_in_use_check.py
similarity index 86%
rename from tests/encap_already_in_use_check/test_encap_already_in_use_check.py
rename to tests/checks/encap_already_in_use_check/test_encap_already_in_use_check.py
index 426ff4c5..e55645f4 100644
--- a/tests/encap_already_in_use_check/test_encap_already_in_use_check.py
+++ b/tests/checks/encap_already_in_use_check/test_encap_already_in_use_check.py
@@ -9,6 +9,7 @@
log = logging.getLogger(__name__)
dir = os.path.dirname(os.path.abspath(__file__))
+test_function = "encap_already_in_use_check"
# icurl queries
faultInsts = (
@@ -43,6 +44,6 @@
),
],
)
-def test_logic(mock_icurl, expected_result):
- result = script.encap_already_in_use_check(1, 1)
- assert result == expected_result
+def test_logic(run_check, mock_icurl, expected_result):
+ result = run_check()
+ assert result.result == expected_result
diff --git a/tests/equipment_disk_limits_exceeded/faultInst_neg.json b/tests/checks/equipment_disk_limits_exceeded/faultInst_neg.json
similarity index 100%
rename from tests/equipment_disk_limits_exceeded/faultInst_neg.json
rename to tests/checks/equipment_disk_limits_exceeded/faultInst_neg.json
diff --git a/tests/equipment_disk_limits_exceeded/faultInst_pos.json b/tests/checks/equipment_disk_limits_exceeded/faultInst_pos.json
similarity index 100%
rename from tests/equipment_disk_limits_exceeded/faultInst_pos.json
rename to tests/checks/equipment_disk_limits_exceeded/faultInst_pos.json
diff --git a/tests/equipment_disk_limits_exceeded/test_equipment_disk_limits_exceeded.py b/tests/checks/equipment_disk_limits_exceeded/test_equipment_disk_limits_exceeded.py
similarity index 79%
rename from tests/equipment_disk_limits_exceeded/test_equipment_disk_limits_exceeded.py
rename to tests/checks/equipment_disk_limits_exceeded/test_equipment_disk_limits_exceeded.py
index 9399c700..72fd0aa9 100644
--- a/tests/equipment_disk_limits_exceeded/test_equipment_disk_limits_exceeded.py
+++ b/tests/checks/equipment_disk_limits_exceeded/test_equipment_disk_limits_exceeded.py
@@ -9,9 +9,12 @@
log = logging.getLogger(__name__)
dir = os.path.dirname(os.path.abspath(__file__))
+test_function = "equipment_disk_limits_exceeded"
+
f182x_api = 'faultInst.json'
f182x_api += '?query-target-filter=or(eq(faultInst.code,"F1820"),eq(faultInst.code,"F1821"),eq(faultInst.code,"F1822"))'
+
@pytest.mark.parametrize(
"icurl_outputs, expected_result",
[
@@ -25,6 +28,6 @@
)
],
)
-def test_logic(mock_icurl, expected_result):
- result = script.equipment_disk_limits_exceeded(1, 1)
- assert result == expected_result
\ No newline at end of file
+def test_logic(run_check, mock_icurl, expected_result):
+ result = run_check()
+ assert result.result == expected_result
diff --git a/tests/eventmgr_db_defect_check/test_eventmgr_db_defect_check.py b/tests/checks/eventmgr_db_defect_check/test_eventmgr_db_defect_check.py
similarity index 77%
rename from tests/eventmgr_db_defect_check/test_eventmgr_db_defect_check.py
rename to tests/checks/eventmgr_db_defect_check/test_eventmgr_db_defect_check.py
index 4524d602..c4f98ddf 100644
--- a/tests/eventmgr_db_defect_check/test_eventmgr_db_defect_check.py
+++ b/tests/checks/eventmgr_db_defect_check/test_eventmgr_db_defect_check.py
@@ -8,6 +8,8 @@
log = logging.getLogger(__name__)
dir = os.path.dirname(os.path.abspath(__file__))
+test_function = "eventmgr_db_defect_check"
+
@pytest.mark.parametrize(
"cversion, expected_result",
@@ -27,8 +29,8 @@
("5.0(1l)", script.PASS),
],
)
-def test_logic(mock_icurl, cversion, expected_result):
- result = script.eventmgr_db_defect_check(
- 1, 1, script.AciVersion(cversion)
+def test_logic(run_check, mock_icurl, cversion, expected_result):
+ result = run_check(
+ cversion=script.AciVersion(cversion),
)
- assert result == expected_result
+ assert result.result == expected_result
diff --git a/tests/fabricPathEP_target_check/fabricRsOosPath_neg.json b/tests/checks/fabricPathEP_target_check/fabricRsOosPath_neg.json
similarity index 100%
rename from tests/fabricPathEP_target_check/fabricRsOosPath_neg.json
rename to tests/checks/fabricPathEP_target_check/fabricRsOosPath_neg.json
diff --git a/tests/fabricPathEP_target_check/fabricRsOosPath_pos1.json b/tests/checks/fabricPathEP_target_check/fabricRsOosPath_pos1.json
similarity index 100%
rename from tests/fabricPathEP_target_check/fabricRsOosPath_pos1.json
rename to tests/checks/fabricPathEP_target_check/fabricRsOosPath_pos1.json
diff --git a/tests/fabricPathEP_target_check/fabricRsOosPath_pos2.json b/tests/checks/fabricPathEP_target_check/fabricRsOosPath_pos2.json
similarity index 100%
rename from tests/fabricPathEP_target_check/fabricRsOosPath_pos2.json
rename to tests/checks/fabricPathEP_target_check/fabricRsOosPath_pos2.json
diff --git a/tests/fabricPathEP_target_check/fabricRsOosPath_pos3.json b/tests/checks/fabricPathEP_target_check/fabricRsOosPath_pos3.json
similarity index 100%
rename from tests/fabricPathEP_target_check/fabricRsOosPath_pos3.json
rename to tests/checks/fabricPathEP_target_check/fabricRsOosPath_pos3.json
diff --git a/tests/fabricPathEP_target_check/fabricRsOosPath_pos4.json b/tests/checks/fabricPathEP_target_check/fabricRsOosPath_pos4.json
similarity index 100%
rename from tests/fabricPathEP_target_check/fabricRsOosPath_pos4.json
rename to tests/checks/fabricPathEP_target_check/fabricRsOosPath_pos4.json
diff --git a/tests/fabricPathEP_target_check/fabricRsOosPath_pos5.json b/tests/checks/fabricPathEP_target_check/fabricRsOosPath_pos5.json
similarity index 100%
rename from tests/fabricPathEP_target_check/fabricRsOosPath_pos5.json
rename to tests/checks/fabricPathEP_target_check/fabricRsOosPath_pos5.json
diff --git a/tests/fabricPathEP_target_check/fabricRsOosPath_pos6.json b/tests/checks/fabricPathEP_target_check/fabricRsOosPath_pos6.json
similarity index 100%
rename from tests/fabricPathEP_target_check/fabricRsOosPath_pos6.json
rename to tests/checks/fabricPathEP_target_check/fabricRsOosPath_pos6.json
diff --git a/tests/fabricPathEP_target_check/infraRsHPathAtt_neg.json b/tests/checks/fabricPathEP_target_check/infraRsHPathAtt_neg.json
similarity index 100%
rename from tests/fabricPathEP_target_check/infraRsHPathAtt_neg.json
rename to tests/checks/fabricPathEP_target_check/infraRsHPathAtt_neg.json
diff --git a/tests/fabricPathEP_target_check/infraRsHPathAtt_pos1.json b/tests/checks/fabricPathEP_target_check/infraRsHPathAtt_pos1.json
similarity index 100%
rename from tests/fabricPathEP_target_check/infraRsHPathAtt_pos1.json
rename to tests/checks/fabricPathEP_target_check/infraRsHPathAtt_pos1.json
diff --git a/tests/fabricPathEP_target_check/infraRsHPathAtt_pos2.json b/tests/checks/fabricPathEP_target_check/infraRsHPathAtt_pos2.json
similarity index 100%
rename from tests/fabricPathEP_target_check/infraRsHPathAtt_pos2.json
rename to tests/checks/fabricPathEP_target_check/infraRsHPathAtt_pos2.json
diff --git a/tests/fabricPathEP_target_check/infraRsHPathAtt_pos3.json b/tests/checks/fabricPathEP_target_check/infraRsHPathAtt_pos3.json
similarity index 100%
rename from tests/fabricPathEP_target_check/infraRsHPathAtt_pos3.json
rename to tests/checks/fabricPathEP_target_check/infraRsHPathAtt_pos3.json
diff --git a/tests/fabricPathEP_target_check/infraRsHPathAtt_pos4.json b/tests/checks/fabricPathEP_target_check/infraRsHPathAtt_pos4.json
similarity index 100%
rename from tests/fabricPathEP_target_check/infraRsHPathAtt_pos4.json
rename to tests/checks/fabricPathEP_target_check/infraRsHPathAtt_pos4.json
diff --git a/tests/fabricPathEP_target_check/infraRsHPathAtt_pos5.json b/tests/checks/fabricPathEP_target_check/infraRsHPathAtt_pos5.json
similarity index 100%
rename from tests/fabricPathEP_target_check/infraRsHPathAtt_pos5.json
rename to tests/checks/fabricPathEP_target_check/infraRsHPathAtt_pos5.json
diff --git a/tests/fabricPathEP_target_check/infraRsHPathAtt_pos6.json b/tests/checks/fabricPathEP_target_check/infraRsHPathAtt_pos6.json
similarity index 100%
rename from tests/fabricPathEP_target_check/infraRsHPathAtt_pos6.json
rename to tests/checks/fabricPathEP_target_check/infraRsHPathAtt_pos6.json
diff --git a/tests/fabricPathEP_target_check/test_fabricPathEP_target_check.py b/tests/checks/fabricPathEP_target_check/test_fabricPathEP_target_check.py
similarity index 89%
rename from tests/fabricPathEP_target_check/test_fabricPathEP_target_check.py
rename to tests/checks/fabricPathEP_target_check/test_fabricPathEP_target_check.py
index b136d7c6..98fdd4ec 100644
--- a/tests/fabricPathEP_target_check/test_fabricPathEP_target_check.py
+++ b/tests/checks/fabricPathEP_target_check/test_fabricPathEP_target_check.py
@@ -9,10 +9,12 @@
log = logging.getLogger(__name__)
dir = os.path.dirname(os.path.abspath(__file__))
+test_function = "fabricPathEp_target_check"
# icurl queries
-hpath_api = 'infraRsHPathAtt.json'
-oosPorts_api = 'fabricRsOosPath.json'
+hpath_api = 'infraRsHPathAtt.json'
+oosPorts_api = 'fabricRsOosPath.json'
+
@pytest.mark.parametrize(
"icurl_outputs, expected_result",
@@ -62,6 +64,6 @@
],
)
-def test_logic(mock_icurl, expected_result):
- result = script.fabricPathEp_target_check(1, 1)
- assert result == expected_result
+def test_logic(run_check, mock_icurl, expected_result):
+ result = run_check()
+ assert result.result == expected_result
diff --git a/tests/fabric_dpp_check/lbpPol_NEG.json b/tests/checks/fabric_dpp_check/lbpPol_NEG.json
similarity index 100%
rename from tests/fabric_dpp_check/lbpPol_NEG.json
rename to tests/checks/fabric_dpp_check/lbpPol_NEG.json
diff --git a/tests/fabric_dpp_check/lbpPol_POS.json b/tests/checks/fabric_dpp_check/lbpPol_POS.json
similarity index 100%
rename from tests/fabric_dpp_check/lbpPol_POS.json
rename to tests/checks/fabric_dpp_check/lbpPol_POS.json
diff --git a/tests/fabric_dpp_check/test_fabric_dpp_check.py b/tests/checks/fabric_dpp_check/test_fabric_dpp_check.py
similarity index 85%
rename from tests/fabric_dpp_check/test_fabric_dpp_check.py
rename to tests/checks/fabric_dpp_check/test_fabric_dpp_check.py
index 9eacfb7e..6e2fddd2 100644
--- a/tests/fabric_dpp_check/test_fabric_dpp_check.py
+++ b/tests/checks/fabric_dpp_check/test_fabric_dpp_check.py
@@ -9,11 +9,13 @@
log = logging.getLogger(__name__)
dir = os.path.dirname(os.path.abspath(__file__))
+test_function = "fabric_dpp_check"
# icurl queries
-lbpPol = 'lbpPol.json'
+lbpPol = 'lbpPol.json'
lbpPol += '?query-target-filter=eq(lbpPol.pri,"on")'
+
@pytest.mark.parametrize(
"icurl_outputs, tversion, expected_result",
[
@@ -62,8 +64,8 @@
],
)
-def test_logic(mock_icurl, tversion, expected_result):
- result = script.fabric_dpp_check(
- 1, 1, script.AciVersion(tversion)
+def test_logic(run_check, mock_icurl, tversion, expected_result):
+ result = run_check(
+ tversion=script.AciVersion(tversion),
)
- assert result == expected_result
+ assert result.result == expected_result
diff --git a/tests/checks/fabric_link_redundancy_check/fabricNode.json b/tests/checks/fabric_link_redundancy_check/fabricNode.json
new file mode 100644
index 00000000..3dffa268
--- /dev/null
+++ b/tests/checks/fabric_link_redundancy_check/fabricNode.json
@@ -0,0 +1,122 @@
+[
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-1/node-1",
+ "fabricSt": "commissioned",
+ "id": "1",
+ "name": "apic1",
+ "role": "controller",
+ "nodeType": "unspecified"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-1/node-101",
+ "fabricSt": "active",
+ "id": "101",
+ "name": "LF101",
+ "role": "leaf",
+ "nodeType": "unspecified"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-1/node-102",
+ "fabricSt": "active",
+ "id": "102",
+ "name": "LF102",
+ "role": "leaf",
+ "nodeType": "unspecified"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-1/node-103",
+ "fabricSt": "active",
+ "id": "103",
+ "name": "LF103",
+ "role": "leaf",
+ "nodeType": "unspecified"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-1/node-111",
+ "fabricSt": "active",
+ "id": "111",
+ "name": "T2_LF111",
+ "role": "leaf",
+ "nodeType": "tier-2-leaf"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-1/node-112",
+ "fabricSt": "active",
+ "id": "112",
+ "name": "T2_LF112",
+ "role": "leaf",
+ "nodeType": "tier-2-leaf"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-1/node-121",
+ "fabricSt": "active",
+ "id": "121",
+ "name": "RL_LF121",
+ "role": "leaf",
+ "nodeType": "remote-leaf-wan"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-1/node-122",
+ "fabricSt": "active",
+ "id": "122",
+ "name": "RL_LF122",
+ "role": "leaf",
+ "nodeType": "remote-leaf-wan"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-1/node-1001",
+ "fabricSt": "active",
+ "id": "1001",
+ "name": "SP1001",
+ "role": "spine",
+ "nodeType": "unspecified"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-1/node-1002",
+ "fabricSt": "active",
+ "id": "1002",
+ "name": "SP1002",
+ "role": "spine",
+ "nodeType": "unspecified"
+ }
+ }
+ }
+]
diff --git a/tests/fabric_link_redundancy_check/lldpAdjEp_neg.json b/tests/checks/fabric_link_redundancy_check/lldpAdjEp_neg.json
similarity index 100%
rename from tests/fabric_link_redundancy_check/lldpAdjEp_neg.json
rename to tests/checks/fabric_link_redundancy_check/lldpAdjEp_neg.json
diff --git a/tests/fabric_link_redundancy_check/lldpAdjEp_pos_spine_only.json b/tests/checks/fabric_link_redundancy_check/lldpAdjEp_pos_spine_only.json
similarity index 100%
rename from tests/fabric_link_redundancy_check/lldpAdjEp_pos_spine_only.json
rename to tests/checks/fabric_link_redundancy_check/lldpAdjEp_pos_spine_only.json
diff --git a/tests/fabric_link_redundancy_check/lldpAdjEp_pos_spine_t1.json b/tests/checks/fabric_link_redundancy_check/lldpAdjEp_pos_spine_t1.json
similarity index 100%
rename from tests/fabric_link_redundancy_check/lldpAdjEp_pos_spine_t1.json
rename to tests/checks/fabric_link_redundancy_check/lldpAdjEp_pos_spine_t1.json
diff --git a/tests/fabric_link_redundancy_check/lldpAdjEp_pos_t1_only.json b/tests/checks/fabric_link_redundancy_check/lldpAdjEp_pos_t1_only.json
similarity index 100%
rename from tests/fabric_link_redundancy_check/lldpAdjEp_pos_t1_only.json
rename to tests/checks/fabric_link_redundancy_check/lldpAdjEp_pos_t1_only.json
diff --git a/tests/checks/fabric_link_redundancy_check/test_fabric_link_redundancy_check.py b/tests/checks/fabric_link_redundancy_check/test_fabric_link_redundancy_check.py
new file mode 100644
index 00000000..b31f8fe5
--- /dev/null
+++ b/tests/checks/fabric_link_redundancy_check/test_fabric_link_redundancy_check.py
@@ -0,0 +1,67 @@
+import os
+import pytest
+import logging
+import importlib
+from helpers.utils import read_data
+
+script = importlib.import_module("aci-preupgrade-validation-script")
+
+log = logging.getLogger(__name__)
+dir = os.path.dirname(os.path.abspath(__file__))
+
+test_function = "fabric_link_redundancy_check"
+
+# icurl queries
+lldp_adj_api = "lldpAdjEp.json"
+lldp_adj_api += '?query-target-filter=wcard(lldpAdjEp.sysDesc,"topology/pod")'
+
+
+@pytest.mark.parametrize(
+ "icurl_outputs, fabric_nodes, expected_result, expected_data",
+ [
+ # FAILING = T1 leaf101 single-homed, T1 leaf102 none, T1 leaf103 multi-homed
+ (
+ {lldp_adj_api: read_data(dir, "lldpAdjEp_pos_spine_only.json")},
+ read_data(dir, "fabricNode.json"),
+ script.FAIL_O,
+ [
+ ["LF101", "SP1001", "Only one spine adjacency"],
+ ["LF102", "", "No spine adjacency"],
+ ]
+ ),
+ # FAILING = T1 leafs multi-homed, T2 leaf111 single-homed, T2 leaf112 multi-homed
+ (
+ {lldp_adj_api: read_data(dir, "lldpAdjEp_pos_t1_only.json")},
+ read_data(dir, "fabricNode.json"),
+ script.FAIL_O,
+ [
+ ["T2_LF111", "LF102", "Only one tier 1 leaf adjacency"],
+ ]
+ ),
+ # FAILING = T1 leaf101 single-homed, T1 leaf102 none, T1 leaf103 multi-homed
+ # T2 leaf111 single-homed, T2 leaf112 multi-homed
+ (
+ {lldp_adj_api: read_data(dir, "lldpAdjEp_pos_spine_t1.json")},
+ read_data(dir, "fabricNode.json"),
+ script.FAIL_O,
+ [
+ ["LF101", "SP1001", "Only one spine adjacency"],
+ ["LF102", "", "No spine adjacency"],
+ ["T2_LF111", "LF102", "Only one tier 1 leaf adjacency"],
+ ]
+ ),
+ # PASSING = ALL LEAF SWITCHES ARE MULTI-HOMED except for RL
+ (
+ {lldp_adj_api: read_data(dir, "lldpAdjEp_neg.json")},
+ read_data(dir, "fabricNode.json"),
+ script.PASS,
+ [],
+ ),
+ ],
+)
+def test_logic(run_check, mock_icurl, fabric_nodes, expected_result, expected_data):
+ result = run_check(
+ fabric_nodes=fabric_nodes,
+ )
+ assert result.result == expected_result
+ assert sorted(result.data) == sorted(expected_data)
diff --git a/tests/fabric_port_down_check/faultInst_pos.json b/tests/checks/fabric_port_down_check/faultInst_pos.json
similarity index 100%
rename from tests/fabric_port_down_check/faultInst_pos.json
rename to tests/checks/fabric_port_down_check/faultInst_pos.json
diff --git a/tests/fabric_port_down_check/test_fabric_port_down_check.py b/tests/checks/fabric_port_down_check/test_fabric_port_down_check.py
similarity index 78%
rename from tests/fabric_port_down_check/test_fabric_port_down_check.py
rename to tests/checks/fabric_port_down_check/test_fabric_port_down_check.py
index 034e343a..4b132334 100644
--- a/tests/fabric_port_down_check/test_fabric_port_down_check.py
+++ b/tests/checks/fabric_port_down_check/test_fabric_port_down_check.py
@@ -9,9 +9,10 @@
log = logging.getLogger(__name__)
dir = os.path.dirname(os.path.abspath(__file__))
+test_function = "fabric_port_down_check"
# icurl queries
-faultInsts = 'faultInst.json'
+faultInsts = 'faultInst.json'
faultInsts += '?&query-target-filter=and(eq(faultInst.code,"F1394")'
faultInsts += ',eq(faultInst.rule,"ethpm-if-port-down-fabric"))'
@@ -33,6 +34,6 @@
),
],
)
-def test_logic(mock_icurl, expected_result):
- result = script.fabric_port_down_check(1, 1)
- assert result == expected_result
+def test_logic(run_check, mock_icurl, expected_result):
+ result = run_check()
+ assert result.result == expected_result
diff --git a/tests/checks/fabricdomain_name_check/fabricNode.json b/tests/checks/fabricdomain_name_check/fabricNode.json
new file mode 100644
index 00000000..80f5c867
--- /dev/null
+++ b/tests/checks/fabricdomain_name_check/fabricNode.json
@@ -0,0 +1,108 @@
+[
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.1",
+ "dn": "topology/pod-1/node-1",
+ "fabricSt": "commissioned",
+ "id": "1",
+ "model": "APIC-SERVER-L2",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "apic1",
+ "nodeType": "unspecified",
+ "role": "controller"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.2",
+ "dn": "topology/pod-1/node-2",
+ "fabricSt": "commissioned",
+ "id": "2",
+ "model": "APIC-SERVER-L2",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "apic2",
+ "nodeType": "unspecified",
+ "role": "controller"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.3",
+ "dn": "topology/pod-2/node-3",
+ "fabricSt": "commissioned",
+ "id": "3",
+ "model": "APIC-SERVER-L2",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "apic3",
+ "nodeType": "unspecified",
+ "role": "controller"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.101",
+ "dn": "topology/pod-1/node-101",
+ "fabricSt": "active",
+ "id": "101",
+ "model": "N9K-C93180YC-FX",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "leaf101",
+ "nodeType": "unspecified",
+ "role": "leaf"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.102",
+ "dn": "topology/pod-1/node-102",
+ "fabricSt": "active",
+ "id": "102",
+ "model": "N9K-C93108TC-FX",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "leaf102",
+ "nodeType": "unspecified",
+ "role": "leaf"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.103",
+ "dn": "topology/pod-1/node-103",
+ "fabricSt": "active",
+ "id": "103",
+ "model": "N9K-C93180YC-FX3",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "leaf103",
+ "nodeType": "unspecified",
+ "role": "leaf"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.201",
+ "dn": "topology/pod-1/node-201",
+ "fabricSt": "active",
+ "id": "201",
+ "model": "N9K-C9504",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "spine201",
+ "nodeType": "unspecified",
+ "role": "spine"
+ }
+ }
+ }
+]
+
diff --git a/tests/checks/fabricdomain_name_check/fabricNode_no_apic1.json b/tests/checks/fabricdomain_name_check/fabricNode_no_apic1.json
new file mode 100644
index 00000000..fcc76406
--- /dev/null
+++ b/tests/checks/fabricdomain_name_check/fabricNode_no_apic1.json
@@ -0,0 +1,93 @@
+[
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.2",
+ "dn": "topology/pod-1/node-2",
+ "fabricSt": "commissioned",
+ "id": "2",
+ "model": "APIC-SERVER-L2",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "apic2",
+ "nodeType": "unspecified",
+ "role": "controller"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.3",
+ "dn": "topology/pod-2/node-3",
+ "fabricSt": "commissioned",
+ "id": "3",
+ "model": "APIC-SERVER-L2",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "apic3",
+ "nodeType": "unspecified",
+ "role": "controller"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.101",
+ "dn": "topology/pod-1/node-101",
+ "fabricSt": "active",
+ "id": "101",
+ "model": "N9K-C93180YC-FX",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "leaf101",
+ "nodeType": "unspecified",
+ "role": "leaf"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.102",
+ "dn": "topology/pod-1/node-102",
+ "fabricSt": "active",
+ "id": "102",
+ "model": "N9K-C93108TC-FX",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "leaf102",
+ "nodeType": "unspecified",
+ "role": "leaf"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.103",
+ "dn": "topology/pod-1/node-103",
+ "fabricSt": "active",
+ "id": "103",
+ "model": "N9K-C93180YC-FX3",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "leaf103",
+ "nodeType": "unspecified",
+ "role": "leaf"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.201",
+ "dn": "topology/pod-1/node-201",
+ "fabricSt": "active",
+ "id": "201",
+ "model": "N9K-C9504",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "spine201",
+ "nodeType": "unspecified",
+ "role": "spine"
+ }
+ }
+ }
+]
+
diff --git a/tests/checks/fabricdomain_name_check/test_fabricdomain_name_check.py b/tests/checks/fabricdomain_name_check/test_fabricdomain_name_check.py
new file mode 100644
index 00000000..c2881bba
--- /dev/null
+++ b/tests/checks/fabricdomain_name_check/test_fabricdomain_name_check.py
@@ -0,0 +1,126 @@
+import os
+import pytest
+import logging
+import importlib
+from helpers.utils import read_data
+
+script = importlib.import_module("aci-preupgrade-validation-script")
+
+log = logging.getLogger(__name__)
+dir = os.path.dirname(os.path.abspath(__file__))
+
+test_function = "fabricdomain_name_check"
+
+# icurl queries
+topSystem = 'topology/pod-1/node-1/sys.json'
+
+
+@pytest.mark.parametrize(
+ "icurl_outputs, cversion, tversion, fabric_nodes, expected_result, expected_data",
+ [
+ # tversion missing
+ (
+ {topSystem: read_data(dir, "topSystem_1POS.json")},
+ "5.2(3g)",
+ None,
+ read_data(dir, "fabricNode.json"),
+ script.MANUAL,
+ [],
+ ),
+ # APIC 1 missing in fabric_nodes (fabricNode)
+ (
+ {topSystem: read_data(dir, "topSystem_1POS.json")},
+ "5.2(3g)",
+ "6.0(2h)",
+ read_data(dir, "fabricNode_no_apic1.json"),
+ script.ERROR,
+ [],
+ ),
+ # APIC 1 missing in topSystem
+ (
+ {topSystem: []},
+ "5.2(3g)",
+ "6.0(2h)",
+ read_data(dir, "fabricNode.json"),
+ script.ERROR,
+ [],
+ ),
+ # `;` char test
+ (
+ {topSystem: read_data(dir, "topSystem_1POS.json")},
+ "5.2(3g)",
+ "6.0(2h)",
+ read_data(dir, "fabricNode.json"),
+ script.FAIL_O,
+ [["fabric;4", "Contains a special character"]]
+ ),
+ (
+ {topSystem: read_data(dir, "topSystem_1POS.json")},
+ "6.0(3a)",
+ "6.0(2h)",
+ read_data(dir, "fabricNode.json"),
+ script.FAIL_O,
+ [["fabric;4", "Contains a special character"]]
+ ),
+ # `#` char test
+ (
+ {topSystem: read_data(dir, "topSystem_2POS.json")},
+ "5.2(3g)",
+ "6.0(2h)",
+ read_data(dir, "fabricNode.json"),
+ script.FAIL_O,
+ [["fabric#4", "Contains a special character"]]
+ ),
+ (
+ {topSystem: read_data(dir, "topSystem_2POS.json")},
+ "6.0(3a)",
+ "6.0(2h)",
+ read_data(dir, "fabricNode.json"),
+ script.FAIL_O,
+ [["fabric#4", "Contains a special character"]]
+ ),
+ # Neither ; or # in fabricDomain
+ (
+ {topSystem: read_data(dir, "topSystem_NEG.json")},
+ "5.2(3g)",
+ "6.0(2h)",
+ read_data(dir, "fabricNode.json"),
+ script.PASS,
+ [],
+ ),
+ # only affected 6.0(2h), regardless of special chars
+ (
+ {topSystem: read_data(dir, "topSystem_1POS.json")},
+ "5.2(3g)",
+ "6.0(1j)",
+ read_data(dir, "fabricNode.json"),
+ script.PASS,
+ [],
+ ),
+ # Eventual 6.0(3) has fix
+ (
+ {topSystem: read_data(dir, "topSystem_1POS.json")},
+ "5.2(3g)",
+ "6.0(3a)",
+ read_data(dir, "fabricNode.json"),
+ script.PASS,
+ [],
+ ),
+ (
+ {topSystem: read_data(dir, "topSystem_1POS.json")},
+ "6.0(3a)",
+ "6.0(4a)",
+ read_data(dir, "fabricNode.json"),
+ script.PASS,
+ [],
+ ),
+ ],
+)
+def test_logic(run_check, mock_icurl, cversion, tversion, fabric_nodes, expected_result, expected_data):
+ result = run_check(
+ cversion=script.AciVersion(cversion),
+ tversion=script.AciVersion(tversion) if tversion else None,
+ fabric_nodes=fabric_nodes,
+ )
+ assert result.result == expected_result
+ assert result.data == expected_data
diff --git a/tests/checks/fabricdomain_name_check/topSystem_1POS.json b/tests/checks/fabricdomain_name_check/topSystem_1POS.json
new file mode 100644
index 00000000..3bd73dfb
--- /dev/null
+++ b/tests/checks/fabricdomain_name_check/topSystem_1POS.json
@@ -0,0 +1,13 @@
+[
+ {
+ "topSystem": {
+ "attributes": {
+ "address": "10.0.0.1",
+ "fabricId": "1",
+ "id": "1",
+ "fabricDomain": "fabric;4",
+ "role": "controller"
+ }
+ }
+ }
+]
diff --git a/tests/checks/fabricdomain_name_check/topSystem_2POS.json b/tests/checks/fabricdomain_name_check/topSystem_2POS.json
new file mode 100644
index 00000000..175bdd80
--- /dev/null
+++ b/tests/checks/fabricdomain_name_check/topSystem_2POS.json
@@ -0,0 +1,13 @@
+[
+ {
+ "topSystem": {
+ "attributes": {
+ "address": "10.0.0.1",
+ "fabricId": "1",
+ "id": "1",
+ "fabricDomain": "fabric#4",
+ "role": "controller"
+ }
+ }
+ }
+]
diff --git a/tests/checks/fabricdomain_name_check/topSystem_NEG.json b/tests/checks/fabricdomain_name_check/topSystem_NEG.json
new file mode 100644
index 00000000..8e6d3ba6
--- /dev/null
+++ b/tests/checks/fabricdomain_name_check/topSystem_NEG.json
@@ -0,0 +1,13 @@
+[
+ {
+ "topSystem": {
+ "attributes": {
+ "address": "10.0.0.1",
+ "fabricId": "1",
+ "id": "1",
+ "fabricDomain": "fabric4",
+ "role": "controller"
+ }
+ }
+ }
+]
diff --git a/tests/checks/fc_ex_model_check/fabricNode_NEG.json b/tests/checks/fc_ex_model_check/fabricNode_NEG.json
new file mode 100644
index 00000000..9b11d6b4
--- /dev/null
+++ b/tests/checks/fc_ex_model_check/fabricNode_NEG.json
@@ -0,0 +1,107 @@
+[
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.1",
+ "dn": "topology/pod-1/node-1",
+ "fabricSt": "commissioned",
+ "id": "1",
+ "model": "APIC-SERVER-L2",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "apic1",
+ "nodeType": "unspecified",
+ "role": "controller"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.2",
+ "dn": "topology/pod-1/node-2",
+ "fabricSt": "commissioned",
+ "id": "2",
+ "model": "APIC-SERVER-L2",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "apic2",
+ "nodeType": "unspecified",
+ "role": "controller"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.3",
+ "dn": "topology/pod-2/node-3",
+ "fabricSt": "commissioned",
+ "id": "3",
+ "model": "APIC-SERVER-L2",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "apic3",
+ "nodeType": "unspecified",
+ "role": "controller"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.101",
+ "dn": "topology/pod-1/node-101",
+ "fabricSt": "active",
+ "id": "101",
+ "model": "N9K-C93180YC-FX",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "leaf101",
+ "nodeType": "unspecified",
+ "role": "leaf"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.102",
+ "dn": "topology/pod-1/node-102",
+ "fabricSt": "active",
+ "id": "102",
+ "model": "N9K-C93108TC-FX",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "leaf102",
+ "nodeType": "unspecified",
+ "role": "leaf"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.103",
+ "dn": "topology/pod-1/node-103",
+ "fabricSt": "active",
+ "id": "103",
+ "model": "N9K-C93180YC-FX3",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "leaf103",
+ "nodeType": "unspecified",
+ "role": "leaf"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.201",
+ "dn": "topology/pod-1/node-201",
+ "fabricSt": "active",
+ "id": "201",
+ "model": "N9K-C9504",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "spine201",
+ "nodeType": "unspecified",
+ "role": "spine"
+ }
+ }
+ }
+]
diff --git a/tests/checks/fc_ex_model_check/fabricNode_POS.json b/tests/checks/fc_ex_model_check/fabricNode_POS.json
new file mode 100644
index 00000000..d0b31928
--- /dev/null
+++ b/tests/checks/fc_ex_model_check/fabricNode_POS.json
@@ -0,0 +1,122 @@
+[
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.1",
+ "dn": "topology/pod-1/node-1",
+ "fabricSt": "commissioned",
+ "id": "1",
+ "model": "APIC-SERVER-L2",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "apic1",
+ "nodeType": "unspecified",
+ "role": "controller"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.2",
+ "dn": "topology/pod-1/node-2",
+ "fabricSt": "commissioned",
+ "id": "2",
+ "model": "APIC-SERVER-L2",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "apic2",
+ "nodeType": "unspecified",
+ "role": "controller"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.3",
+ "dn": "topology/pod-2/node-3",
+ "fabricSt": "commissioned",
+ "id": "3",
+ "model": "APIC-SERVER-L2",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "apic3",
+ "nodeType": "unspecified",
+ "role": "controller"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.101",
+ "dn": "topology/pod-1/node-101",
+ "fabricSt": "active",
+ "id": "101",
+ "model": "N9K-C93180YC-EX",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "leaf101",
+ "nodeType": "unspecified",
+ "role": "leaf"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.102",
+ "dn": "topology/pod-1/node-102",
+ "fabricSt": "active",
+ "id": "102",
+ "model": "N9K-C93108TC-EX",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "leaf102",
+ "nodeType": "unspecified",
+ "role": "leaf"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.103",
+ "dn": "topology/pod-1/node-103",
+ "fabricSt": "active",
+ "id": "103",
+ "model": "N9K-C93108LC-EX",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "leaf103",
+ "nodeType": "unspecified",
+ "role": "leaf"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.104",
+ "dn": "topology/pod-1/node-104",
+ "fabricSt": "active",
+ "id": "104",
+ "model": "N9K-C93180YC-FX",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "leaf104",
+ "nodeType": "unspecified",
+ "role": "leaf"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.201",
+ "dn": "topology/pod-1/node-201",
+ "fabricSt": "active",
+ "id": "201",
+ "model": "N9K-C9504",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "spine201",
+ "nodeType": "unspecified",
+ "role": "spine"
+ }
+ }
+ }
+]
diff --git a/tests/checks/fc_ex_model_check/fcEntity_101_102.json b/tests/checks/fc_ex_model_check/fcEntity_101_102.json
new file mode 100644
index 00000000..8f052ce3
--- /dev/null
+++ b/tests/checks/fc_ex_model_check/fcEntity_101_102.json
@@ -0,0 +1,18 @@
+[
+ {
+ "fcEntity": {
+ "attributes": {
+ "adminSt": "enabled",
+ "dn": "topology/pod-1/node-102/sys/fc"
+ }
+ }
+ },
+ {
+ "fcEntity": {
+ "attributes": {
+ "adminSt": "enabled",
+ "dn": "topology/pod-1/node-101/sys/fc"
+ }
+ }
+ }
+]
diff --git a/tests/checks/fc_ex_model_check/fcEntity_101_102_103.json b/tests/checks/fc_ex_model_check/fcEntity_101_102_103.json
new file mode 100644
index 00000000..9da46636
--- /dev/null
+++ b/tests/checks/fc_ex_model_check/fcEntity_101_102_103.json
@@ -0,0 +1,26 @@
+[
+ {
+ "fcEntity": {
+ "attributes": {
+ "adminSt": "enabled",
+ "dn": "topology/pod-1/node-102/sys/fc"
+ }
+ }
+ },
+ {
+ "fcEntity": {
+ "attributes": {
+ "adminSt": "enabled",
+ "dn": "topology/pod-1/node-101/sys/fc"
+ }
+ }
+ },
+ {
+ "fcEntity": {
+ "attributes": {
+ "adminSt": "enabled",
+ "dn": "topology/pod-1/node-103/sys/fc"
+ }
+ }
+ }
+]
diff --git a/tests/checks/fc_ex_model_check/fcEntity_104.json b/tests/checks/fc_ex_model_check/fcEntity_104.json
new file mode 100644
index 00000000..eed71689
--- /dev/null
+++ b/tests/checks/fc_ex_model_check/fcEntity_104.json
@@ -0,0 +1,10 @@
+[
+ {
+ "fcEntity": {
+ "attributes": {
+ "adminSt": "enabled",
+ "dn": "topology/pod-1/node-104/sys/fc"
+ }
+ }
+ }
+]
diff --git a/tests/checks/fc_ex_model_check/test_fc_ex_model_check.py b/tests/checks/fc_ex_model_check/test_fc_ex_model_check.py
new file mode 100644
index 00000000..88e421c2
--- /dev/null
+++ b/tests/checks/fc_ex_model_check/test_fc_ex_model_check.py
@@ -0,0 +1,140 @@
+import os
+import pytest
+import logging
+import importlib
+from helpers.utils import read_data
+
+script = importlib.import_module("aci-preupgrade-validation-script")
+
+log = logging.getLogger(__name__)
+dir = os.path.dirname(os.path.abspath(__file__))
+
+test_function = "fc_ex_model_check"
+
+# icurl queries
+fcEntity_api = "fcEntity.json"
+
+
+@pytest.mark.parametrize(
+ "icurl_outputs, tversion, fabric_nodes, expected_result, expected_data",
+ [
+ # TVERSION MISSING
+ (
+ {fcEntity_api: read_data(dir, "fcEntity_101_102_103.json")},
+ None,
+ read_data(dir, "fabricNode_POS.json"),
+ script.MANUAL,
+ [],
+ ),
+ # TVERSION NOT AFFECTED
+ (
+ {fcEntity_api: read_data(dir, "fcEntity_101_102_103.json")},
+ "6.0(1f)",
+ read_data(dir, "fabricNode_POS.json"),
+ script.PASS,
+ [],
+ ),
+ (
+ {fcEntity_api: read_data(dir, "fcEntity_101_102_103.json")},
+ "6.0(9f)",
+ read_data(dir, "fabricNode_POS.json"),
+ script.PASS,
+ [],
+ ),
+ (
+ {fcEntity_api: read_data(dir, "fcEntity_101_102_103.json")},
+ "6.1(4h)",
+ read_data(dir, "fabricNode_POS.json"),
+ script.PASS,
+ [],
+ ),
+ # FABRIC HAS EX NODES and ALL OF THEM HAVE FC/FCOE CONFIG
+ (
+ {fcEntity_api: read_data(dir, "fcEntity_101_102_103.json")},
+ "6.1(1f)",
+ read_data(dir, "fabricNode_POS.json"),
+ script.FAIL_O,
+ [
+ ["topology/pod-1/node-101", "N9K-C93180YC-EX"],
+ ["topology/pod-1/node-102", "N9K-C93108TC-EX"],
+ ["topology/pod-1/node-103", "N9K-C93108LC-EX"],
+ ],
+ ),
+ (
+ {fcEntity_api: read_data(dir, "fcEntity_101_102_103.json")},
+ "6.0(7e)",
+ read_data(dir, "fabricNode_POS.json"),
+ script.FAIL_O,
+ [
+ ["topology/pod-1/node-101", "N9K-C93180YC-EX"],
+ ["topology/pod-1/node-102", "N9K-C93108TC-EX"],
+ ["topology/pod-1/node-103", "N9K-C93108LC-EX"],
+ ],
+ ),
+ # FABRIC HAS EX NODES and SOME OF THEM HAVE FC/FCOE CONFIG
+ (
+ {fcEntity_api: read_data(dir, "fcEntity_101_102.json")},
+ "6.1(1f)",
+ read_data(dir, "fabricNode_POS.json"),
+ script.FAIL_O,
+ [
+ ["topology/pod-1/node-101", "N9K-C93180YC-EX"],
+ ["topology/pod-1/node-102", "N9K-C93108TC-EX"],
+ ],
+ ),
+ (
+ {fcEntity_api: read_data(dir, "fcEntity_101_102.json")},
+ "6.0(7e)",
+ read_data(dir, "fabricNode_POS.json"),
+ script.FAIL_O,
+ [
+ ["topology/pod-1/node-101", "N9K-C93180YC-EX"],
+ ["topology/pod-1/node-102", "N9K-C93108TC-EX"],
+ ],
+ ),
+ # FABRIC HAS EX NODES and NONE OF THEM HAVE FC/FCOE CONFIG
+ (
+ {fcEntity_api: []},
+ "6.0(7e)",
+ read_data(dir, "fabricNode_POS.json"),
+ script.PASS,
+ [],
+ ),
+ (
+ {fcEntity_api: read_data(dir, "fcEntity_104.json")},
+ "6.0(7e)",
+ read_data(dir, "fabricNode_POS.json"),
+ script.PASS,
+ [],
+ ),
+ # FABRIC DOES NOT HAVE EX NODES
+ (
+ {fcEntity_api: []},
+ "6.0(7e)",
+ read_data(dir, "fabricNode_NEG.json"),
+ script.PASS,
+ [],
+ ),
+ (
+ {fcEntity_api: read_data(dir, "fcEntity_101_102_103.json")},
+ "6.0(7e)",
+ read_data(dir, "fabricNode_NEG.json"),
+ script.PASS,
+ [],
+ ),
+ (
+ {fcEntity_api: read_data(dir, "fcEntity_104.json")},
+ "6.0(7e)",
+ read_data(dir, "fabricNode_NEG.json"),
+ script.PASS,
+ [],
+ ),
+ ],
+)
+def test_logic(run_check, mock_icurl, tversion, fabric_nodes, expected_result, expected_data):
+ result = run_check(
+ tversion=script.AciVersion(tversion) if tversion else None,
+ fabric_nodes=fabric_nodes,
+ )
+ assert result.result == expected_result
+ assert result.data == expected_data
diff --git a/tests/checks/gen1_switch_compatibility_check/fabricNode_no_gen1.json b/tests/checks/gen1_switch_compatibility_check/fabricNode_no_gen1.json
new file mode 100644
index 00000000..d266e8af
--- /dev/null
+++ b/tests/checks/gen1_switch_compatibility_check/fabricNode_no_gen1.json
@@ -0,0 +1,30 @@
+[
+ {
+ "fabricNode": {
+ "attributes": {
+ "adSt": "on",
+ "dn": "topology/pod-2/node-201",
+ "fabricSt": "active",
+ "id": "201",
+ "model": "N9K-C93180YC-FX",
+ "name": "RL201",
+ "nodeType": "remote-leaf-wan",
+ "role": "leaf"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "adSt": "on",
+ "dn": "topology/pod-2/node-202",
+ "fabricSt": "active",
+ "id": "202",
+ "model": "N9K-C93180YC-FX",
+ "name": "RL202",
+ "nodeType": "remote-leaf-wan",
+ "role": "leaf"
+ }
+ }
+ }
+]
diff --git a/tests/checks/gen1_switch_compatibility_check/fabricNode_with_gen1.json b/tests/checks/gen1_switch_compatibility_check/fabricNode_with_gen1.json
new file mode 100644
index 00000000..e01273b6
--- /dev/null
+++ b/tests/checks/gen1_switch_compatibility_check/fabricNode_with_gen1.json
@@ -0,0 +1,44 @@
+[
+ {
+ "fabricNode": {
+ "attributes": {
+ "adSt": "on",
+ "dn": "topology/pod-1/node-101",
+ "fabricSt": "active",
+ "id": "101",
+ "model": "N9K-C9372TX-E",
+ "name": "Leaf-101",
+ "nodeType": "unspecified",
+ "role": "leaf"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "adSt": "on",
+ "dn": "topology/pod-1/node-102",
+ "fabricSt": "active",
+ "id": "102",
+ "model": "N9K-C9372TX-E",
+ "name": "Leaf-102",
+ "nodeType": "unspecified",
+ "role": "leaf"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "adSt": "on",
+ "dn": "topology/pod-1/node-1001",
+ "fabricSt": "active",
+ "id": "1001",
+ "model": "N9K-C9332PQ",
+ "name": "Spine-1001",
+ "nodeType": "unspecified",
+ "role": "spine"
+ }
+ }
+ }
+]
diff --git a/tests/checks/gen1_switch_compatibility_check/test_gen1_switch_compatibility_check.py b/tests/checks/gen1_switch_compatibility_check/test_gen1_switch_compatibility_check.py
new file mode 100644
index 00000000..4f076c56
--- /dev/null
+++ b/tests/checks/gen1_switch_compatibility_check/test_gen1_switch_compatibility_check.py
@@ -0,0 +1,42 @@
+import os
+import pytest
+import logging
+import importlib
+from helpers.utils import read_data
+
+log = logging.getLogger(__name__)
+dir = os.path.dirname(os.path.abspath(__file__))
+
+script = importlib.import_module("aci-preupgrade-validation-script")
+AciVersion = script.AciVersion
+
+test_function = "gen1_switch_compatibility_check"
+
+
+@pytest.mark.parametrize(
+ "tversion, fabric_nodes, expected_result, expected_data",
+ [
+ # FAIL - gen1 HW does not support t_ver
+ (
+ "5.2(3b)",
+ read_data(dir, "fabricNode_with_gen1.json"),
+ script.FAIL_UF,
+ [
+ ["5.2(3b)", "101", "N9K-C9372TX-E", "Not supported on 5.x+"],
+ ["5.2(3b)", "102", "N9K-C9372TX-E", "Not supported on 5.x+"],
+ ["5.2(3b)", "1001", "N9K-C9332PQ", "Not supported on 5.x+"],
+ ],
+ ),
+ # PASS - gen1 HW supports t_ver
+ ("4.2(7r)", read_data(dir, "fabricNode_with_gen1.json"), script.PASS, []),
+ # PASS - no gen1 hw found
+ ("5.2(3b)", read_data(dir, "fabricNode_no_gen1.json"), script.PASS, []),
+ ],
+)
+def test_logic(run_check, tversion, fabric_nodes, expected_result, expected_data):
+ result = run_check(
+ tversion=AciVersion(tversion) if tversion else None,
+ fabric_nodes=fabric_nodes,
+ )
+ assert result.result == expected_result
+ assert result.data == expected_data
diff --git a/tests/helpers/__init__.py b/tests/checks/helpers/__init__.py
similarity index 100%
rename from tests/helpers/__init__.py
rename to tests/checks/helpers/__init__.py
diff --git a/tests/helpers/utils.py b/tests/checks/helpers/utils.py
similarity index 68%
rename from tests/helpers/utils.py
rename to tests/checks/helpers/utils.py
index 98fa913a..10236f13 100644
--- a/tests/helpers/utils.py
+++ b/tests/checks/helpers/utils.py
@@ -3,7 +3,7 @@
def read_data(dir, json_file):
- data_path = os.path.join("tests", dir, json_file)
+ data_path = os.path.join("tests", "checks", dir, json_file)
with open(data_path, "r") as file:
data = json.load(file)
return data
diff --git a/tests/https_throttle_rate_check/commHttps_neg1.json b/tests/checks/https_throttle_rate_check/commHttps_neg1.json
similarity index 100%
rename from tests/https_throttle_rate_check/commHttps_neg1.json
rename to tests/checks/https_throttle_rate_check/commHttps_neg1.json
diff --git a/tests/https_throttle_rate_check/commHttps_neg2.json b/tests/checks/https_throttle_rate_check/commHttps_neg2.json
similarity index 100%
rename from tests/https_throttle_rate_check/commHttps_neg2.json
rename to tests/checks/https_throttle_rate_check/commHttps_neg2.json
diff --git a/tests/https_throttle_rate_check/commHttps_pos.json b/tests/checks/https_throttle_rate_check/commHttps_pos.json
similarity index 100%
rename from tests/https_throttle_rate_check/commHttps_pos.json
rename to tests/checks/https_throttle_rate_check/commHttps_pos.json
diff --git a/tests/https_throttle_rate_check/test_https_throttle_rate_check.py b/tests/checks/https_throttle_rate_check/test_https_throttle_rate_check.py
similarity index 88%
rename from tests/https_throttle_rate_check/test_https_throttle_rate_check.py
rename to tests/checks/https_throttle_rate_check/test_https_throttle_rate_check.py
index f79edf26..7f2f9a68 100644
--- a/tests/https_throttle_rate_check/test_https_throttle_rate_check.py
+++ b/tests/checks/https_throttle_rate_check/test_https_throttle_rate_check.py
@@ -9,6 +9,7 @@
log = logging.getLogger(__name__)
dir = os.path.dirname(os.path.abspath(__file__))
+test_function = "https_throttle_rate_check"
# icurl queries
commHttps = "commHttps.json"
@@ -75,8 +76,9 @@
),
],
)
-def test_logic(mock_icurl, cver, tver, expected_result):
- cversion = script.AciVersion(cver)
- tversion = script.AciVersion(tver) if tver else None
- result = script.https_throttle_rate_check(1, 1, cversion, tversion)
- assert result == expected_result
+def test_logic(run_check, mock_icurl, cver, tver, expected_result):
+ result = run_check(
+ cversion=script.AciVersion(cver),
+ tversion=script.AciVersion(tver) if tver else None,
+ )
+ assert result.result == expected_result
diff --git a/tests/internal_vlanpool_check/fvnsVlanInstP_neg.json b/tests/checks/internal_vlanpool_check/fvnsVlanInstP_neg.json
similarity index 100%
rename from tests/internal_vlanpool_check/fvnsVlanInstP_neg.json
rename to tests/checks/internal_vlanpool_check/fvnsVlanInstP_neg.json
diff --git a/tests/internal_vlanpool_check/fvnsVlanInstP_pos.json b/tests/checks/internal_vlanpool_check/fvnsVlanInstP_pos.json
similarity index 100%
rename from tests/internal_vlanpool_check/fvnsVlanInstP_pos.json
rename to tests/checks/internal_vlanpool_check/fvnsVlanInstP_pos.json
diff --git a/tests/internal_vlanpool_check/test_internal_vlanpool_check.py b/tests/checks/internal_vlanpool_check/test_internal_vlanpool_check.py
similarity index 93%
rename from tests/internal_vlanpool_check/test_internal_vlanpool_check.py
rename to tests/checks/internal_vlanpool_check/test_internal_vlanpool_check.py
index 6d16279b..704b3350 100644
--- a/tests/internal_vlanpool_check/test_internal_vlanpool_check.py
+++ b/tests/checks/internal_vlanpool_check/test_internal_vlanpool_check.py
@@ -9,6 +9,7 @@
log = logging.getLogger(__name__)
dir = os.path.dirname(os.path.abspath(__file__))
+test_function = "internal_vlanpool_check"
# icurl queries
fvnsVlanInstPs = "fvnsVlanInstP.json?rsp-subtree=children&rsp-subtree-class=fvnsRtVlanNs,fvnsEncapBlk&rsp-subtree-include=required"
@@ -100,6 +101,6 @@
),
],
)
-def test_logic(mock_icurl, tversion, expected_result):
- result = script.internal_vlanpool_check(1, 1, script.AciVersion(tversion))
- assert result == expected_result
+def test_logic(run_check, mock_icurl, tversion, expected_result):
+ result = run_check(tversion=script.AciVersion(tversion))
+ assert result.result == expected_result
diff --git a/tests/internal_vlanpool_check/vmmDomP_neg.json b/tests/checks/internal_vlanpool_check/vmmDomP_neg.json
similarity index 100%
rename from tests/internal_vlanpool_check/vmmDomP_neg.json
rename to tests/checks/internal_vlanpool_check/vmmDomP_neg.json
diff --git a/tests/internal_vlanpool_check/vmmDomP_pos.json b/tests/checks/internal_vlanpool_check/vmmDomP_pos.json
similarity index 100%
rename from tests/internal_vlanpool_check/vmmDomP_pos.json
rename to tests/checks/internal_vlanpool_check/vmmDomP_pos.json
diff --git a/tests/isis_database_byte_check/isisDTEp_NEG.json b/tests/checks/isis_database_byte_check/isisDTEp_NEG.json
similarity index 100%
rename from tests/isis_database_byte_check/isisDTEp_NEG.json
rename to tests/checks/isis_database_byte_check/isisDTEp_NEG.json
diff --git a/tests/isis_database_byte_check/isisDTEp_POS.json b/tests/checks/isis_database_byte_check/isisDTEp_POS.json
similarity index 100%
rename from tests/isis_database_byte_check/isisDTEp_POS.json
rename to tests/checks/isis_database_byte_check/isisDTEp_POS.json
diff --git a/tests/isis_database_byte_check/test_isis_database_byte_check.py b/tests/checks/isis_database_byte_check/test_isis_database_byte_check.py
similarity index 88%
rename from tests/isis_database_byte_check/test_isis_database_byte_check.py
rename to tests/checks/isis_database_byte_check/test_isis_database_byte_check.py
index 39003bab..538e132e 100644
--- a/tests/isis_database_byte_check/test_isis_database_byte_check.py
+++ b/tests/checks/isis_database_byte_check/test_isis_database_byte_check.py
@@ -9,10 +9,13 @@
log = logging.getLogger(__name__)
dir = os.path.dirname(os.path.abspath(__file__))
+test_function = "isis_database_byte_check"
+
# icurl queries
isisDTEp_api = 'isisDTEp.json'
isisDTEp_api += '?query-target-filter=eq(isisDTEp.role,"spine")'
+
@pytest.mark.parametrize(
"icurl_outputs, tversion, expected_result",
[
@@ -75,7 +78,8 @@
)
]
)
-def test_logic(mock_icurl, tversion, expected_result):
- tversion = script.AciVersion(tversion) if tversion else None
- result = script.isis_database_byte_check(1, 1, tversion)
- assert result == expected_result
+def test_logic(run_check, mock_icurl, tversion, expected_result):
+ result = run_check(
+ tversion=script.AciVersion(tversion) if tversion else None,
+ )
+ assert result.result == expected_result
diff --git a/tests/isis_redis_metric_mpod_msite_check/fvFabricExtConnP_pos1.json b/tests/checks/isis_redis_metric_mpod_msite_check/fvFabricExtConnP_pos1.json
similarity index 100%
rename from tests/isis_redis_metric_mpod_msite_check/fvFabricExtConnP_pos1.json
rename to tests/checks/isis_redis_metric_mpod_msite_check/fvFabricExtConnP_pos1.json
diff --git a/tests/isis_redis_metric_mpod_msite_check/fvFabricExtConnP_pos2.json b/tests/checks/isis_redis_metric_mpod_msite_check/fvFabricExtConnP_pos2.json
similarity index 100%
rename from tests/isis_redis_metric_mpod_msite_check/fvFabricExtConnP_pos2.json
rename to tests/checks/isis_redis_metric_mpod_msite_check/fvFabricExtConnP_pos2.json
diff --git a/tests/isis_redis_metric_mpod_msite_check/fvFabricExtConnP_pos3.json b/tests/checks/isis_redis_metric_mpod_msite_check/fvFabricExtConnP_pos3.json
similarity index 100%
rename from tests/isis_redis_metric_mpod_msite_check/fvFabricExtConnP_pos3.json
rename to tests/checks/isis_redis_metric_mpod_msite_check/fvFabricExtConnP_pos3.json
diff --git a/tests/isis_redis_metric_mpod_msite_check/isisDomP-default_missing.json b/tests/checks/isis_redis_metric_mpod_msite_check/isisDomP-default_missing.json
similarity index 100%
rename from tests/isis_redis_metric_mpod_msite_check/isisDomP-default_missing.json
rename to tests/checks/isis_redis_metric_mpod_msite_check/isisDomP-default_missing.json
diff --git a/tests/isis_redis_metric_mpod_msite_check/isisDomP-default_neg.json b/tests/checks/isis_redis_metric_mpod_msite_check/isisDomP-default_neg.json
similarity index 100%
rename from tests/isis_redis_metric_mpod_msite_check/isisDomP-default_neg.json
rename to tests/checks/isis_redis_metric_mpod_msite_check/isisDomP-default_neg.json
diff --git a/tests/isis_redis_metric_mpod_msite_check/isisDomP-default_pos.json b/tests/checks/isis_redis_metric_mpod_msite_check/isisDomP-default_pos.json
similarity index 100%
rename from tests/isis_redis_metric_mpod_msite_check/isisDomP-default_pos.json
rename to tests/checks/isis_redis_metric_mpod_msite_check/isisDomP-default_pos.json
diff --git a/tests/isis_redis_metric_mpod_msite_check/test_isis_redis_metric_mpod_msite_check.py b/tests/checks/isis_redis_metric_mpod_msite_check/test_isis_redis_metric_mpod_msite_check.py
similarity index 90%
rename from tests/isis_redis_metric_mpod_msite_check/test_isis_redis_metric_mpod_msite_check.py
rename to tests/checks/isis_redis_metric_mpod_msite_check/test_isis_redis_metric_mpod_msite_check.py
index 153c248f..a159de8a 100644
--- a/tests/isis_redis_metric_mpod_msite_check/test_isis_redis_metric_mpod_msite_check.py
+++ b/tests/checks/isis_redis_metric_mpod_msite_check/test_isis_redis_metric_mpod_msite_check.py
@@ -9,6 +9,7 @@
log = logging.getLogger(__name__)
dir = os.path.dirname(os.path.abspath(__file__))
+test_function = "isis_redis_metric_mpod_msite_check"
# icurl queries
isisDomPs = "uni/fabric/isisDomP-default.json"
@@ -55,6 +56,6 @@
),
],
)
-def test_logic(mock_icurl, expected_result):
- result = script.isis_redis_metric_mpod_msite_check(1, 1)
- assert result == expected_result
+def test_logic(run_check, mock_icurl, expected_result):
+ result = run_check()
+ assert result.result == expected_result
diff --git a/tests/l3out_mtu_check/l2pol-default.json b/tests/checks/l3out_mtu_check/l2pol-default.json
similarity index 100%
rename from tests/l3out_mtu_check/l2pol-default.json
rename to tests/checks/l3out_mtu_check/l2pol-default.json
diff --git a/tests/l3out_mtu_check/l3extRsPathL3OutAtt.json b/tests/checks/l3out_mtu_check/l3extRsPathL3OutAtt.json
similarity index 100%
rename from tests/l3out_mtu_check/l3extRsPathL3OutAtt.json
rename to tests/checks/l3out_mtu_check/l3extRsPathL3OutAtt.json
diff --git a/tests/l3out_mtu_check/l3extVirtualLIfP.json b/tests/checks/l3out_mtu_check/l3extVirtualLIfP.json
similarity index 100%
rename from tests/l3out_mtu_check/l3extVirtualLIfP.json
rename to tests/checks/l3out_mtu_check/l3extVirtualLIfP.json
diff --git a/tests/l3out_mtu_check/l3extVirtualLIfP_unresolved.json b/tests/checks/l3out_mtu_check/l3extVirtualLIfP_unresolved.json
similarity index 100%
rename from tests/l3out_mtu_check/l3extVirtualLIfP_unresolved.json
rename to tests/checks/l3out_mtu_check/l3extVirtualLIfP_unresolved.json
diff --git a/tests/l3out_mtu_check/test_l3out_mtu_check.py b/tests/checks/l3out_mtu_check/test_l3out_mtu_check.py
similarity index 92%
rename from tests/l3out_mtu_check/test_l3out_mtu_check.py
rename to tests/checks/l3out_mtu_check/test_l3out_mtu_check.py
index ee74eb3d..50986acd 100644
--- a/tests/l3out_mtu_check/test_l3out_mtu_check.py
+++ b/tests/checks/l3out_mtu_check/test_l3out_mtu_check.py
@@ -9,6 +9,7 @@
log = logging.getLogger(__name__)
dir = os.path.dirname(os.path.abspath(__file__))
+test_function = "l3out_mtu_check"
# icurl queries
regular_api = "l3extRsPathL3OutAtt.json"
@@ -47,6 +48,6 @@
),
],
)
-def test_logic(mock_icurl, expected_result):
- result = script.l3out_mtu_check(1, 1)
- assert result == expected_result
+def test_logic(run_check, mock_icurl, expected_result):
+ result = run_check()
+ assert result.result == expected_result
diff --git a/tests/l3out_overlapping_loopback_check/diff_l3out_loopback.json b/tests/checks/l3out_overlapping_loopback_check/diff_l3out_loopback.json
similarity index 100%
rename from tests/l3out_overlapping_loopback_check/diff_l3out_loopback.json
rename to tests/checks/l3out_overlapping_loopback_check/diff_l3out_loopback.json
diff --git a/tests/l3out_overlapping_loopback_check/diff_l3out_loopback_and_rtrId.json b/tests/checks/l3out_overlapping_loopback_check/diff_l3out_loopback_and_rtrId.json
similarity index 100%
rename from tests/l3out_overlapping_loopback_check/diff_l3out_loopback_and_rtrId.json
rename to tests/checks/l3out_overlapping_loopback_check/diff_l3out_loopback_and_rtrId.json
diff --git a/tests/l3out_overlapping_loopback_check/diff_l3out_rtrId.json b/tests/checks/l3out_overlapping_loopback_check/diff_l3out_rtrId.json
similarity index 100%
rename from tests/l3out_overlapping_loopback_check/diff_l3out_rtrId.json
rename to tests/checks/l3out_overlapping_loopback_check/diff_l3out_rtrId.json
diff --git a/tests/l3out_overlapping_loopback_check/no_overlap.json b/tests/checks/l3out_overlapping_loopback_check/no_overlap.json
similarity index 100%
rename from tests/l3out_overlapping_loopback_check/no_overlap.json
rename to tests/checks/l3out_overlapping_loopback_check/no_overlap.json
diff --git a/tests/l3out_overlapping_loopback_check/overlap_on_diff_nodes.json b/tests/checks/l3out_overlapping_loopback_check/overlap_on_diff_nodes.json
similarity index 100%
rename from tests/l3out_overlapping_loopback_check/overlap_on_diff_nodes.json
rename to tests/checks/l3out_overlapping_loopback_check/overlap_on_diff_nodes.json
diff --git a/tests/l3out_overlapping_loopback_check/same_l3out_loopback.json b/tests/checks/l3out_overlapping_loopback_check/same_l3out_loopback.json
similarity index 100%
rename from tests/l3out_overlapping_loopback_check/same_l3out_loopback.json
rename to tests/checks/l3out_overlapping_loopback_check/same_l3out_loopback.json
diff --git a/tests/l3out_overlapping_loopback_check/same_l3out_loopback_and_rtrId.json b/tests/checks/l3out_overlapping_loopback_check/same_l3out_loopback_and_rtrId.json
similarity index 100%
rename from tests/l3out_overlapping_loopback_check/same_l3out_loopback_and_rtrId.json
rename to tests/checks/l3out_overlapping_loopback_check/same_l3out_loopback_and_rtrId.json
diff --git a/tests/l3out_overlapping_loopback_check/same_l3out_loopback_with_subnet_mask.json b/tests/checks/l3out_overlapping_loopback_check/same_l3out_loopback_with_subnet_mask.json
similarity index 100%
rename from tests/l3out_overlapping_loopback_check/same_l3out_loopback_with_subnet_mask.json
rename to tests/checks/l3out_overlapping_loopback_check/same_l3out_loopback_with_subnet_mask.json
diff --git a/tests/l3out_overlapping_loopback_check/same_l3out_rtrId.json b/tests/checks/l3out_overlapping_loopback_check/same_l3out_rtrId.json
similarity index 100%
rename from tests/l3out_overlapping_loopback_check/same_l3out_rtrId.json
rename to tests/checks/l3out_overlapping_loopback_check/same_l3out_rtrId.json
diff --git a/tests/l3out_overlapping_loopback_check/same_l3out_rtrId_non_vpc.json b/tests/checks/l3out_overlapping_loopback_check/same_l3out_rtrId_non_vpc.json
similarity index 100%
rename from tests/l3out_overlapping_loopback_check/same_l3out_rtrId_non_vpc.json
rename to tests/checks/l3out_overlapping_loopback_check/same_l3out_rtrId_non_vpc.json
diff --git a/tests/l3out_overlapping_loopback_check/same_l3out_two_loopbacks.json b/tests/checks/l3out_overlapping_loopback_check/same_l3out_two_loopbacks.json
similarity index 100%
rename from tests/l3out_overlapping_loopback_check/same_l3out_two_loopbacks.json
rename to tests/checks/l3out_overlapping_loopback_check/same_l3out_two_loopbacks.json
diff --git a/tests/l3out_overlapping_loopback_check/test_l3out_overlapping_loopback_check.py b/tests/checks/l3out_overlapping_loopback_check/test_l3out_overlapping_loopback_check.py
similarity index 92%
rename from tests/l3out_overlapping_loopback_check/test_l3out_overlapping_loopback_check.py
rename to tests/checks/l3out_overlapping_loopback_check/test_l3out_overlapping_loopback_check.py
index b91cfff8..0235de8f 100644
--- a/tests/l3out_overlapping_loopback_check/test_l3out_overlapping_loopback_check.py
+++ b/tests/checks/l3out_overlapping_loopback_check/test_l3out_overlapping_loopback_check.py
@@ -9,6 +9,7 @@
log = logging.getLogger(__name__)
dir = os.path.dirname(os.path.abspath(__file__))
+test_function = "l3out_overlapping_loopback_check"
# icurl queries
api = 'l3extOut.json'
@@ -43,6 +44,6 @@
({api: read_data(dir, "diff_l3out_loopback_and_rtrId.json")}, script.FAIL_O),
],
)
-def test_logic(mock_icurl, expected_result):
- result = script.l3out_overlapping_loopback_check(1, 1)
- assert result == expected_result
+def test_logic(run_check, mock_icurl, expected_result):
+ result = run_check()
+ assert result.result == expected_result
diff --git a/tests/l3out_route_map_missing_target_check/rtctrlProfile_missing_target.json b/tests/checks/l3out_route_map_missing_target_check/rtctrlProfile_missing_target.json
similarity index 100%
rename from tests/l3out_route_map_missing_target_check/rtctrlProfile_missing_target.json
rename to tests/checks/l3out_route_map_missing_target_check/rtctrlProfile_missing_target.json
diff --git a/tests/l3out_route_map_missing_target_check/rtctrlProfile_multiple_l3out_multiple_missing_target.json b/tests/checks/l3out_route_map_missing_target_check/rtctrlProfile_multiple_l3out_multiple_missing_target.json
similarity index 100%
rename from tests/l3out_route_map_missing_target_check/rtctrlProfile_multiple_l3out_multiple_missing_target.json
rename to tests/checks/l3out_route_map_missing_target_check/rtctrlProfile_multiple_l3out_multiple_missing_target.json
diff --git a/tests/l3out_route_map_missing_target_check/rtctrlProfile_multiple_missing_target.json b/tests/checks/l3out_route_map_missing_target_check/rtctrlProfile_multiple_missing_target.json
similarity index 100%
rename from tests/l3out_route_map_missing_target_check/rtctrlProfile_multiple_missing_target.json
rename to tests/checks/l3out_route_map_missing_target_check/rtctrlProfile_multiple_missing_target.json
diff --git a/tests/l3out_route_map_missing_target_check/rtctrlProfile_no_missing_target.json b/tests/checks/l3out_route_map_missing_target_check/rtctrlProfile_no_missing_target.json
similarity index 100%
rename from tests/l3out_route_map_missing_target_check/rtctrlProfile_no_missing_target.json
rename to tests/checks/l3out_route_map_missing_target_check/rtctrlProfile_no_missing_target.json
diff --git a/tests/l3out_route_map_missing_target_check/test_l3out_route_map_missing_target_check.py b/tests/checks/l3out_route_map_missing_target_check/test_l3out_route_map_missing_target_check.py
similarity index 86%
rename from tests/l3out_route_map_missing_target_check/test_l3out_route_map_missing_target_check.py
rename to tests/checks/l3out_route_map_missing_target_check/test_l3out_route_map_missing_target_check.py
index 045d9aae..8abce063 100644
--- a/tests/l3out_route_map_missing_target_check/test_l3out_route_map_missing_target_check.py
+++ b/tests/checks/l3out_route_map_missing_target_check/test_l3out_route_map_missing_target_check.py
@@ -9,6 +9,7 @@
log = logging.getLogger(__name__)
dir = os.path.dirname(os.path.abspath(__file__))
+test_function = "l3out_route_map_missing_target_check"
# icurl queries
profiles = 'rtctrlProfile.json'
@@ -63,6 +64,9 @@
),
],
)
-def test_logic(mock_icurl, cversion, tversion, expected_result):
- result = script.l3out_route_map_missing_target_check(1, 1, script.AciVersion(cversion), script.AciVersion(tversion))
- assert result == expected_result
+def test_logic(run_check, mock_icurl, cversion, tversion, expected_result):
+ result = run_check(
+ cversion=script.AciVersion(cversion),
+ tversion=script.AciVersion(tversion),
+ )
+ assert result.result == expected_result
diff --git a/tests/lldp_custom_int_description_defect_check/fvRsDomAtt_neg.json b/tests/checks/lldp_custom_int_description_defect_check/fvRsDomAtt_neg.json
similarity index 100%
rename from tests/lldp_custom_int_description_defect_check/fvRsDomAtt_neg.json
rename to tests/checks/lldp_custom_int_description_defect_check/fvRsDomAtt_neg.json
diff --git a/tests/lldp_custom_int_description_defect_check/fvRsDomAtt_pos.json b/tests/checks/lldp_custom_int_description_defect_check/fvRsDomAtt_pos.json
similarity index 100%
rename from tests/lldp_custom_int_description_defect_check/fvRsDomAtt_pos.json
rename to tests/checks/lldp_custom_int_description_defect_check/fvRsDomAtt_pos.json
diff --git a/tests/lldp_custom_int_description_defect_check/infraPortBlk_neg.json b/tests/checks/lldp_custom_int_description_defect_check/infraPortBlk_neg.json
similarity index 100%
rename from tests/lldp_custom_int_description_defect_check/infraPortBlk_neg.json
rename to tests/checks/lldp_custom_int_description_defect_check/infraPortBlk_neg.json
diff --git a/tests/lldp_custom_int_description_defect_check/infraPortBlk_pos.json b/tests/checks/lldp_custom_int_description_defect_check/infraPortBlk_pos.json
similarity index 100%
rename from tests/lldp_custom_int_description_defect_check/infraPortBlk_pos.json
rename to tests/checks/lldp_custom_int_description_defect_check/infraPortBlk_pos.json
diff --git a/tests/lldp_custom_int_description_defect_check/test_lldp_custom_int_description_defect_check.py b/tests/checks/lldp_custom_int_description_defect_check/test_lldp_custom_int_description_defect_check.py
similarity index 91%
rename from tests/lldp_custom_int_description_defect_check/test_lldp_custom_int_description_defect_check.py
rename to tests/checks/lldp_custom_int_description_defect_check/test_lldp_custom_int_description_defect_check.py
index 20f86ac4..72a4fb87 100644
--- a/tests/lldp_custom_int_description_defect_check/test_lldp_custom_int_description_defect_check.py
+++ b/tests/checks/lldp_custom_int_description_defect_check/test_lldp_custom_int_description_defect_check.py
@@ -9,6 +9,7 @@
log = logging.getLogger(__name__)
dir = os.path.dirname(os.path.abspath(__file__))
+test_function = "lldp_custom_int_description_defect_check"
# icurl queries
infraPortBlks = 'infraPortBlk.json?query-target-filter=ne(infraPortBlk.descr,"")&rsp-subtree-include=count'
@@ -84,6 +85,6 @@
),
],
)
-def test_logic(mock_icurl, tversion, expected_result):
- result = script.lldp_custom_int_description_defect_check(1, 1, script.AciVersion(tversion))
- assert result == expected_result
+def test_logic(run_check, mock_icurl, tversion, expected_result):
+ result = run_check(tversion=script.AciVersion(tversion))
+ assert result.result == expected_result
diff --git a/tests/llfc_susceptibility_check/ethpmFcot.json b/tests/checks/llfc_susceptibility_check/ethpmFcot.json
similarity index 100%
rename from tests/llfc_susceptibility_check/ethpmFcot.json
rename to tests/checks/llfc_susceptibility_check/ethpmFcot.json
diff --git a/tests/llfc_susceptibility_check/test_llfc_susceptibility_check.py b/tests/checks/llfc_susceptibility_check/test_llfc_susceptibility_check.py
similarity index 86%
rename from tests/llfc_susceptibility_check/test_llfc_susceptibility_check.py
rename to tests/checks/llfc_susceptibility_check/test_llfc_susceptibility_check.py
index c86b7454..eb90593d 100644
--- a/tests/llfc_susceptibility_check/test_llfc_susceptibility_check.py
+++ b/tests/checks/llfc_susceptibility_check/test_llfc_susceptibility_check.py
@@ -9,6 +9,7 @@
log = logging.getLogger(__name__)
dir = os.path.dirname(os.path.abspath(__file__))
+test_function = "llfc_susceptibility_check"
# icurl queries
ethpmFcots = 'ethpmFcot.json?query-target-filter=and(eq(ethpmFcot.type,"sfp"),eq(ethpmFcot.state,"inserted"))'
@@ -75,12 +76,10 @@
),
],
)
-def test_logic(mock_icurl, cversion, tversion, vpc_node_ids, expected_result):
- result = script.llfc_susceptibility_check(
- 1,
- 1,
- script.AciVersion(cversion),
- script.AciVersion(tversion) if tversion else None,
+def test_logic(run_check, mock_icurl, cversion, tversion, vpc_node_ids, expected_result):
+ result = run_check(
+ cversion=script.AciVersion(cversion),
+ tversion=script.AciVersion(tversion) if tversion else None,
vpc_node_ids=vpc_node_ids,
)
- assert result == expected_result
+ assert result.result == expected_result
diff --git a/tests/checks/mini_aci_6_0_2_check/fabricNode_all_phys_apic.json b/tests/checks/mini_aci_6_0_2_check/fabricNode_all_phys_apic.json
new file mode 100644
index 00000000..f276f099
--- /dev/null
+++ b/tests/checks/mini_aci_6_0_2_check/fabricNode_all_phys_apic.json
@@ -0,0 +1,92 @@
+[
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.1",
+ "dn": "topology/pod-1/node-1",
+ "fabricSt": "commissioned",
+ "id": "1",
+ "model": "APIC-SERVER-L2",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "apic1",
+ "nodeType": "unspecified",
+ "role": "controller"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.2",
+ "dn": "topology/pod-1/node-2",
+ "fabricSt": "commissioned",
+ "id": "2",
+ "model": "APIC-SERVER-L2",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "apic2",
+ "nodeType": "unspecified",
+ "role": "controller"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.3",
+ "dn": "topology/pod-2/node-3",
+ "fabricSt": "commissioned",
+ "id": "3",
+ "model": "APIC-SERVER-L2",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "apic3",
+ "nodeType": "unspecified",
+ "role": "controller"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.101",
+ "dn": "topology/pod-1/node-101",
+ "fabricSt": "active",
+ "id": "101",
+ "model": "N9K-C93180YC-FX",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "leaf101",
+ "nodeType": "unspecified",
+ "role": "leaf"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.102",
+ "dn": "topology/pod-1/node-102",
+ "fabricSt": "active",
+ "id": "102",
+ "model": "N9K-C93180YC-FX",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "leaf102",
+ "nodeType": "unspecified",
+ "role": "leaf"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.201",
+ "dn": "topology/pod-1/node-201",
+ "fabricSt": "active",
+ "id": "201",
+ "model": "N9K-C9504",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "spine201",
+ "nodeType": "unspecified",
+ "role": "spine"
+ }
+ }
+ }
+]
diff --git a/tests/checks/mini_aci_6_0_2_check/fabricNode_mini_aci.json b/tests/checks/mini_aci_6_0_2_check/fabricNode_mini_aci.json
new file mode 100644
index 00000000..de57905a
--- /dev/null
+++ b/tests/checks/mini_aci_6_0_2_check/fabricNode_mini_aci.json
@@ -0,0 +1,92 @@
+[
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.1",
+ "dn": "topology/pod-1/node-1",
+ "fabricSt": "commissioned",
+ "id": "1",
+ "model": "APIC-SERVER-L2",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "apic1",
+ "nodeType": "unspecified",
+ "role": "controller"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.2",
+ "dn": "topology/pod-1/node-2",
+ "fabricSt": "commissioned",
+ "id": "2",
+ "model": "APIC-SERVER-L2",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "apic2",
+ "nodeType": "virtual",
+ "role": "controller"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.3",
+ "dn": "topology/pod-2/node-3",
+ "fabricSt": "commissioned",
+ "id": "3",
+ "model": "APIC-SERVER-L2",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "apic3",
+ "nodeType": "virtual",
+ "role": "controller"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.101",
+ "dn": "topology/pod-1/node-101",
+ "fabricSt": "active",
+ "id": "101",
+ "model": "N9K-C93180YC-FX",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "leaf101",
+ "nodeType": "unspecified",
+ "role": "leaf"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.102",
+ "dn": "topology/pod-1/node-102",
+ "fabricSt": "active",
+ "id": "102",
+ "model": "N9K-C93180YC-FX",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "leaf102",
+ "nodeType": "unspecified",
+ "role": "leaf"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.201",
+ "dn": "topology/pod-1/node-201",
+ "fabricSt": "active",
+ "id": "201",
+ "model": "N9K-C9504",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "spine201",
+ "nodeType": "unspecified",
+ "role": "spine"
+ }
+ }
+ }
+]
diff --git a/tests/checks/mini_aci_6_0_2_check/test_mini_aci_6_0_2_check.py b/tests/checks/mini_aci_6_0_2_check/test_mini_aci_6_0_2_check.py
new file mode 100644
index 00000000..fe5bb275
--- /dev/null
+++ b/tests/checks/mini_aci_6_0_2_check/test_mini_aci_6_0_2_check.py
@@ -0,0 +1,75 @@
+import os
+import pytest
+import logging
+import importlib
+from helpers.utils import read_data
+
+script = importlib.import_module("aci-preupgrade-validation-script")
+
+log = logging.getLogger(__name__)
+dir = os.path.dirname(os.path.abspath(__file__))
+
+test_function = "mini_aci_6_0_2_check"
+
+
+@pytest.mark.parametrize(
+ "cversion, tversion, fabric_nodes, expected_result, expected_data",
+ [
+ # tversion missing
+ (
+ "5.2(3a)",
+ None,
+ read_data(dir, "fabricNode_mini_aci.json"),
+ script.MANUAL,
+ [],
+ ),
+ # Version Not Affected (not crossing 6.0.2)
+ (
+ "3.2(1a)",
+ "5.2(6a)",
+ read_data(dir, "fabricNode_mini_aci.json"),
+ script.NA,
+ [],
+ ),
+ # Version Not Affected (not crossing 6.0.2)
+ (
+ "6.0(2e)",
+ "6.0(5d)",
+ read_data(dir, "fabricNode_mini_aci.json"),
+ script.NA,
+ [],
+ ),
+ # Version Affected, Not mini ACI
+ (
+ "5.2(3a)",
+ "6.0(3d)",
+ read_data(dir, "fabricNode_all_phys_apic.json"),
+ script.PASS,
+ [],
+ ),
+ # Version Affected, mini ACI
+ (
+ "4.2(2a)",
+ "6.0(2c)",
+ read_data(dir, "fabricNode_mini_aci.json"),
+ script.FAIL_UF,
+ [["2", "apic2", "virtual"], ["3", "apic3", "virtual"]],
+ ),
+ # Version Affected, mini ACI
+ (
+ "6.0(1a)",
+ "6.0(2c)",
+ read_data(dir, "fabricNode_mini_aci.json"),
+ script.FAIL_UF,
+ [["2", "apic2", "virtual"], ["3", "apic3", "virtual"]],
+ ),
+ ],
+)
+def test_logic(run_check, cversion, tversion, fabric_nodes, expected_result, expected_data):
+ result = run_check(
+ cversion=script.AciVersion(cversion),
+ tversion=script.AciVersion(tversion) if tversion else None,
+ fabric_nodes=fabric_nodes,
+ )
+ assert result.result == expected_result
+ assert result.data == expected_data
diff --git a/tests/mini_aci_6_0_2/topSystem_controller_neg.json b/tests/checks/mini_aci_6_0_2_check/topSystem_controller_neg.json
similarity index 100%
rename from tests/mini_aci_6_0_2/topSystem_controller_neg.json
rename to tests/checks/mini_aci_6_0_2_check/topSystem_controller_neg.json
diff --git a/tests/mini_aci_6_0_2/topSystem_controller_pos.json b/tests/checks/mini_aci_6_0_2_check/topSystem_controller_pos.json
similarity index 100%
rename from tests/mini_aci_6_0_2/topSystem_controller_pos.json
rename to tests/checks/mini_aci_6_0_2_check/topSystem_controller_pos.json
diff --git a/tests/n9408_model_check/eqptCh_NEG.json b/tests/checks/n9408_model_check/eqptCh_NEG.json
similarity index 100%
rename from tests/n9408_model_check/eqptCh_NEG.json
rename to tests/checks/n9408_model_check/eqptCh_NEG.json
diff --git a/tests/n9408_model_check/eqptCh_POS.json b/tests/checks/n9408_model_check/eqptCh_POS.json
similarity index 100%
rename from tests/n9408_model_check/eqptCh_POS.json
rename to tests/checks/n9408_model_check/eqptCh_POS.json
diff --git a/tests/n9408_model_check/test_n9408_model_check.py b/tests/checks/n9408_model_check/test_n9408_model_check.py
similarity index 82%
rename from tests/n9408_model_check/test_n9408_model_check.py
rename to tests/checks/n9408_model_check/test_n9408_model_check.py
index 86d01b40..3076de1e 100644
--- a/tests/n9408_model_check/test_n9408_model_check.py
+++ b/tests/checks/n9408_model_check/test_n9408_model_check.py
@@ -9,12 +9,14 @@
log = logging.getLogger(__name__)
dir = os.path.dirname(os.path.abspath(__file__))
+test_function = "n9408_model_check"
# icurl queries
eqptCh_api = 'eqptCh.json'
eqptCh_api += '?query-target-filter=eq(eqptCh.model,"N9K-C9400-SW-GX2A")'
+
@pytest.mark.parametrize(
"icurl_outputs, tversion, expected_result",
[
@@ -38,6 +40,6 @@
),
],
)
-def test_logic(mock_icurl, tversion, expected_result):
- result = script.n9408_model_check(1, 1, script.AciVersion(tversion))
- assert result == expected_result
+def test_logic(run_check, mock_icurl, tversion, expected_result):
+ result = run_check(tversion=script.AciVersion(tversion))
+ assert result.result == expected_result
diff --git a/tests/checks/n9k_c93108tc_fx3p_interface_down_check/fabricNode_FX3H.json b/tests/checks/n9k_c93108tc_fx3p_interface_down_check/fabricNode_FX3H.json
new file mode 100644
index 00000000..d9747624
--- /dev/null
+++ b/tests/checks/n9k_c93108tc_fx3p_interface_down_check/fabricNode_FX3H.json
@@ -0,0 +1,77 @@
+[
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.1",
+ "dn": "topology/pod-1/node-1",
+ "fabricSt": "commissioned",
+ "id": "1",
+ "model": "APIC-SERVER-L2",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "apic1",
+ "nodeType": "unspecified",
+ "role": "controller"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.101",
+ "dn": "topology/pod-1/node-101",
+ "fabricSt": "active",
+ "id": "101",
+ "model": "N9K-C93180YC-FX",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "leaf101",
+ "nodeType": "unspecified",
+ "role": "leaf"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.113",
+ "dn": "topology/pod-2/node-113",
+ "fabricSt": "active",
+ "id": "113",
+ "model": "N9K-C93108TC-FX3H",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "leaf113",
+ "nodeType": "unspecified",
+ "role": "leaf"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.114",
+ "dn": "topology/pod-2/node-114",
+ "fabricSt": "active",
+ "id": "114",
+ "model": "N9K-C93108TC-FX3H",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "leaf114",
+ "nodeType": "unspecified",
+ "role": "leaf"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.201",
+ "dn": "topology/pod-1/node-201",
+ "fabricSt": "active",
+ "id": "201",
+ "model": "N9K-C9504",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "spine201",
+ "nodeType": "unspecified",
+ "role": "spine"
+ }
+ }
+ }
+]
diff --git a/tests/checks/n9k_c93108tc_fx3p_interface_down_check/fabricNode_FX3P.json b/tests/checks/n9k_c93108tc_fx3p_interface_down_check/fabricNode_FX3P.json
new file mode 100644
index 00000000..e31c4096
--- /dev/null
+++ b/tests/checks/n9k_c93108tc_fx3p_interface_down_check/fabricNode_FX3P.json
@@ -0,0 +1,77 @@
+[
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.1",
+ "dn": "topology/pod-1/node-1",
+ "fabricSt": "commissioned",
+ "id": "1",
+ "model": "APIC-SERVER-L2",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "apic1",
+ "nodeType": "unspecified",
+ "role": "controller"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.101",
+ "dn": "topology/pod-1/node-101",
+ "fabricSt": "active",
+ "id": "101",
+ "model": "N9K-C93180YC-FX",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "leaf101",
+ "nodeType": "unspecified",
+ "role": "leaf"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.113",
+ "dn": "topology/pod-2/node-113",
+ "fabricSt": "active",
+ "id": "113",
+ "model": "N9K-C93108TC-FX3P",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "leaf113",
+ "nodeType": "unspecified",
+ "role": "leaf"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.114",
+ "dn": "topology/pod-2/node-114",
+ "fabricSt": "active",
+ "id": "114",
+ "model": "N9K-C93108TC-FX3P",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "leaf114",
+ "nodeType": "unspecified",
+ "role": "leaf"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.201",
+ "dn": "topology/pod-1/node-201",
+ "fabricSt": "active",
+ "id": "201",
+ "model": "N9K-C9504",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "spine201",
+ "nodeType": "unspecified",
+ "role": "spine"
+ }
+ }
+ }
+]
diff --git a/tests/checks/n9k_c93108tc_fx3p_interface_down_check/fabricNode_FX3P3H.json b/tests/checks/n9k_c93108tc_fx3p_interface_down_check/fabricNode_FX3P3H.json
new file mode 100644
index 00000000..05a5ffa0
--- /dev/null
+++ b/tests/checks/n9k_c93108tc_fx3p_interface_down_check/fabricNode_FX3P3H.json
@@ -0,0 +1,77 @@
+[
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.1",
+ "dn": "topology/pod-1/node-1",
+ "fabricSt": "commissioned",
+ "id": "1",
+ "model": "APIC-SERVER-L2",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "apic1",
+ "nodeType": "unspecified",
+ "role": "controller"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.101",
+ "dn": "topology/pod-1/node-101",
+ "fabricSt": "active",
+ "id": "101",
+ "model": "N9K-C93180YC-FX",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "leaf101",
+ "nodeType": "unspecified",
+ "role": "leaf"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.113",
+ "dn": "topology/pod-2/node-113",
+ "fabricSt": "active",
+ "id": "113",
+ "model": "N9K-C93108TC-FX3P",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "leaf113",
+ "nodeType": "unspecified",
+ "role": "leaf"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.114",
+ "dn": "topology/pod-2/node-114",
+ "fabricSt": "active",
+ "id": "114",
+ "model": "N9K-C93108TC-FX3H",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "leaf114",
+ "nodeType": "unspecified",
+ "role": "leaf"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.201",
+ "dn": "topology/pod-1/node-201",
+ "fabricSt": "active",
+ "id": "201",
+ "model": "N9K-C9504",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "spine201",
+ "nodeType": "unspecified",
+ "role": "spine"
+ }
+ }
+ }
+]
diff --git a/tests/checks/n9k_c93108tc_fx3p_interface_down_check/fabricNode_no_FX3P3H.json b/tests/checks/n9k_c93108tc_fx3p_interface_down_check/fabricNode_no_FX3P3H.json
new file mode 100644
index 00000000..2d427721
--- /dev/null
+++ b/tests/checks/n9k_c93108tc_fx3p_interface_down_check/fabricNode_no_FX3P3H.json
@@ -0,0 +1,47 @@
+[
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.1",
+ "dn": "topology/pod-1/node-1",
+ "fabricSt": "commissioned",
+ "id": "1",
+ "model": "APIC-SERVER-L2",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "apic1",
+ "nodeType": "unspecified",
+ "role": "controller"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.101",
+ "dn": "topology/pod-1/node-101",
+ "fabricSt": "active",
+ "id": "101",
+ "model": "N9K-C93180YC-FX",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "leaf101",
+ "nodeType": "unspecified",
+ "role": "leaf"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.201",
+ "dn": "topology/pod-1/node-201",
+ "fabricSt": "active",
+ "id": "201",
+ "model": "N9K-C9504",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "spine201",
+ "nodeType": "unspecified",
+ "role": "spine"
+ }
+ }
+ }
+]
diff --git a/tests/checks/n9k_c93108tc_fx3p_interface_down_check/test_n9k_c93108tc_fx3p_interface_down_check.py b/tests/checks/n9k_c93108tc_fx3p_interface_down_check/test_n9k_c93108tc_fx3p_interface_down_check.py
new file mode 100644
index 00000000..e23529c9
--- /dev/null
+++ b/tests/checks/n9k_c93108tc_fx3p_interface_down_check/test_n9k_c93108tc_fx3p_interface_down_check.py
@@ -0,0 +1,93 @@
+import os
+import pytest
+import logging
+import importlib
+from helpers.utils import read_data
+
+script = importlib.import_module("aci-preupgrade-validation-script")
+
+log = logging.getLogger(__name__)
+dir = os.path.dirname(os.path.abspath(__file__))
+
+test_function = "n9k_c93108tc_fx3p_interface_down_check"
+
+
+@pytest.mark.parametrize(
+ "tversion, fabric_nodes, expected_result, expected_data",
+ [
+ # Version not supplied
+ (None, read_data(dir, "fabricNode_FX3P3H.json"), script.MANUAL, []),
+ # Version not affected
+ ("5.2(8h)", read_data(dir, "fabricNode_FX3P3H.json"), script.PASS, []),
+ ("5.3(2b)", read_data(dir, "fabricNode_FX3P3H.json"), script.PASS, []),
+ ("6.0(4c)", read_data(dir, "fabricNode_FX3P3H.json"), script.PASS, []),
+ # Affected version, no FX3P or FX3H
+ ("5.2(8g)", read_data(dir, "fabricNode_no_FX3P3H.json"), script.PASS, []),
+ ("5.3(1d)", read_data(dir, "fabricNode_no_FX3P3H.json"), script.PASS, []),
+ ("6.0(2h)", read_data(dir, "fabricNode_no_FX3P3H.json"), script.PASS, []),
+ # Affected version, FX3P
+ (
+ "5.2(8g)",
+ read_data(dir, "fabricNode_FX3P.json"),
+ script.FAIL_O,
+ [["113", "leaf113", "N9K-C93108TC-FX3P"], ["114", "leaf114", "N9K-C93108TC-FX3P"]],
+ ),
+ (
+ "5.3(1d)",
+ read_data(dir, "fabricNode_FX3P.json"),
+ script.FAIL_O,
+ [["113", "leaf113", "N9K-C93108TC-FX3P"], ["114", "leaf114", "N9K-C93108TC-FX3P"]],
+ ),
+ (
+ "6.0(2h)",
+ read_data(dir, "fabricNode_FX3P.json"),
+ script.FAIL_O,
+ [["113", "leaf113", "N9K-C93108TC-FX3P"], ["114", "leaf114", "N9K-C93108TC-FX3P"]],
+ ),
+ # Affected version, FX3H
+ (
+ "5.2(8g)",
+ read_data(dir, "fabricNode_FX3H.json"),
+ script.FAIL_O,
+ [["113", "leaf113", "N9K-C93108TC-FX3H"], ["114", "leaf114", "N9K-C93108TC-FX3H"]],
+ ),
+ (
+ "5.3(1d)",
+ read_data(dir, "fabricNode_FX3H.json"),
+ script.FAIL_O,
+ [["113", "leaf113", "N9K-C93108TC-FX3H"], ["114", "leaf114", "N9K-C93108TC-FX3H"]],
+ ),
+ (
+ "6.0(2h)",
+ read_data(dir, "fabricNode_FX3H.json"),
+ script.FAIL_O,
+ [["113", "leaf113", "N9K-C93108TC-FX3H"], ["114", "leaf114", "N9K-C93108TC-FX3H"]],
+ ),
+ # Affected version, FX3P and FX3H
+ (
+ "5.2(8g)",
+ read_data(dir, "fabricNode_FX3P3H.json"),
+ script.FAIL_O,
+ [["113", "leaf113", "N9K-C93108TC-FX3P"], ["114", "leaf114", "N9K-C93108TC-FX3H"]],
+ ),
+ (
+ "5.3(1d)",
+ read_data(dir, "fabricNode_FX3P3H.json"),
+ script.FAIL_O,
+ [["113", "leaf113", "N9K-C93108TC-FX3P"], ["114", "leaf114", "N9K-C93108TC-FX3H"]],
+ ),
+ (
+ "6.0(2h)",
+ read_data(dir, "fabricNode_FX3P3H.json"),
+ script.FAIL_O,
+ [["113", "leaf113", "N9K-C93108TC-FX3P"], ["114", "leaf114", "N9K-C93108TC-FX3H"]],
+ ),
+ ],
+)
+def test_logic(run_check, tversion, fabric_nodes, expected_result, expected_data):
+ result = run_check(
+ tversion=script.AciVersion(tversion) if tversion else None,
+ fabric_nodes=fabric_nodes,
+ )
+ assert result.result == expected_result
+ assert result.data == expected_data
diff --git a/tests/checks/ntp_status_check/NEG_datetimeClkPol.json b/tests/checks/ntp_status_check/NEG_datetimeClkPol.json
new file mode 100644
index 00000000..437efa37
--- /dev/null
+++ b/tests/checks/ntp_status_check/NEG_datetimeClkPol.json
@@ -0,0 +1,46 @@
+{
+ "imdata": [
+ {
+ "datetimeClkPol": {
+ "attributes": {
+ "StratumValue": "8",
+ "adminSt": "enabled",
+ "authSt": "disabled",
+ "childAction": "",
+ "clock": "2022-12-07T18:22:33.715-05:00",
+ "clockRaw": "13206872637755334329",
+ "descr": "",
+ "dn": "topology/pod-1/node-201/sys/time",
+ "flags": "synced",
+ "lcOwn": "local",
+ "leap": "0",
+ "masterMode": "disabled",
+ "modTs": "2022-01-02T22:36:04.148-05:00",
+ "monPolDn": "uni/fabric/monfab-Spine-Mon",
+ "name": "default",
+ "nameAlias": "",
+ "ntpdCfgFailedBmp": "",
+ "ntpdCfgFailedTs": "00:00:00:00.000",
+ "ntpdCfgState": "0",
+ "ownerKey": "",
+ "ownerTag": "",
+ "peer": "258740908",
+ "polDn": "uni/fabric/time-default",
+ "poll": "6",
+ "precision": "-20",
+ "refId": "172.18.108.15",
+ "refName": "172.18.108.15",
+ "refTime": "2022-12-07T18:21:19.436-05:00",
+ "refTimeRaw": "8059221958312304239",
+ "rootDelay": "49",
+ "rootDispersion": "2315255808",
+ "serverState": "disabled",
+ "srvStatus": "synced_remote_server",
+ "status": "",
+ "stratum": "2"
+ }
+ }
+ }
+ ],
+ "totalCount": "1"
+}
diff --git a/tests/checks/ntp_status_check/NEG_datetimeNtpq.json b/tests/checks/ntp_status_check/NEG_datetimeNtpq.json
new file mode 100644
index 00000000..0b676fe9
--- /dev/null
+++ b/tests/checks/ntp_status_check/NEG_datetimeNtpq.json
@@ -0,0 +1,29 @@
+{
+ "imdata": [
+ {
+ "datetimeNtpq": {
+ "attributes": {
+ "auth": "none",
+ "childAction": "",
+ "delay": "0.875",
+ "dn": "topology/pod-1/node-1/sys/ntpq-calo-timeserver-1.cisco.com",
+ "jitter": "0.027",
+ "lcOwn": "local",
+ "modTs": "2022-12-07T18:20:27.571-05:00",
+ "monPolDn": "uni/fabric/monfab-default",
+ "offset": "0.004",
+ "poll": "64",
+ "reach": "377",
+ "refid": ".GPS.",
+ "remote": "calo-timeserver-1.cisco.com",
+ "status": "",
+ "stratum": "1",
+ "t": "u",
+ "tally": "*",
+ "when": "16"
+ }
+ }
+ }
+ ],
+ "totalCount": "1"
+}
diff --git a/tests/checks/ntp_status_check/POS_datetimeClkPol.json b/tests/checks/ntp_status_check/POS_datetimeClkPol.json
new file mode 100644
index 00000000..5c83dbbb
--- /dev/null
+++ b/tests/checks/ntp_status_check/POS_datetimeClkPol.json
@@ -0,0 +1,46 @@
+{
+ "imdata": [
+ {
+ "datetimeClkPol": {
+ "attributes": {
+ "StratumValue": "8",
+ "adminSt": "enabled",
+ "authSt": "disabled",
+ "childAction": "",
+ "clock": "2022-12-07T18:22:33.715-05:00",
+ "clockRaw": "13206872637755334329",
+ "descr": "",
+ "dn": "topology/pod-1/node-201/sys/time",
+ "flags": "synced",
+ "lcOwn": "local",
+ "leap": "0",
+ "masterMode": "disabled",
+ "modTs": "2022-01-02T22:36:04.148-05:00",
+ "monPolDn": "uni/fabric/monfab-Spine-Mon",
+ "name": "default",
+ "nameAlias": "",
+ "ntpdCfgFailedBmp": "",
+ "ntpdCfgFailedTs": "00:00:00:00.000",
+ "ntpdCfgState": "0",
+ "ownerKey": "",
+ "ownerTag": "",
+ "peer": "258740908",
+ "polDn": "uni/fabric/time-default",
+ "poll": "6",
+ "precision": "-20",
+ "refId": "172.18.108.15",
+ "refName": "172.18.108.15",
+ "refTime": "2022-12-07T18:21:19.436-05:00",
+ "refTimeRaw": "8059221958312304239",
+ "rootDelay": "49",
+ "rootDispersion": "2315255808",
+ "serverState": "disabled",
+ "srvStatus": "",
+ "status": "",
+ "stratum": "2"
+ }
+ }
+ }
+ ],
+ "totalCount": "1"
+}
diff --git a/tests/checks/ntp_status_check/POS_datetimeNtpq.json b/tests/checks/ntp_status_check/POS_datetimeNtpq.json
new file mode 100644
index 00000000..698e516a
--- /dev/null
+++ b/tests/checks/ntp_status_check/POS_datetimeNtpq.json
@@ -0,0 +1,29 @@
+{
+ "imdata": [
+ {
+ "datetimeNtpq": {
+ "attributes": {
+ "auth": "none",
+ "childAction": "",
+ "delay": "0.875",
+ "dn": "topology/pod-1/node-1/sys/ntpq-calo-timeserver-1.cisco.com",
+ "jitter": "0.027",
+ "lcOwn": "local",
+ "modTs": "2022-12-07T18:20:27.571-05:00",
+ "monPolDn": "uni/fabric/monfab-default",
+ "offset": "0.004",
+ "poll": "64",
+ "reach": "377",
+ "refid": ".GPS.",
+ "remote": "calo-timeserver-1.cisco.com",
+ "status": "",
+ "stratum": "1",
+ "t": "u",
+ "tally": "",
+ "when": "16"
+ }
+ }
+ }
+ ],
+ "totalCount": "1"
+}
diff --git a/tests/checks/ntp_status_check/fabricNode.json b/tests/checks/ntp_status_check/fabricNode.json
new file mode 100644
index 00000000..ce488e69
--- /dev/null
+++ b/tests/checks/ntp_status_check/fabricNode.json
@@ -0,0 +1,64 @@
+[
+ {
+ "fabricNode": {
+ "attributes": {
+ "adSt": "on",
+ "address": "10.0.0.1",
+ "annotation": "",
+ "apicType": "apic",
+ "childAction": "",
+ "delayedHeartbeat": "no",
+ "dn": "topology/pod-1/node-1",
+ "extMngdBy": "",
+ "fabricSt": "unknown",
+ "id": "1",
+ "lastStateModTs": "2022-11-14T13:36:15.401-05:00",
+ "lcOwn": "local",
+ "modTs": "2022-11-14T13:36:16.119-05:00",
+ "model": "APIC-SERVER-L2",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "fab3-apic1",
+ "nameAlias": "",
+ "nodeType": "unspecified",
+ "role": "controller",
+ "serial": "FOX1234ABCE",
+ "status": "",
+ "uid": "0",
+ "userdom": "all",
+ "vendor": "Cisco Systems, Inc",
+ "version": "5.2(7f)"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "adSt": "on",
+ "address": "10.0.128.65",
+ "annotation": "",
+ "apicType": "apic",
+ "childAction": "",
+ "delayedHeartbeat": "no",
+ "dn": "topology/pod-1/node-201",
+ "extMngdBy": "",
+ "fabricSt": "active",
+ "id": "201",
+ "lastStateModTs": "2022-03-30T11:34:22.529-05:00",
+ "lcOwn": "local",
+ "modTs": "2022-03-30T11:34:34.616-05:00",
+ "model": "N9K-C9504",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "fab3-spine201",
+ "nameAlias": "",
+ "nodeType": "unspecified",
+ "role": "spine",
+ "serial": "FOX1234ABCD",
+ "status": "",
+ "uid": "0",
+ "userdom": "all",
+ "vendor": "Cisco Systems, Inc",
+ "version": "n9000-14.2(6d)"
+ }
+ }
+ }
+]
diff --git a/tests/checks/ntp_status_check/test_ntp_status_check.py b/tests/checks/ntp_status_check/test_ntp_status_check.py
new file mode 100644
index 00000000..3fff822b
--- /dev/null
+++ b/tests/checks/ntp_status_check/test_ntp_status_check.py
@@ -0,0 +1,65 @@
+import os
+import pytest
+import logging
+import importlib
+from helpers.utils import read_data
+
+script = importlib.import_module("aci-preupgrade-validation-script")
+
+log = logging.getLogger(__name__)
+dir = os.path.dirname(os.path.abspath(__file__))
+
+test_function = "ntp_status_check"
+
+# icurl queries
+apic_ntp = "datetimeNtpq.json"
+switch_ntp = "datetimeClkPol.json"
+
+
+@pytest.mark.parametrize(
+ "icurl_outputs, expected_result, expected_data",
+ [
+ # FAIL - APIC is not Synced
+ (
+ {
+ apic_ntp: read_data(dir, "POS_datetimeNtpq.json"),
+ switch_ntp: read_data(dir, "NEG_datetimeClkPol.json"),
+ },
+ script.FAIL_UF,
+ [["1", "1"]],
+ ),
+ # FAIL - Switch is not Synced
+ (
+ {
+ apic_ntp: read_data(dir, "NEG_datetimeNtpq.json"),
+ switch_ntp: read_data(dir, "POS_datetimeClkPol.json"),
+ },
+ script.FAIL_UF,
+ [["1", "201"]],
+ ),
+ # FAIL - APIC and Switch are not Synced
+ (
+ {
+ apic_ntp: read_data(dir, "POS_datetimeNtpq.json"),
+ switch_ntp: read_data(dir, "POS_datetimeClkPol.json"),
+ },
+ script.FAIL_UF,
+ [["1", "1"], ["1", "201"]],
+ ),
+ # PASS - Both are synced
+ (
+ {
+ apic_ntp: read_data(dir, "NEG_datetimeNtpq.json"),
+ switch_ntp: read_data(dir, "NEG_datetimeClkPol.json"),
+ },
+ script.PASS,
+ [],
+ ),
+ ],
+)
+def test_logic(run_check, mock_icurl, expected_result, expected_data):
+ result = run_check(
+ fabric_nodes=read_data(dir, "fabricNode.json"),
+ )
+ assert result.result == expected_result
+ assert result.data == expected_data
diff --git a/tests/checks/observer_db_size_check/fabricNode.json b/tests/checks/observer_db_size_check/fabricNode.json
new file mode 100644
index 00000000..21eed6a3
--- /dev/null
+++ b/tests/checks/observer_db_size_check/fabricNode.json
@@ -0,0 +1,46 @@
+[
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.1",
+ "dn": "topology/pod-1/node-1",
+ "id": "1",
+ "name": "apic1",
+ "role": "controller"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.2",
+ "dn": "topology/pod-1/node-2",
+ "id": "2",
+ "name": "apic2",
+ "role": "controller"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.3",
+ "dn": "topology/pod-1/node-3",
+ "id": "3",
+ "name": "apic3",
+ "role": "controller"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.101",
+ "dn": "topology/pod-1/node-101",
+ "id": "101",
+ "name": "leaf1",
+ "role": "leaf"
+ }
+ }
+ }
+]
diff --git a/tests/checks/observer_db_size_check/fabricNode_no_apic.json b/tests/checks/observer_db_size_check/fabricNode_no_apic.json
new file mode 100644
index 00000000..b82c912f
--- /dev/null
+++ b/tests/checks/observer_db_size_check/fabricNode_no_apic.json
@@ -0,0 +1,13 @@
+[
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.101",
+ "dn": "topology/pod-1/node-101",
+ "id": "101",
+ "name": "fab5-leaf1",
+ "role": "leaf"
+ }
+ }
+ }
+]
diff --git a/tests/checks/observer_db_size_check/fabricNode_old.json b/tests/checks/observer_db_size_check/fabricNode_old.json
new file mode 100644
index 00000000..f71fb9fc
--- /dev/null
+++ b/tests/checks/observer_db_size_check/fabricNode_old.json
@@ -0,0 +1,62 @@
+[
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-1/node-1",
+ "fabricSt": "unknown",
+ "nodeType": "unspecified",
+ "id": "1",
+ "version": "A",
+ "role": "controller",
+ "adSt": "on",
+ "name": "apic1",
+ "model": "APIC-SERVER-M1"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-1/node-2",
+ "fabricSt": "unknown",
+ "nodeType": "unspecified",
+ "id": "2",
+ "version": "A",
+ "role": "controller",
+ "adSt": "on",
+ "name": "apic2",
+ "model": "APIC-SERVER-M1"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-2/node-3",
+ "fabricSt": "unknown",
+ "nodeType": "unspecified",
+ "id": "3",
+ "version": "A",
+ "role": "controller",
+ "adSt": "on",
+ "name": "apic3",
+ "model": "APIC-SERVER-M1"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-1/node-101",
+ "fabricSt": "active",
+ "nodeType": "unspecified",
+ "id": "101",
+ "version": "",
+ "role": "leaf",
+ "adSt": "on",
+ "name": "leaf1",
+ "model": "N9K-C9396PX"
+ }
+ }
+ }
+]
diff --git a/tests/checks/observer_db_size_check/infraWiNode_apic1.json b/tests/checks/observer_db_size_check/infraWiNode_apic1.json
new file mode 100644
index 00000000..b6626d02
--- /dev/null
+++ b/tests/checks/observer_db_size_check/infraWiNode_apic1.json
@@ -0,0 +1,62 @@
+[
+ {
+ "infraWiNode": {
+ "attributes": {
+ "addr": "10.0.0.1",
+ "adminSt": "in-service",
+ "apicMode": "active",
+ "cntrlSbstState": "approved",
+ "dn": "topology/pod-1/node-1/av/node-1",
+ "failoverStatus": "idle",
+ "health": "fully-fit",
+ "id": "1",
+ "mbSn": "FCH1234ABCD",
+ "name": "",
+ "nodeName": "apic1",
+ "operSt": "available",
+ "podId": "0",
+ "targetMbSn": ""
+ }
+ }
+ },
+ {
+ "infraWiNode": {
+ "attributes": {
+ "addr": "10.0.0.2",
+ "adminSt": "in-service",
+ "apicMode": "active",
+ "cntrlSbstState": "approved",
+ "dn": "topology/pod-1/node-1/av/node-2",
+ "failoverStatus": "idle",
+ "health": "fully-fit",
+ "id": "2",
+ "mbSn": "FCH1235ABCD",
+ "name": "",
+ "nodeName": "apic2",
+ "operSt": "available",
+ "podId": "0",
+ "targetMbSn": ""
+ }
+ }
+ },
+ {
+ "infraWiNode": {
+ "attributes": {
+ "addr": "10.0.0.3",
+ "adminSt": "in-service",
+ "apicMode": "active",
+ "cntrlSbstState": "approved",
+ "dn": "topology/pod-1/node-1/av/node-3",
+ "failoverStatus": "idle",
+ "health": "fully-fit",
+ "id": "3",
+ "mbSn": "FCH1236ABCD",
+ "name": "",
+ "nodeName": "apic3",
+ "operSt": "available",
+ "podId": "1",
+ "targetMbSn": ""
+ }
+ }
+ }
+]
diff --git a/tests/checks/observer_db_size_check/test_observer_db_size_check.py b/tests/checks/observer_db_size_check/test_observer_db_size_check.py
new file mode 100644
index 00000000..b70f791e
--- /dev/null
+++ b/tests/checks/observer_db_size_check/test_observer_db_size_check.py
@@ -0,0 +1,185 @@
+import os
+import pytest
+import logging
+import importlib
+from helpers.utils import read_data
+
+script = importlib.import_module("aci-preupgrade-validation-script")
+
+log = logging.getLogger(__name__)
+dir = os.path.dirname(os.path.abspath(__file__))
+
+test_function = "observer_db_size_check"
+
+infraWiNode = "topology/pod-1/node-1/infraWiNode.json"
+
+fabricNodes = read_data(dir, "fabricNode.json")
+apic_ips = [
+ mo["fabricNode"]["attributes"]["address"]
+ for mo in fabricNodes
+ if mo["fabricNode"]["attributes"]["role"] == "controller"
+]
+
+ls_cmd = "ls -lh /data2/dbstats | awk '{print $5, $9}'"
+ls_output_neg = """\
+
+11M observer_8.db
+11M observer_9.db
+11M observer_10.db
+11M observer_template.db
+apic1#
+"""
+ls_output_pos = """\
+
+1.0G observer_8.db
+12G observer_9.db
+999M observer_10.db
+11M observer_template.db
+apic1#
+"""
+ls_output_no_such_file = """\
+ls: cannot access /data2/dbstats: No such file or directory
+apic1#
+"""
+
+
+@pytest.mark.parametrize(
+ "icurl_outputs, fabric_nodes, conn_failure, conn_cmds, expected_result, expected_data",
+ [
+ # Connection failure
+ (
+ {},
+ fabricNodes,
+ True,
+ [],
+ script.ERROR,
+ [
+ ["1", "apic1", "-", "Simulated exception at connect()"],
+ ["2", "apic2", "-", "Simulated exception at connect()"],
+ ["3", "apic3", "-", "Simulated exception at connect()"],
+ ],
+ ),
+ # Simulatated exception at `ls` command
+ (
+ {},
+ fabricNodes,
+ False,
+ {
+ apic_ip: [
+ {
+ "cmd": ls_cmd,
+ "output": "",
+ "exception": Exception("Simulated exception at `ls` command"),
+ }
+ ]
+ for apic_ip in apic_ips
+ },
+ script.ERROR,
+ [
+ ["1", "apic1", "-", "Simulated exception at `ls` command"],
+ ["2", "apic2", "-", "Simulated exception at `ls` command"],
+ ["3", "apic3", "-", "Simulated exception at `ls` command"],
+ ],
+ ),
+ # dbstats dir not found/not accessible
+ (
+ {},
+ fabricNodes,
+ False,
+ {
+ apic_ip: [
+ {
+ "cmd": ls_cmd,
+ "output": "\n".join([ls_cmd, ls_output_no_such_file]),
+ "exception": None,
+ }
+ ]
+ for apic_ip in apic_ips
+ },
+ script.ERROR,
+ [
+ ["1", "apic1", "/data2/dbstats/ not found", "Check user permissions or retry as 'apic#fallback\\\\admin'"],
+ ["2", "apic2", "/data2/dbstats/ not found", "Check user permissions or retry as 'apic#fallback\\\\admin'"],
+ ["3", "apic3", "/data2/dbstats/ not found", "Check user permissions or retry as 'apic#fallback\\\\admin'"],
+ ],
+ ),
+ # dbstats dir found, all DBs under 1G
+ (
+ {},
+ fabricNodes,
+ False,
+ {
+ apic_ip: [
+ {
+ "cmd": ls_cmd,
+ "output": "\n".join([ls_cmd, ls_output_neg]),
+ "exception": None,
+ }
+ ]
+ for apic_ip in apic_ips
+ },
+ script.PASS,
+ [],
+ ),
+ # dbstats dir found, all DBs under 1G (pre-4.0 with infraWiNode)
+ (
+ {infraWiNode: read_data(dir, "infraWiNode_apic1.json")},
+ read_data(dir, "fabricNode_old.json"),
+ False,
+ {
+ apic_ip: [
+ {
+ "cmd": ls_cmd,
+ "output": "\n".join([ls_cmd, ls_output_neg]),
+ "exception": None,
+ }
+ ]
+ for apic_ip in apic_ips
+ },
+ script.PASS,
+ [],
+ ),
+ # dbstats dir found, found DBs over 1G
+ (
+ {},
+ fabricNodes,
+ False,
+ {
+ apic_ip: [
+ {
+ "cmd": ls_cmd,
+ "output": "\n".join([ls_cmd, ls_output_pos]),
+ "exception": None,
+ }
+ ]
+ for apic_ip in apic_ips
+ },
+ script.FAIL_UF,
+ [
+ ["1", "apic1", "/data2/dbstats/observer_8.db", "1.0G"],
+ ["1", "apic1", "/data2/dbstats/observer_9.db", "12G"],
+ ["2", "apic2", "/data2/dbstats/observer_8.db", "1.0G"],
+ ["2", "apic2", "/data2/dbstats/observer_9.db", "12G"],
+ ["3", "apic3", "/data2/dbstats/observer_8.db", "1.0G"],
+ ["3", "apic3", "/data2/dbstats/observer_9.db", "12G"],
+ ],
+ ),
+ # ERROR, fabricNode failure
+ (
+ {},
+ read_data(dir, "fabricNode_no_apic.json"),
+ False,
+ [],
+ script.ERROR,
+ [],
+ ),
+ ],
+)
+def test_logic(run_check, mock_icurl, fabric_nodes, mock_conn, expected_result, expected_data):
+ result = run_check(
+ username="fake_username",
+ password="fake_password",
+ fabric_nodes=fabric_nodes,
+ )
+ assert result.result == expected_result
+ assert result.data == expected_data
diff --git a/tests/oob_mgmt_security_check/mgmtInstP.json b/tests/checks/oob_mgmt_security_check/mgmtInstP.json
similarity index 100%
rename from tests/oob_mgmt_security_check/mgmtInstP.json
rename to tests/checks/oob_mgmt_security_check/mgmtInstP.json
diff --git a/tests/oob_mgmt_security_check/mgmtInstP_no_contracts.json b/tests/checks/oob_mgmt_security_check/mgmtInstP_no_contracts.json
similarity index 100%
rename from tests/oob_mgmt_security_check/mgmtInstP_no_contracts.json
rename to tests/checks/oob_mgmt_security_check/mgmtInstP_no_contracts.json
diff --git a/tests/oob_mgmt_security_check/mgmtInstP_no_subnets.json b/tests/checks/oob_mgmt_security_check/mgmtInstP_no_subnets.json
similarity index 100%
rename from tests/oob_mgmt_security_check/mgmtInstP_no_subnets.json
rename to tests/checks/oob_mgmt_security_check/mgmtInstP_no_subnets.json
diff --git a/tests/oob_mgmt_security_check/mgmtOoB.json b/tests/checks/oob_mgmt_security_check/mgmtOoB.json
similarity index 100%
rename from tests/oob_mgmt_security_check/mgmtOoB.json
rename to tests/checks/oob_mgmt_security_check/mgmtOoB.json
diff --git a/tests/oob_mgmt_security_check/mgmtOoB_no_contracts.json b/tests/checks/oob_mgmt_security_check/mgmtOoB_no_contracts.json
similarity index 100%
rename from tests/oob_mgmt_security_check/mgmtOoB_no_contracts.json
rename to tests/checks/oob_mgmt_security_check/mgmtOoB_no_contracts.json
diff --git a/tests/oob_mgmt_security_check/test_oob_mgmt_security_check.py b/tests/checks/oob_mgmt_security_check/test_oob_mgmt_security_check.py
similarity index 92%
rename from tests/oob_mgmt_security_check/test_oob_mgmt_security_check.py
rename to tests/checks/oob_mgmt_security_check/test_oob_mgmt_security_check.py
index 95b33b12..86e093a6 100644
--- a/tests/oob_mgmt_security_check/test_oob_mgmt_security_check.py
+++ b/tests/checks/oob_mgmt_security_check/test_oob_mgmt_security_check.py
@@ -9,6 +9,7 @@
log = logging.getLogger(__name__)
dir = os.path.dirname(os.path.abspath(__file__))
+test_function = "oob_mgmt_security_check"
# icurl queries
mgmtOoB = "mgmtOoB.json?rsp-subtree=children"
@@ -129,8 +130,9 @@
),
],
)
-def test_logic(mock_icurl, cver, tver, expected_result):
- cversion = script.AciVersion(cver)
- tversion = script.AciVersion(tver) if tver else None
- result = script.oob_mgmt_security_check(1, 1, cversion, tversion)
- assert result == expected_result
+def test_logic(run_check, mock_icurl, cver, tver, expected_result):
+ result = run_check(
+ cversion=script.AciVersion(cver),
+ tversion=script.AciVersion(tver) if tver else None,
+ )
+ assert result.result == expected_result
diff --git a/tests/out_of_service_ports_check/ethpmPhysIf-neg.json b/tests/checks/out_of_service_ports_check/ethpmPhysIf-neg.json
similarity index 100%
rename from tests/out_of_service_ports_check/ethpmPhysIf-neg.json
rename to tests/checks/out_of_service_ports_check/ethpmPhysIf-neg.json
diff --git a/tests/out_of_service_ports_check/ethpmPhysIf-pos.json b/tests/checks/out_of_service_ports_check/ethpmPhysIf-pos.json
similarity index 100%
rename from tests/out_of_service_ports_check/ethpmPhysIf-pos.json
rename to tests/checks/out_of_service_ports_check/ethpmPhysIf-pos.json
diff --git a/tests/out_of_service_ports_check/test_out_of_service_ports_check.py b/tests/checks/out_of_service_ports_check/test_out_of_service_ports_check.py
similarity index 75%
rename from tests/out_of_service_ports_check/test_out_of_service_ports_check.py
rename to tests/checks/out_of_service_ports_check/test_out_of_service_ports_check.py
index 069677eb..4296eeec 100644
--- a/tests/out_of_service_ports_check/test_out_of_service_ports_check.py
+++ b/tests/checks/out_of_service_ports_check/test_out_of_service_ports_check.py
@@ -9,6 +9,8 @@
log = logging.getLogger(__name__)
dir = os.path.dirname(os.path.abspath(__file__))
+test_function = "out_of_service_ports_check"
+
# operst: '1' = 'up'
# usage: '32' = 'blacklist', '2' = 'epg'. '34'= 'blacklist,epg'
ethpmPhysIf_api = 'ethpmPhysIf.json'
@@ -19,17 +21,17 @@
"icurl_outputs, expected_result",
[
(
- ## Two 'up' ports flagged with 'blacklist,epg'
+ # Two 'up' ports flagged with 'blacklist,epg'
{ethpmPhysIf_api: read_data(dir, "ethpmPhysIf-pos.json")},
script.FAIL_O,
),
(
- ## 0 ports returned
+ # 0 ports returned
{ethpmPhysIf_api: read_data(dir, "ethpmPhysIf-neg.json")},
script.PASS,
)
],
)
-def test_logic(mock_icurl, expected_result):
- result = script.out_of_service_ports_check(1, 1)
- assert result == expected_result
+def test_logic(run_check, mock_icurl, expected_result):
+ result = run_check()
+ assert result.result == expected_result
diff --git a/tests/overlapping_vlan_pools_check/access_policy.json b/tests/checks/overlapping_vlan_pools_check/access_policy.json
similarity index 100%
rename from tests/overlapping_vlan_pools_check/access_policy.json
rename to tests/checks/overlapping_vlan_pools_check/access_policy.json
diff --git a/tests/overlapping_vlan_pools_check/fvAEPg.json b/tests/checks/overlapping_vlan_pools_check/fvAEPg.json
similarity index 100%
rename from tests/overlapping_vlan_pools_check/fvAEPg.json
rename to tests/checks/overlapping_vlan_pools_check/fvAEPg.json
diff --git a/tests/overlapping_vlan_pools_check/fvIfConn.json b/tests/checks/overlapping_vlan_pools_check/fvIfConn.json
similarity index 100%
rename from tests/overlapping_vlan_pools_check/fvIfConn.json
rename to tests/checks/overlapping_vlan_pools_check/fvIfConn.json
diff --git a/tests/overlapping_vlan_pools_check/infraSetPol_no.json b/tests/checks/overlapping_vlan_pools_check/infraSetPol_no.json
similarity index 100%
rename from tests/overlapping_vlan_pools_check/infraSetPol_no.json
rename to tests/checks/overlapping_vlan_pools_check/infraSetPol_no.json
diff --git a/tests/overlapping_vlan_pools_check/infraSetPol_yes.json b/tests/checks/overlapping_vlan_pools_check/infraSetPol_yes.json
similarity index 100%
rename from tests/overlapping_vlan_pools_check/infraSetPol_yes.json
rename to tests/checks/overlapping_vlan_pools_check/infraSetPol_yes.json
diff --git a/tests/overlapping_vlan_pools_check/templates/access_policy.j2 b/tests/checks/overlapping_vlan_pools_check/templates/access_policy.j2
similarity index 100%
rename from tests/overlapping_vlan_pools_check/templates/access_policy.j2
rename to tests/checks/overlapping_vlan_pools_check/templates/access_policy.j2
diff --git a/tests/overlapping_vlan_pools_check/templates/fvAEPg.j2 b/tests/checks/overlapping_vlan_pools_check/templates/fvAEPg.j2
similarity index 100%
rename from tests/overlapping_vlan_pools_check/templates/fvAEPg.j2
rename to tests/checks/overlapping_vlan_pools_check/templates/fvAEPg.j2
diff --git a/tests/overlapping_vlan_pools_check/templates/fvIfConn.j2 b/tests/checks/overlapping_vlan_pools_check/templates/fvIfConn.j2
similarity index 100%
rename from tests/overlapping_vlan_pools_check/templates/fvIfConn.j2
rename to tests/checks/overlapping_vlan_pools_check/templates/fvIfConn.j2
diff --git a/tests/overlapping_vlan_pools_check/templates/macros.j2 b/tests/checks/overlapping_vlan_pools_check/templates/macros.j2
similarity index 100%
rename from tests/overlapping_vlan_pools_check/templates/macros.j2
rename to tests/checks/overlapping_vlan_pools_check/templates/macros.j2
diff --git a/tests/overlapping_vlan_pools_check/test_overlapping_vlan_pools_check.py b/tests/checks/overlapping_vlan_pools_check/test_overlapping_vlan_pools_check.py
similarity index 99%
rename from tests/overlapping_vlan_pools_check/test_overlapping_vlan_pools_check.py
rename to tests/checks/overlapping_vlan_pools_check/test_overlapping_vlan_pools_check.py
index cf6f6ee6..24a31f02 100644
--- a/tests/overlapping_vlan_pools_check/test_overlapping_vlan_pools_check.py
+++ b/tests/checks/overlapping_vlan_pools_check/test_overlapping_vlan_pools_check.py
@@ -11,6 +11,8 @@
log = logging.getLogger(__name__)
dir = os.path.dirname(os.path.abspath(__file__))
+test_function = "overlapping_vlan_pools_check"
+
j2_env = Environment(loader=FileSystemLoader("/".join([dir, "templates"])))
tmpl = {
"infra": j2_env.get_template("access_policy.j2"),
@@ -2384,15 +2386,7 @@ def input(param):
+ [input(param) for param in params],
ids=["validation_yes", "validation_no"] + [param["id"] for param in params],
)
-def test_logic(capsys, mock_icurl, expected_result, expected_num_bad_ports):
- result = script.overlapping_vlan_pools_check(1, 1)
- assert result == expected_result
-
- captured = capsys.readouterr()
- log.debug(captured.out)
- lines = [
- x
- for x in captured.out.split("\n")
- if x.endswith("Outage") or x.endswith("Flood Scope")
- ]
- assert len(lines) == expected_num_bad_ports
+def test_logic(run_check, mock_icurl, expected_result, expected_num_bad_ports):
+ result = run_check()
+ assert result.result == expected_result
+ assert len(result.data) == expected_num_bad_ports
diff --git a/tests/pbr_high_scale_check/test_pbr_high_scale_check.py b/tests/checks/pbr_high_scale_check/test_pbr_high_scale_check.py
similarity index 90%
rename from tests/pbr_high_scale_check/test_pbr_high_scale_check.py
rename to tests/checks/pbr_high_scale_check/test_pbr_high_scale_check.py
index 5a498f41..9516bea6 100644
--- a/tests/pbr_high_scale_check/test_pbr_high_scale_check.py
+++ b/tests/checks/pbr_high_scale_check/test_pbr_high_scale_check.py
@@ -9,12 +9,14 @@
log = logging.getLogger(__name__)
dir = os.path.dirname(os.path.abspath(__file__))
+test_function = "pbr_high_scale_check"
# icurl queries
vnsAdjacencyDefCont_api = 'vnsAdjacencyDefCont.json'
vnsSvcRedirEcmpBucketCons_api = 'vnsSvcRedirEcmpBucketCons.json'
count_filter = '?rsp-subtree-include=count'
+
@pytest.mark.parametrize(
"icurl_outputs, tversion, expected_result",
[
@@ -65,10 +67,8 @@
),
],
)
-def test_logic(mock_icurl, tversion, expected_result):
- result = script.pbr_high_scale_check(
- 1,
- 1,
- script.AciVersion(tversion) if tversion else None,
+def test_logic(run_check, mock_icurl, tversion, expected_result):
+ result = run_check(
+ tversion=script.AciVersion(tversion) if tversion else None,
)
- assert result == expected_result
+ assert result.result == expected_result
diff --git a/tests/pbr_high_scale_check/vnsAdjacencyDefCont_HIGH.json b/tests/checks/pbr_high_scale_check/vnsAdjacencyDefCont_HIGH.json
similarity index 100%
rename from tests/pbr_high_scale_check/vnsAdjacencyDefCont_HIGH.json
rename to tests/checks/pbr_high_scale_check/vnsAdjacencyDefCont_HIGH.json
diff --git a/tests/pbr_high_scale_check/vnsAdjacencyDefCont_LOW.json b/tests/checks/pbr_high_scale_check/vnsAdjacencyDefCont_LOW.json
similarity index 100%
rename from tests/pbr_high_scale_check/vnsAdjacencyDefCont_LOW.json
rename to tests/checks/pbr_high_scale_check/vnsAdjacencyDefCont_LOW.json
diff --git a/tests/pbr_high_scale_check/vnsSvcRedirEcmpBucketCons_HIGH.json b/tests/checks/pbr_high_scale_check/vnsSvcRedirEcmpBucketCons_HIGH.json
similarity index 100%
rename from tests/pbr_high_scale_check/vnsSvcRedirEcmpBucketCons_HIGH.json
rename to tests/checks/pbr_high_scale_check/vnsSvcRedirEcmpBucketCons_HIGH.json
diff --git a/tests/pbr_high_scale_check/vnsSvcRedirEcmpBucketCons_LOW.json b/tests/checks/pbr_high_scale_check/vnsSvcRedirEcmpBucketCons_LOW.json
similarity index 100%
rename from tests/pbr_high_scale_check/vnsSvcRedirEcmpBucketCons_LOW.json
rename to tests/checks/pbr_high_scale_check/vnsSvcRedirEcmpBucketCons_LOW.json
diff --git a/tests/post_upgrade_cb_check/moCount_0.json b/tests/checks/post_upgrade_cb_check/moCount_0.json
similarity index 100%
rename from tests/post_upgrade_cb_check/moCount_0.json
rename to tests/checks/post_upgrade_cb_check/moCount_0.json
diff --git a/tests/post_upgrade_cb_check/moCount_10.json b/tests/checks/post_upgrade_cb_check/moCount_10.json
similarity index 100%
rename from tests/post_upgrade_cb_check/moCount_10.json
rename to tests/checks/post_upgrade_cb_check/moCount_10.json
diff --git a/tests/post_upgrade_cb_check/moCount_8.json b/tests/checks/post_upgrade_cb_check/moCount_8.json
similarity index 100%
rename from tests/post_upgrade_cb_check/moCount_8.json
rename to tests/checks/post_upgrade_cb_check/moCount_8.json
diff --git a/tests/post_upgrade_cb_check/test_post_upgrade_cb_check.py b/tests/checks/post_upgrade_cb_check/test_post_upgrade_cb_check.py
similarity index 92%
rename from tests/post_upgrade_cb_check/test_post_upgrade_cb_check.py
rename to tests/checks/post_upgrade_cb_check/test_post_upgrade_cb_check.py
index c964ad97..fb14efc2 100644
--- a/tests/post_upgrade_cb_check/test_post_upgrade_cb_check.py
+++ b/tests/checks/post_upgrade_cb_check/test_post_upgrade_cb_check.py
@@ -9,6 +9,8 @@
log = logging.getLogger(__name__)
dir = os.path.dirname(os.path.abspath(__file__))
+test_function = "post_upgrade_cb_check"
+
# icurl queries
mo1_new = "infraRsToImplicitSetPol.json?rsp-subtree-include=count"
@@ -29,7 +31,6 @@
mo6_new = 'compatSwitchHw.json?rsp-subtree-include=count&query-target-filter=eq(compatSwitchHw.suppBit,"32")'
-
# icurl output sets
mo_count_pass = {
mo1_new: read_data(dir, "moCount_10.json"),
@@ -96,11 +97,9 @@
(mo_count_fail, "6.0(3e)", "6.0(3e)", script.FAIL_O),
]
)
-def test_logic(mock_icurl, cversion, tversion, expected_result):
- result = script.post_upgrade_cb_check(
- 1,
- 1,
- script.AciVersion(cversion),
- script.AciVersion(tversion) if tversion else None,
+def test_logic(run_check, mock_icurl, cversion, tversion, expected_result):
+ result = run_check(
+ cversion=script.AciVersion(cversion),
+ tversion=script.AciVersion(tversion) if tversion else None,
)
- assert result == expected_result
+ assert result.result == expected_result
diff --git a/tests/prefix_already_in_use_check/faultInst_F0467_prefix-entry-already-in-use_new.json b/tests/checks/prefix_already_in_use_check/faultInst_F0467_prefix-entry-already-in-use_new.json
similarity index 100%
rename from tests/prefix_already_in_use_check/faultInst_F0467_prefix-entry-already-in-use_new.json
rename to tests/checks/prefix_already_in_use_check/faultInst_F0467_prefix-entry-already-in-use_new.json
diff --git a/tests/prefix_already_in_use_check/faultInst_F0467_prefix-entry-already-in-use_old.json b/tests/checks/prefix_already_in_use_check/faultInst_F0467_prefix-entry-already-in-use_old.json
similarity index 100%
rename from tests/prefix_already_in_use_check/faultInst_F0467_prefix-entry-already-in-use_old.json
rename to tests/checks/prefix_already_in_use_check/faultInst_F0467_prefix-entry-already-in-use_old.json
diff --git a/tests/prefix_already_in_use_check/fvCtx.json b/tests/checks/prefix_already_in_use_check/fvCtx.json
similarity index 100%
rename from tests/prefix_already_in_use_check/fvCtx.json
rename to tests/checks/prefix_already_in_use_check/fvCtx.json
diff --git a/tests/prefix_already_in_use_check/l3extRsEctx.json b/tests/checks/prefix_already_in_use_check/l3extRsEctx.json
similarity index 100%
rename from tests/prefix_already_in_use_check/l3extRsEctx.json
rename to tests/checks/prefix_already_in_use_check/l3extRsEctx.json
diff --git a/tests/prefix_already_in_use_check/l3extSubnet_no_overlap.json b/tests/checks/prefix_already_in_use_check/l3extSubnet_no_overlap.json
similarity index 100%
rename from tests/prefix_already_in_use_check/l3extSubnet_no_overlap.json
rename to tests/checks/prefix_already_in_use_check/l3extSubnet_no_overlap.json
diff --git a/tests/prefix_already_in_use_check/l3extSubnet_overlap.json b/tests/checks/prefix_already_in_use_check/l3extSubnet_overlap.json
similarity index 100%
rename from tests/prefix_already_in_use_check/l3extSubnet_overlap.json
rename to tests/checks/prefix_already_in_use_check/l3extSubnet_overlap.json
diff --git a/tests/prefix_already_in_use_check/test_prefix_already_in_use_check.py b/tests/checks/prefix_already_in_use_check/test_prefix_already_in_use_check.py
similarity index 91%
rename from tests/prefix_already_in_use_check/test_prefix_already_in_use_check.py
rename to tests/checks/prefix_already_in_use_check/test_prefix_already_in_use_check.py
index 8871eb70..10fe53bd 100644
--- a/tests/prefix_already_in_use_check/test_prefix_already_in_use_check.py
+++ b/tests/checks/prefix_already_in_use_check/test_prefix_already_in_use_check.py
@@ -9,6 +9,7 @@
log = logging.getLogger(__name__)
dir = os.path.dirname(os.path.abspath(__file__))
+test_function = "prefix_already_in_use_check"
# icurl queries
faultInst = 'faultInst.json?query-target-filter=and(wcard(faultInst.changeSet,"prefix-entry-already-in-use"),wcard(faultInst.dn,"uni/epp/rtd"))'
@@ -55,6 +56,6 @@
),
],
)
-def test_logic(mock_icurl, expected_result):
- result = script.prefix_already_in_use_check(1, 1)
- assert result == expected_result
+def test_logic(run_check, mock_icurl, expected_result):
+ result = run_check()
+ assert result.result == expected_result
diff --git a/tests/checks/r_leaf_compatibility_check/fabricNode_no_RL.json b/tests/checks/r_leaf_compatibility_check/fabricNode_no_RL.json
new file mode 100644
index 00000000..025af757
--- /dev/null
+++ b/tests/checks/r_leaf_compatibility_check/fabricNode_no_RL.json
@@ -0,0 +1,123 @@
+[
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-1/node-1",
+ "id": "1",
+ "name": "apic1",
+ "role": "controller",
+ "nodeType": "unspecified"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-1/node-2",
+ "id": "2",
+ "name": "apic2",
+ "role": "controller",
+ "nodeType": "unspecified"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-1/node-3",
+ "id": "3",
+ "name": "apic3",
+ "role": "controller",
+ "nodeType": "unspecified"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-1/node-101",
+ "id": "101",
+ "name": "LF101",
+ "role": "leaf",
+ "nodeType": "unspecified"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-1/node-102",
+ "id": "102",
+ "name": "LF102",
+ "role": "leaf",
+ "nodeType": "unspecified"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-1/node-103",
+ "id": "103",
+ "name": "LF103",
+ "role": "leaf",
+ "nodeType": "unspecified"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-1/node-104",
+ "id": "104",
+ "name": "LF104",
+ "role": "leaf",
+ "nodeType": "unspecified"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-1/node-111",
+ "id": "111",
+ "name": "T2_LF111",
+ "role": "leaf",
+ "nodeType": "tier-2-leaf"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-1/node-112",
+ "id": "112",
+ "name": "T2_LF112",
+ "role": "leaf",
+ "nodeType": "tier-2-leaf"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-1/node-1001",
+ "id": "1001",
+ "name": "SP1001",
+ "role": "spine",
+ "nodeType": "unspecified"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-1/node-1002",
+ "id": "1002",
+ "name": "SP1002",
+ "role": "spine",
+ "nodeType": "unspecified"
+ }
+ }
+ }
+]
diff --git a/tests/checks/r_leaf_compatibility_check/fabricNode_with_RL.json b/tests/checks/r_leaf_compatibility_check/fabricNode_with_RL.json
new file mode 100644
index 00000000..73e7d5b8
--- /dev/null
+++ b/tests/checks/r_leaf_compatibility_check/fabricNode_with_RL.json
@@ -0,0 +1,145 @@
+[
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-1/node-1",
+ "id": "1",
+ "name": "apic1",
+ "role": "controller",
+ "nodeType": "unspecified"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-1/node-2",
+ "id": "2",
+ "name": "apic2",
+ "role": "controller",
+ "nodeType": "unspecified"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-1/node-3",
+ "id": "3",
+ "name": "apic3",
+ "role": "controller",
+ "nodeType": "unspecified"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-1/node-101",
+ "id": "101",
+ "name": "LF101",
+ "role": "leaf",
+ "nodeType": "unspecified"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-1/node-102",
+ "id": "102",
+ "name": "LF102",
+ "role": "leaf",
+ "nodeType": "unspecified"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-1/node-103",
+ "id": "103",
+ "name": "LF103",
+ "role": "leaf",
+ "nodeType": "unspecified"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-1/node-104",
+ "id": "104",
+ "name": "LF104",
+ "role": "leaf",
+ "nodeType": "unspecified"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-1/node-111",
+ "id": "111",
+ "name": "T2_LF111",
+ "role": "leaf",
+ "nodeType": "tier-2-leaf"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-1/node-112",
+ "id": "112",
+ "name": "T2_LF112",
+ "role": "leaf",
+ "nodeType": "tier-2-leaf"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-1/node-121",
+ "id": "121",
+ "name": "RL_LF121",
+ "role": "leaf",
+ "nodeType": "remote-leaf-wan"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-1/node-122",
+ "id": "122",
+ "name": "RL_LF122",
+ "role": "leaf",
+ "nodeType": "remote-leaf-wan"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-1/node-1001",
+ "id": "1001",
+ "name": "SP1001",
+ "role": "spine",
+ "nodeType": "unspecified"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-1/node-1002",
+ "id": "1002",
+ "name": "SP1002",
+ "role": "spine",
+ "nodeType": "unspecified"
+ }
+ }
+ }
+]
diff --git a/tests/checks/r_leaf_compatibility_check/infraSetPol_DTF_disabled.json b/tests/checks/r_leaf_compatibility_check/infraSetPol_DTF_disabled.json
new file mode 100644
index 00000000..fd88e044
--- /dev/null
+++ b/tests/checks/r_leaf_compatibility_check/infraSetPol_DTF_disabled.json
@@ -0,0 +1,13 @@
+{
+ "totalCount": "1",
+ "imdata": [
+ {
+ "infraSetPol": {
+ "attributes": {
+ "dn": "uni/infra/settings",
+ "enableRemoteLeafDirect": "no"
+ }
+ }
+ }
+ ]
+}
diff --git a/tests/checks/r_leaf_compatibility_check/infraSetPol_DTF_enabled.json b/tests/checks/r_leaf_compatibility_check/infraSetPol_DTF_enabled.json
new file mode 100644
index 00000000..52e0a3e5
--- /dev/null
+++ b/tests/checks/r_leaf_compatibility_check/infraSetPol_DTF_enabled.json
@@ -0,0 +1,13 @@
+{
+ "totalCount": "1",
+ "imdata": [
+ {
+ "infraSetPol": {
+ "attributes": {
+ "dn": "uni/infra/settings",
+ "enableRemoteLeafDirect": "yes"
+ }
+ }
+ }
+ ]
+}
diff --git a/tests/checks/r_leaf_compatibility_check/infraSetPol_no_DTF.json b/tests/checks/r_leaf_compatibility_check/infraSetPol_no_DTF.json
new file mode 100644
index 00000000..0b6f56b9
--- /dev/null
+++ b/tests/checks/r_leaf_compatibility_check/infraSetPol_no_DTF.json
@@ -0,0 +1,12 @@
+{
+ "totalCount": "1",
+ "imdata": [
+ {
+ "infraSetPol": {
+ "attributes": {
+ "dn": "uni/infra/settings"
+ }
+ }
+ }
+ ]
+}
diff --git a/tests/checks/r_leaf_compatibility_check/test_r_leaf_compatibility_check.py b/tests/checks/r_leaf_compatibility_check/test_r_leaf_compatibility_check.py
new file mode 100644
index 00000000..b5d53ab3
--- /dev/null
+++ b/tests/checks/r_leaf_compatibility_check/test_r_leaf_compatibility_check.py
@@ -0,0 +1,103 @@
+import os
+import pytest
+import logging
+import importlib
+from helpers.utils import read_data
+
+log = logging.getLogger(__name__)
+dir = os.path.dirname(os.path.abspath(__file__))
+
+script = importlib.import_module("aci-preupgrade-validation-script")
+AciVersion = script.AciVersion
+
+test_function = "r_leaf_compatibility_check"
+
+# icurl queries
+infraSetPol = "uni/infra/settings.json"
+
+
+@pytest.mark.parametrize(
+ "icurl_outputs, fabric_nodes, cversion, tversion, expected_result, expected_data",
+ [
+ # MANUAL - No TVER
+ (
+ {infraSetPol: read_data(dir, "infraSetPol_no_DTF.json")},
+ read_data(dir, "fabricNode_with_RL.json"),
+ "4.1(1a)",
+ None,
+ script.MANUAL,
+ [],
+ ),
+ # FAIL - pre DTF version, yes RL
+ (
+ {infraSetPol: read_data(dir, "infraSetPol_no_DTF.json")},
+ read_data(dir, "fabricNode_with_RL.json"),
+ "4.1(1a)",
+ "5.2(1a)",
+ script.FAIL_O,
+ [["5.2(1a)", "Present", "Not Supported"]],
+ ),
+ # PASS - pre DTF version, no RL
+ (
+ {infraSetPol: read_data(dir, "infraSetPol_no_DTF.json")},
+ read_data(dir, "fabricNode_no_RL.json"),
+ "4.1(1a)",
+ "5.2(1a)",
+ script.NA,
+ [],
+ ),
+ # FAIL - bug version upgrade, yes RL
+ (
+ {infraSetPol: read_data(dir, "infraSetPol_DTF_enabled.json")},
+ read_data(dir, "fabricNode_with_RL.json"),
+ "4.1(2a)",
+ "4.2(2a)",
+ script.FAIL_O,
+ [["4.2(2a)", "Present", True]],
+ ),
+ # PASS - bug version upgrade, no RL
+ (
+ {infraSetPol: read_data(dir, "infraSetPol_DTF_enabled.json")},
+ read_data(dir, "fabricNode_no_RL.json"),
+ "4.1(2a)",
+ "4.2(2a)",
+ script.NA,
+ [],
+ ),
+ # PASS - Fix ver to 5.x, yes RL, DTF enabled
+ (
+ {infraSetPol: read_data(dir, "infraSetPol_DTF_enabled.json")},
+ read_data(dir, "fabricNode_with_RL.json"),
+ "4.2(3a)",
+ "5.2(3a)",
+ script.PASS,
+ [],
+ ),
+ # FAIL - Fix ver to 5.x, yes RL, DTF disabled
+ (
+ {infraSetPol: read_data(dir, "infraSetPol_DTF_disabled.json")},
+ read_data(dir, "fabricNode_with_RL.json"),
+ "4.2(3a)",
+ "5.2(3a)",
+ script.FAIL_O,
+ [["5.2(3a)", "Present", False]],
+ ),
+ # PASS - Fix ver to 5.x, no RL
+ (
+ {infraSetPol: read_data(dir, "infraSetPol_DTF_disabled.json")},
+ read_data(dir, "fabricNode_no_RL.json"),
+ "4.2(3a)",
+ "5.2(3a)",
+ script.NA,
+ [],
+ ),
+ ],
+)
+def test_logic(run_check, mock_icurl, fabric_nodes, cversion, tversion, expected_result, expected_data):
+ result = run_check(
+ cversion=AciVersion(cversion),
+ tversion=AciVersion(tversion) if tversion else None,
+ fabric_nodes=fabric_nodes,
+ )
+ assert result.result == expected_result
+ assert result.data == expected_data
diff --git a/tests/rtmap_comm_match_defect_check/rtctrlCtxP_NEG.json b/tests/checks/rtmap_comm_match_defect_check/rtctrlCtxP_NEG.json
similarity index 100%
rename from tests/rtmap_comm_match_defect_check/rtctrlCtxP_NEG.json
rename to tests/checks/rtmap_comm_match_defect_check/rtctrlCtxP_NEG.json
diff --git a/tests/rtmap_comm_match_defect_check/rtctrlCtxP_POS.json b/tests/checks/rtmap_comm_match_defect_check/rtctrlCtxP_POS.json
similarity index 100%
rename from tests/rtmap_comm_match_defect_check/rtctrlCtxP_POS.json
rename to tests/checks/rtmap_comm_match_defect_check/rtctrlCtxP_POS.json
diff --git a/tests/rtmap_comm_match_defect_check/rtctrlSubjP_NEG.json b/tests/checks/rtmap_comm_match_defect_check/rtctrlSubjP_NEG.json
similarity index 100%
rename from tests/rtmap_comm_match_defect_check/rtctrlSubjP_NEG.json
rename to tests/checks/rtmap_comm_match_defect_check/rtctrlSubjP_NEG.json
diff --git a/tests/rtmap_comm_match_defect_check/rtctrlSubjP_POS.json b/tests/checks/rtmap_comm_match_defect_check/rtctrlSubjP_POS.json
similarity index 100%
rename from tests/rtmap_comm_match_defect_check/rtctrlSubjP_POS.json
rename to tests/checks/rtmap_comm_match_defect_check/rtctrlSubjP_POS.json
diff --git a/tests/rtmap_comm_match_defect_check/test_rtmap_comm_match_defect_check.py b/tests/checks/rtmap_comm_match_defect_check/test_rtmap_comm_match_defect_check.py
similarity index 88%
rename from tests/rtmap_comm_match_defect_check/test_rtmap_comm_match_defect_check.py
rename to tests/checks/rtmap_comm_match_defect_check/test_rtmap_comm_match_defect_check.py
index c090d508..2dc9000b 100644
--- a/tests/rtmap_comm_match_defect_check/test_rtmap_comm_match_defect_check.py
+++ b/tests/checks/rtmap_comm_match_defect_check/test_rtmap_comm_match_defect_check.py
@@ -9,11 +9,13 @@
log = logging.getLogger(__name__)
dir = os.path.dirname(os.path.abspath(__file__))
+test_function = "rtmap_comm_match_defect_check"
# icurl queries
rtctrlSubjPs = "rtctrlSubjP.json?rsp-subtree=full&rsp-subtree-class=rtctrlMatchCommFactor,rtctrlMatchRtDest&rsp-subtree-include=required"
rtctrlCtxPs = "rtctrlCtxP.json?rsp-subtree=full&rsp-subtree-class=rtctrlRsCtxPToSubjP,rtctrlRsScopeToAttrP&rsp-subtree-include=required"
+
@pytest.mark.parametrize(
"icurl_outputs, tversion, expected_result",
[
@@ -67,10 +69,8 @@
),
],
)
-def test_logic(mock_icurl, tversion, expected_result):
- result = script.rtmap_comm_match_defect_check(
- 1,
- 1,
- script.AciVersion(tversion) if tversion else None,
+def test_logic(run_check, mock_icurl, tversion, expected_result):
+ result = run_check(
+ tversion=script.AciVersion(tversion) if tversion else None,
)
- assert result == expected_result
+ assert result.result == expected_result
diff --git a/tests/service_bd_forceful_routing_check/fvRtEPpInfoToBD.json b/tests/checks/service_bd_forceful_routing_check/fvRtEPpInfoToBD.json
similarity index 100%
rename from tests/service_bd_forceful_routing_check/fvRtEPpInfoToBD.json
rename to tests/checks/service_bd_forceful_routing_check/fvRtEPpInfoToBD.json
diff --git a/tests/service_bd_forceful_routing_check/test_service_bd_forceful_routing_check.py b/tests/checks/service_bd_forceful_routing_check/test_service_bd_forceful_routing_check.py
similarity index 81%
rename from tests/service_bd_forceful_routing_check/test_service_bd_forceful_routing_check.py
rename to tests/checks/service_bd_forceful_routing_check/test_service_bd_forceful_routing_check.py
index 86038b39..5867c884 100644
--- a/tests/service_bd_forceful_routing_check/test_service_bd_forceful_routing_check.py
+++ b/tests/checks/service_bd_forceful_routing_check/test_service_bd_forceful_routing_check.py
@@ -9,6 +9,7 @@
log = logging.getLogger(__name__)
dir = os.path.dirname(os.path.abspath(__file__))
+test_function = "service_bd_forceful_routing_check"
# icurl queries
fvRtEPpInfoToBD = "fvRtEPpInfoToBD.json"
@@ -54,8 +55,9 @@
),
],
)
-def test_logic(mock_icurl, cversion, tversion, expected_result):
- cver = script.AciVersion(cversion)
- tver = script.AciVersion(tversion) if tversion else None
- result = script.service_bd_forceful_routing_check(1, 1, cver, tver)
- assert result == expected_result
+def test_logic(run_check, mock_icurl, cversion, tversion, expected_result):
+ result = run_check(
+ cversion=script.AciVersion(cversion),
+ tversion=script.AciVersion(tversion) if tversion else None,
+ )
+ assert result.result == expected_result
diff --git a/tests/checks/stale_decomissioned_spine_check/fabricNode.json b/tests/checks/stale_decomissioned_spine_check/fabricNode.json
new file mode 100644
index 00000000..7a79d17c
--- /dev/null
+++ b/tests/checks/stale_decomissioned_spine_check/fabricNode.json
@@ -0,0 +1,46 @@
+[
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-1/node-101",
+ "id": "101",
+ "fabricSt": "active",
+ "role": "leaf",
+ "name": "leaf1"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-1/node-201",
+ "id": "201",
+ "fabricSt": "active",
+ "role": "spine",
+ "name": "spine1"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-1/node-106",
+ "id": "106",
+ "fabricSt": "active",
+ "role": "spine",
+ "name": "spine2"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-1/node-107",
+ "id": "107",
+ "fabricSt": "disabled",
+ "role": "spine",
+ "name": "spine3"
+ }
+ }
+ }
+]
diff --git a/tests/stale_decomissioned_spine_check/fabricRsDecommissionNode_POS.json b/tests/checks/stale_decomissioned_spine_check/fabricRsDecommissionNode_POS.json
similarity index 100%
rename from tests/stale_decomissioned_spine_check/fabricRsDecommissionNode_POS.json
rename to tests/checks/stale_decomissioned_spine_check/fabricRsDecommissionNode_POS.json
diff --git a/tests/checks/stale_decomissioned_spine_check/test_stale_decomissioned_spine_check.py b/tests/checks/stale_decomissioned_spine_check/test_stale_decomissioned_spine_check.py
new file mode 100644
index 00000000..7d9c6f98
--- /dev/null
+++ b/tests/checks/stale_decomissioned_spine_check/test_stale_decomissioned_spine_check.py
@@ -0,0 +1,57 @@
+import os
+import pytest
+import logging
+import importlib
+from helpers.utils import read_data
+
+script = importlib.import_module("aci-preupgrade-validation-script")
+
+log = logging.getLogger(__name__)
+dir = os.path.dirname(os.path.abspath(__file__))
+
+test_function = "stale_decomissioned_spine_check"
+
+# icurl queries
+decomissioned_api = "fabricRsDecommissionNode.json"
+
+
+@pytest.mark.parametrize(
+ "icurl_outputs, tversion, expected_result, expected_data",
+ [
+ # TVERSION not supplied
+ (
+ {decomissioned_api: read_data(dir, "fabricRsDecommissionNode_POS.json")},
+ None,
+ script.MANUAL,
+ [],
+ ),
+ # No decom objects
+ (
+ {decomissioned_api: []},
+ "5.2(5e)",
+ script.PASS,
+ [],
+ ),
+ # Spine has stale decom object, and going to affected version
+ (
+ {decomissioned_api: read_data(dir, "fabricRsDecommissionNode_POS.json")},
+ "5.2(6a)",
+ script.FAIL_O,
+ [["106", "spine2", "active"]],
+ ),
+ # Fixed Target Version
+ (
+ {decomissioned_api: read_data(dir, "fabricRsDecommissionNode_POS.json")},
+ "6.0(4a)",
+ script.PASS,
+ [],
+ ),
+ ],
+)
+def test_logic(run_check, mock_icurl, tversion, expected_result, expected_data):
+ result = run_check(
+ tversion=script.AciVersion(tversion) if tversion else None,
+ fabric_nodes=read_data(dir, "fabricNode.json"),
+ )
+ assert result.result == expected_result
+ assert result.data == expected_data
diff --git a/tests/standby_sup_sync_check/eqptSupC_NEG.json b/tests/checks/standby_sup_sync_check/eqptSupC_NEG.json
similarity index 100%
rename from tests/standby_sup_sync_check/eqptSupC_NEG.json
rename to tests/checks/standby_sup_sync_check/eqptSupC_NEG.json
diff --git a/tests/standby_sup_sync_check/eqptSupC_POS.json b/tests/checks/standby_sup_sync_check/eqptSupC_POS.json
similarity index 100%
rename from tests/standby_sup_sync_check/eqptSupC_POS.json
rename to tests/checks/standby_sup_sync_check/eqptSupC_POS.json
diff --git a/tests/checks/standby_sup_sync_check/test_standby_sup_sync_check.py b/tests/checks/standby_sup_sync_check/test_standby_sup_sync_check.py
new file mode 100644
index 00000000..7268aff0
--- /dev/null
+++ b/tests/checks/standby_sup_sync_check/test_standby_sup_sync_check.py
@@ -0,0 +1,153 @@
+import os
+import pytest
+import logging
+import importlib
+from helpers.utils import read_data
+
+script = importlib.import_module("aci-preupgrade-validation-script")
+
+log = logging.getLogger(__name__)
+dir = os.path.dirname(os.path.abspath(__file__))
+
+test_function = "standby_sup_sync_check"
+
+# icurl queries
+eqptSupC_api = "eqptSupC.json"
+eqptSupC_api += '?query-target-filter=eq(eqptSupC.rdSt,"standby")'
+
+"""
+Bug cversion/tversion matrix based on image size
+
+4.2(7t)+ - fixed versions LT 2 Gigs: 4.2(7t)+
+5.2(5d)+ - fixed versions LT 2 Gigs: 5.2(7f)+
+5.3(1d)+ - fixed versions LT 2 Gigs: 5.3(1d)+
+6.0(1g)+ - fixed versions LT 2 Gigs: 6.0(1g), 6.0(1j). 32-bit only: 6.0(2h), 6.0(2j). 64-bit: NONE
+6.1(1f)+ - fixed versions LT 2 Gigs: NONE
+"""
+
+
+@pytest.mark.parametrize(
+ "icurl_outputs, cversion, tversion, expected_result",
+ [
+ # NO TVERSION - MANUAL
+ (
+ {eqptSupC_api: read_data(dir, "eqptSupC_POS.json")},
+ "5.2(1a)",
+ None,
+ script.MANUAL,
+ ),
+ # CVERSION 4.2
+ # cversion 4.2 -nofix, tversion 4.2 -fix LT 2G
+ (
+ {eqptSupC_api: read_data(dir, "eqptSupC_POS.json")},
+ "4.2(7a)",
+ "4.2(8d)",
+ script.PASS,
+ ),
+ # cversion 4.2 -nofix, tversion 5.2 -fix but over 2G
+ (
+ {eqptSupC_api: read_data(dir, "eqptSupC_POS.json")},
+ "4.2(7a)",
+ "5.2(5d)",
+ script.FAIL_UF,
+ ),
+ # cversion 4.2 -nofix, tversion 5.2 -fix and LT 2G
+ (
+ {eqptSupC_api: read_data(dir, "eqptSupC_POS.json")},
+ "4.2(7a)",
+ "5.2(7f)",
+ script.PASS,
+ ),
+ # cversion 4.2 -nofix, tversion 5.3 -fix and LT 2G
+ (
+ {eqptSupC_api: read_data(dir, "eqptSupC_POS.json")},
+ "4.2(7a)",
+ "5.3(1d)",
+ script.PASS,
+ ),
+ # cversion 4.2 -nofix, tversion 6.0 -fix and LT 2G
+ (
+ {eqptSupC_api: read_data(dir, "eqptSupC_POS.json")},
+ "4.2(7a)",
+ "6.0(8d)",
+ script.FAIL_UF,
+ ),
+ # cversion 4.2 -nofix, tversion 6.1 -fix and LT 2G
+ (
+ {eqptSupC_api: read_data(dir, "eqptSupC_POS.json")},
+ "4.2(7a)",
+ "6.1(1f)",
+ script.FAIL_UF,
+ ),
+ # cversion 4.2 -fix, tversion 6.0 -fix but over 2G
+ (
+ {eqptSupC_api: read_data(dir, "eqptSupC_POS.json")},
+ "4.2(7t)",
+ "6.0(6h)",
+ script.PASS,
+ ),
+ # cversion 4.2 -fix, tversion 6.1 -fix but over 2G
+ (
+ {eqptSupC_api: read_data(dir, "eqptSupC_POS.json")},
+ "4.2(7t)",
+ "6.1(1f)",
+ script.PASS,
+ ),
+ # CVERSION 5.2
+ # cversion 5.2 -nofix, tversion 5.2 -fix but over 2G
+ (
+ {eqptSupC_api: read_data(dir, "eqptSupC_POS.json")},
+ "5.2(4a)",
+ "5.2(7a)",
+ script.FAIL_UF,
+ ),
+ # cversion 5.2 -nofix, tversion 5.2 -fix LT 2G
+ (
+ {eqptSupC_api: read_data(dir, "eqptSupC_POS.json")},
+ "5.2(4a)",
+ "5.2(7f)",
+ script.PASS,
+ ),
+ # cversion 5.2 -nofix, tversion 5.3 -fix LT 2G
+ (
+ {eqptSupC_api: read_data(dir, "eqptSupC_POS.json")},
+ "5.2(4a)",
+ "5.3(1d)",
+ script.PASS,
+ ),
+ # cversion 5.2 -nofix, tversion 6.0 -fix but over 2G
+ (
+ {eqptSupC_api: read_data(dir, "eqptSupC_POS.json")},
+ "5.2(4a)",
+ "6.0(8d)",
+ script.FAIL_UF,
+ ),
+ # cversion 5.2 -nofix, tversion 6.1 -fix but over 2G
+ (
+ {eqptSupC_api: read_data(dir, "eqptSupC_POS.json")},
+ "5.2(4a)",
+ "6.1(1f)",
+ script.FAIL_UF,
+ ),
+ # cversion 5.2 -fix, tversion 6.1 -fix but over 2G
+ (
+ {eqptSupC_api: read_data(dir, "eqptSupC_POS.json")},
+ "5.2(5d)",
+ "6.1(1f)",
+ script.PASS,
+ ),
+ # NO STANDBY SUPS
+ (
+ {eqptSupC_api: read_data(dir, "eqptSupC_NEG.json")},
+ "4.2(7a)",
+ "6.1(1f)",
+ script.PASS,
+ ),
+ ],
+)
+def test_logic(run_check, mock_icurl, cversion, tversion, expected_result):
+ result = run_check(
+ cversion=script.AciVersion(cversion),
+ tversion=script.AciVersion(tversion) if tversion else None
+ )
+ assert result.result == expected_result
diff --git a/tests/static_route_overlap_check/fvRsCtx.json b/tests/checks/static_route_overlap_check/fvRsCtx.json
similarity index 100%
rename from tests/static_route_overlap_check/fvRsCtx.json
rename to tests/checks/static_route_overlap_check/fvRsCtx.json
diff --git a/tests/static_route_overlap_check/fvSubnet.json b/tests/checks/static_route_overlap_check/fvSubnet.json
similarity index 100%
rename from tests/static_route_overlap_check/fvSubnet.json
rename to tests/checks/static_route_overlap_check/fvSubnet.json
diff --git a/tests/static_route_overlap_check/ipRouteP_empty.json b/tests/checks/static_route_overlap_check/ipRouteP_empty.json
similarity index 100%
rename from tests/static_route_overlap_check/ipRouteP_empty.json
rename to tests/checks/static_route_overlap_check/ipRouteP_empty.json
diff --git a/tests/static_route_overlap_check/ipRouteP_neg.json b/tests/checks/static_route_overlap_check/ipRouteP_neg.json
similarity index 100%
rename from tests/static_route_overlap_check/ipRouteP_neg.json
rename to tests/checks/static_route_overlap_check/ipRouteP_neg.json
diff --git a/tests/static_route_overlap_check/ipRouteP_pos.json b/tests/checks/static_route_overlap_check/ipRouteP_pos.json
similarity index 100%
rename from tests/static_route_overlap_check/ipRouteP_pos.json
rename to tests/checks/static_route_overlap_check/ipRouteP_pos.json
diff --git a/tests/static_route_overlap_check/l3extRsEctx.json b/tests/checks/static_route_overlap_check/l3extRsEctx.json
similarity index 100%
rename from tests/static_route_overlap_check/l3extRsEctx.json
rename to tests/checks/static_route_overlap_check/l3extRsEctx.json
diff --git a/tests/checks/static_route_overlap_check/test_static_route_overlap_check.py b/tests/checks/static_route_overlap_check/test_static_route_overlap_check.py
new file mode 100644
index 00000000..21333137
--- /dev/null
+++ b/tests/checks/static_route_overlap_check/test_static_route_overlap_check.py
@@ -0,0 +1,91 @@
+import os
+import pytest
+import logging
+import importlib
+from helpers.utils import read_data
+
+script = importlib.import_module("aci-preupgrade-validation-script")
+
+log = logging.getLogger(__name__)
+dir = os.path.dirname(os.path.abspath(__file__))
+
+test_function = "static_route_overlap_check"
+
+# icurl queries
+staticRoutes = 'ipRouteP.json?query-target-filter=and(wcard(ipRouteP.dn,"/32"))'
+staticroute_vrf = "l3extRsEctx.json"
+bds_in_vrf = "fvRsCtx.json"
+subnets_in_bd = "fvSubnet.json"
+
+
+@pytest.mark.parametrize(
+ "icurl_outputs, cversion, tversion, expected_result",
+ [
+ # FAIL = AFFECTED VERSION + AFFECTED MO
+ (
+ {
+ staticRoutes: read_data(dir, "ipRouteP_pos.json"),
+ staticroute_vrf: read_data(dir, "l3extRsEctx.json"),
+ bds_in_vrf: read_data(dir, "fvRsCtx.json"),
+ subnets_in_bd: read_data(dir, "fvSubnet.json"),
+ },
+ "4.2(7f)",
+ "5.2(4d)",
+ script.FAIL_O,
+ ),
+ # FAIL = AFFECTED VERSION + AFFECTED MO
+ (
+ {
+ staticRoutes: read_data(dir, "ipRouteP_pos.json"),
+ staticroute_vrf: read_data(dir, "l3extRsEctx.json"),
+ bds_in_vrf: read_data(dir, "fvRsCtx.json"),
+ subnets_in_bd: read_data(dir, "fvSubnet.json"),
+ },
+ "5.1(1a)",
+ "5.2(4d)",
+ script.FAIL_O,
+ ),
+ # PASS = AFFECTED VERSION + NON-AFFECTED MO
+ (
+ {
+ staticRoutes: read_data(dir, "ipRouteP_neg.json"),
+ staticroute_vrf: read_data(dir, "l3extRsEctx.json"),
+ bds_in_vrf: read_data(dir, "fvRsCtx.json"),
+ subnets_in_bd: read_data(dir, "fvSubnet.json"),
+ },
+ "4.2(7f)",
+ "5.2(4d)",
+ script.PASS,
+ ),
+ # PASS = AFFECTED VERSION + AFFECTED MO NON EXISTING
+ (
+ {
+ staticRoutes: read_data(dir, "ipRouteP_empty.json"),
+ staticroute_vrf: read_data(dir, "l3extRsEctx.json"),
+ bds_in_vrf: read_data(dir, "fvRsCtx.json"),
+ subnets_in_bd: read_data(dir, "fvSubnet.json"),
+ },
+ "4.2(7f)",
+ "5.2(4d)",
+ script.PASS,
+ ),
+ # PASS = NON-AFFECTED VERSION + AFFECTED MO
+ (
+ {
+ staticRoutes: read_data(dir, "ipRouteP_pos.json"),
+ staticroute_vrf: read_data(dir, "l3extRsEctx.json"),
+ bds_in_vrf: read_data(dir, "fvRsCtx.json"),
+ subnets_in_bd: read_data(dir, "fvSubnet.json"),
+ },
+ "4.2(7f)",
+ "5.2(6e)",
+ script.PASS,
+ ),
+ ],
+)
+def test_logic(run_check, mock_icurl, cversion, tversion, expected_result):
+ result = run_check(
+ cversion=script.AciVersion(cversion),
+ tversion=script.AciVersion(tversion),
+ )
+ assert result.result == expected_result
diff --git a/tests/subnet_scope_check/fvAEPg_empty.json b/tests/checks/subnet_scope_check/fvAEPg_empty.json
similarity index 100%
rename from tests/subnet_scope_check/fvAEPg_empty.json
rename to tests/checks/subnet_scope_check/fvAEPg_empty.json
diff --git a/tests/subnet_scope_check/fvAEPg_neg.json b/tests/checks/subnet_scope_check/fvAEPg_neg.json
similarity index 100%
rename from tests/subnet_scope_check/fvAEPg_neg.json
rename to tests/checks/subnet_scope_check/fvAEPg_neg.json
diff --git a/tests/subnet_scope_check/fvAEPg_pos.json b/tests/checks/subnet_scope_check/fvAEPg_pos.json
similarity index 100%
rename from tests/subnet_scope_check/fvAEPg_pos.json
rename to tests/checks/subnet_scope_check/fvAEPg_pos.json
diff --git a/tests/subnet_scope_check/fvBD.json b/tests/checks/subnet_scope_check/fvBD.json
similarity index 100%
rename from tests/subnet_scope_check/fvBD.json
rename to tests/checks/subnet_scope_check/fvBD.json
diff --git a/tests/subnet_scope_check/fvRsBd.json b/tests/checks/subnet_scope_check/fvRsBd.json
similarity index 100%
rename from tests/subnet_scope_check/fvRsBd.json
rename to tests/checks/subnet_scope_check/fvRsBd.json
diff --git a/tests/checks/subnet_scope_check/test_subnet_scope_check.py b/tests/checks/subnet_scope_check/test_subnet_scope_check.py
new file mode 100644
index 00000000..479d4809
--- /dev/null
+++ b/tests/checks/subnet_scope_check/test_subnet_scope_check.py
@@ -0,0 +1,74 @@
+import os
+import pytest
+import logging
+import importlib
+from helpers.utils import read_data
+
+script = importlib.import_module("aci-preupgrade-validation-script")
+
+log = logging.getLogger(__name__)
+dir = os.path.dirname(os.path.abspath(__file__))
+
+test_function = "subnet_scope_check"
+
+# icurl queries
+bd_api = "fvBD.json"
+bd_api += "?rsp-subtree=children&rsp-subtree-class=fvSubnet&rsp-subtree-include=required"
+
+epg_api = "fvAEPg.json?"
+epg_api += "rsp-subtree=children&rsp-subtree-class=fvSubnet&rsp-subtree-include=required"
+
+
+@pytest.mark.parametrize(
+ "icurl_outputs, cversion, expected_result",
+ [
+ (
+ {
+ bd_api: read_data(dir, "fvBD.json"),
+ epg_api: read_data(dir, "fvAEPg_empty.json"),
+ "fvRsBd.json": read_data(dir, "fvRsBd.json"),
+ },
+ "4.2(6a)",
+ script.NA,
+ ),
+ (
+ {
+ bd_api: read_data(dir, "fvBD.json"),
+ epg_api: read_data(dir, "fvAEPg_pos.json"),
+ "fvRsBd.json": read_data(dir, "fvRsBd.json"),
+ },
+ "4.2(6a)",
+ script.FAIL_O,
+ ),
+ (
+ {
+ bd_api: read_data(dir, "fvBD.json"),
+ epg_api: read_data(dir, "fvAEPg_pos.json"),
+ "fvRsBd.json": read_data(dir, "fvRsBd.json"),
+ },
+ "5.1(1a)",
+ script.FAIL_O,
+ ),
+ (
+ {
+ bd_api: read_data(dir, "fvBD.json"),
+ epg_api: read_data(dir, "fvAEPg_neg.json"),
+ "fvRsBd.json": read_data(dir, "fvRsBd.json"),
+ },
+ "5.1(1a)",
+ script.PASS,
+ ),
+ (
+ {
+ bd_api: read_data(dir, "fvBD.json"),
+ epg_api: read_data(dir, "fvAEPg_neg.json"),
+ "fvRsBd.json": read_data(dir, "fvRsBd.json"),
+ },
+ "5.2(8h)",
+ script.PASS,
+ ),
+ ],
+)
+def test_logic(run_check, mock_icurl, cversion, expected_result):
+ result = run_check(cversion=script.AciVersion(cversion))
+ assert result.result == expected_result
diff --git a/tests/sup_a_high_memory_check/eqptSupC_SUP_A.json b/tests/checks/sup_a_high_memory_check/eqptSupC_SUP_A.json
similarity index 100%
rename from tests/sup_a_high_memory_check/eqptSupC_SUP_A.json
rename to tests/checks/sup_a_high_memory_check/eqptSupC_SUP_A.json
diff --git a/tests/sup_a_high_memory_check/eqptSupC_SUP_A_Aplus.json b/tests/checks/sup_a_high_memory_check/eqptSupC_SUP_A_Aplus.json
similarity index 100%
rename from tests/sup_a_high_memory_check/eqptSupC_SUP_A_Aplus.json
rename to tests/checks/sup_a_high_memory_check/eqptSupC_SUP_A_Aplus.json
diff --git a/tests/sup_a_high_memory_check/eqptSupC_SUP_Aplus.json b/tests/checks/sup_a_high_memory_check/eqptSupC_SUP_Aplus.json
similarity index 100%
rename from tests/sup_a_high_memory_check/eqptSupC_SUP_Aplus.json
rename to tests/checks/sup_a_high_memory_check/eqptSupC_SUP_Aplus.json
diff --git a/tests/sup_a_high_memory_check/eqptSupC_no_SUP_A_Aplus.json b/tests/checks/sup_a_high_memory_check/eqptSupC_no_SUP_A_Aplus.json
similarity index 100%
rename from tests/sup_a_high_memory_check/eqptSupC_no_SUP_A_Aplus.json
rename to tests/checks/sup_a_high_memory_check/eqptSupC_no_SUP_A_Aplus.json
diff --git a/tests/sup_a_high_memory_check/test_sup_a_high_memory_check.py b/tests/checks/sup_a_high_memory_check/test_sup_a_high_memory_check.py
similarity index 87%
rename from tests/sup_a_high_memory_check/test_sup_a_high_memory_check.py
rename to tests/checks/sup_a_high_memory_check/test_sup_a_high_memory_check.py
index 80982357..17ce861c 100644
--- a/tests/sup_a_high_memory_check/test_sup_a_high_memory_check.py
+++ b/tests/checks/sup_a_high_memory_check/test_sup_a_high_memory_check.py
@@ -9,6 +9,7 @@
log = logging.getLogger(__name__)
dir = os.path.dirname(os.path.abspath(__file__))
+test_function = "sup_a_high_memory_check"
# icurl queries
eqptSupCs = "eqptSupC.json"
@@ -55,6 +56,6 @@
),
],
)
-def test_logic(mock_icurl, tversion, expected_result):
- result = script.sup_a_high_memory_check(1, 1, script.AciVersion(tversion))
- assert result == expected_result
+def test_logic(run_check, mock_icurl, tversion, expected_result):
+ result = run_check(tversion=script.AciVersion(tversion))
+ assert result.result == expected_result
diff --git a/tests/sup_hwrev_check/eqptSpCmnBlk_NEG.json b/tests/checks/sup_hwrev_check/eqptSpCmnBlk_NEG.json
similarity index 100%
rename from tests/sup_hwrev_check/eqptSpCmnBlk_NEG.json
rename to tests/checks/sup_hwrev_check/eqptSpCmnBlk_NEG.json
diff --git a/tests/sup_hwrev_check/eqptSpCmnBlk_POS.json b/tests/checks/sup_hwrev_check/eqptSpCmnBlk_POS.json
similarity index 100%
rename from tests/sup_hwrev_check/eqptSpCmnBlk_POS.json
rename to tests/checks/sup_hwrev_check/eqptSpCmnBlk_POS.json
diff --git a/tests/sup_hwrev_check/test_sup_hwrev_check.py b/tests/checks/sup_hwrev_check/test_sup_hwrev_check.py
similarity index 77%
rename from tests/sup_hwrev_check/test_sup_hwrev_check.py
rename to tests/checks/sup_hwrev_check/test_sup_hwrev_check.py
index 5ad6baa9..80ed301b 100644
--- a/tests/sup_hwrev_check/test_sup_hwrev_check.py
+++ b/tests/checks/sup_hwrev_check/test_sup_hwrev_check.py
@@ -9,6 +9,7 @@
log = logging.getLogger(__name__)
dir = os.path.dirname(os.path.abspath(__file__))
+test_function = "sup_hwrev_check"
# icurl queries
eqptSpCmnBlk = 'eqptSpCmnBlk.json?&query-target-filter=wcard(eqptSpromSupBlk.dn,"sup")'
@@ -17,6 +18,13 @@
@pytest.mark.parametrize(
"icurl_outputs, cversion, tversion, expected_result",
[
+ # Affected versions. No Sups found
+ (
+ {eqptSpCmnBlk: []},
+ "5.2(1g)",
+ "5.2(8e)",
+ script.MANUAL,
+ ),
# Affected Sups and on 5.2. VRM and FPGA Concern
(
{eqptSpCmnBlk: read_data(dir, "eqptSpCmnBlk_POS.json")},
@@ -54,8 +62,9 @@
),
],
)
-def test_logic(mock_icurl, cversion, tversion, expected_result):
- result = script.sup_hwrev_check(
- 1, 1, script.AciVersion(cversion), script.AciVersion(tversion)
+def test_logic(run_check, mock_icurl, cversion, tversion, expected_result):
+ result = run_check(
+ cversion=script.AciVersion(cversion),
+ tversion=script.AciVersion(tversion),
)
- assert result == expected_result
+ assert result.result == expected_result
diff --git a/tests/switch_bootflash_usage_check/eqptcapacityFSPartition.json b/tests/checks/switch_bootflash_usage_check/eqptcapacityFSPartition.json
similarity index 100%
rename from tests/switch_bootflash_usage_check/eqptcapacityFSPartition.json
rename to tests/checks/switch_bootflash_usage_check/eqptcapacityFSPartition.json
diff --git a/tests/switch_bootflash_usage_check/maintUpgJob_not_downloaded.json b/tests/checks/switch_bootflash_usage_check/maintUpgJob_not_downloaded.json
similarity index 100%
rename from tests/switch_bootflash_usage_check/maintUpgJob_not_downloaded.json
rename to tests/checks/switch_bootflash_usage_check/maintUpgJob_not_downloaded.json
diff --git a/tests/switch_bootflash_usage_check/maintUpgJob_old_ver_no_prop.json b/tests/checks/switch_bootflash_usage_check/maintUpgJob_old_ver_no_prop.json
similarity index 100%
rename from tests/switch_bootflash_usage_check/maintUpgJob_old_ver_no_prop.json
rename to tests/checks/switch_bootflash_usage_check/maintUpgJob_old_ver_no_prop.json
diff --git a/tests/switch_bootflash_usage_check/maintUpgJob_pre_downloaded.json b/tests/checks/switch_bootflash_usage_check/maintUpgJob_pre_downloaded.json
similarity index 100%
rename from tests/switch_bootflash_usage_check/maintUpgJob_pre_downloaded.json
rename to tests/checks/switch_bootflash_usage_check/maintUpgJob_pre_downloaded.json
diff --git a/tests/checks/switch_bootflash_usage_check/test_switch_bootflash_usage_check.py b/tests/checks/switch_bootflash_usage_check/test_switch_bootflash_usage_check.py
new file mode 100644
index 00000000..18e9c1c3
--- /dev/null
+++ b/tests/checks/switch_bootflash_usage_check/test_switch_bootflash_usage_check.py
@@ -0,0 +1,62 @@
+import os
+import pytest
+import logging
+import importlib
+from helpers.utils import read_data
+
+script = importlib.import_module("aci-preupgrade-validation-script")
+
+log = logging.getLogger(__name__)
+dir = os.path.dirname(os.path.abspath(__file__))
+
+test_function = "switch_bootflash_usage_check"
+
+# icurl queries
+partitions = "eqptcapacityFSPartition.json"
+partitions += '?query-target-filter=eq(eqptcapacityFSPartition.path,"/bootflash")'
+
+download_sts = "maintUpgJob.json"
+download_sts += '?query-target-filter=and(eq(maintUpgJob.dnldStatus,"downloaded")'
+download_sts += ',eq(maintUpgJob.desiredVersion,"n9000-16.0(2h)"))'
+
+
+@pytest.mark.parametrize(
+ "icurl_outputs, tversion, expected_result",
+ [
+ (
+ {
+ partitions: [],
+ download_sts: [],
+ },
+ "6.0(2h)",
+ script.MANUAL,
+ ),
+ (
+ {
+ partitions: read_data(dir, "eqptcapacityFSPartition.json"),
+ download_sts: read_data(dir, "maintUpgJob_not_downloaded.json"),
+ },
+ "6.0(2h)",
+ script.FAIL_UF,
+ ),
+ (
+ {
+ partitions: read_data(dir, "eqptcapacityFSPartition.json"),
+ download_sts: read_data(dir, "maintUpgJob_pre_downloaded.json"),
+ },
+ "6.0(2h)",
+ script.PASS,
+ ),
+ (
+ {
+ partitions: read_data(dir, "eqptcapacityFSPartition.json"),
+ download_sts: read_data(dir, "maintUpgJob_old_ver_no_prop.json"),
+ },
+ "6.0(2h)",
+ script.FAIL_UF,
+ ),
+ ],
+)
+def test_logic(run_check, mock_icurl, tversion, expected_result):
+ result = run_check(tversion=script.AciVersion(tversion))
+ assert result.result == expected_result
diff --git a/tests/checks/switch_group_guideline_check/bgpRRNodePEp_1001_1002_2001_2002.json b/tests/checks/switch_group_guideline_check/bgpRRNodePEp_1001_1002_2001_2002.json
new file mode 100644
index 00000000..dfd90b26
--- /dev/null
+++ b/tests/checks/switch_group_guideline_check/bgpRRNodePEp_1001_1002_2001_2002.json
@@ -0,0 +1,38 @@
+[
+ {
+ "bgpRRNodePEp": {
+ "attributes": {
+ "dn": "uni/fabric/bgpInstP-default/rr/node-1001",
+ "id": "1001",
+ "podId": "1"
+ }
+ }
+ },
+ {
+ "bgpRRNodePEp": {
+ "attributes": {
+ "dn": "uni/fabric/bgpInstP-default/rr/node-1002",
+ "id": "1002",
+ "podId": "1"
+ }
+ }
+ },
+ {
+ "bgpRRNodePEp": {
+ "attributes": {
+ "dn": "uni/fabric/bgpInstP-default/rr/node-2001",
+ "id": "2001",
+ "podId": "2"
+ }
+ }
+ },
+ {
+ "bgpRRNodePEp": {
+ "attributes": {
+ "dn": "uni/fabric/bgpInstP-default/rr/node-2002",
+ "id": "2002",
+ "podId": "2"
+ }
+ }
+ }
+]
diff --git a/tests/checks/switch_group_guideline_check/fabricExplicitGEp.json b/tests/checks/switch_group_guideline_check/fabricExplicitGEp.json
new file mode 100644
index 00000000..05cb7bc9
--- /dev/null
+++ b/tests/checks/switch_group_guideline_check/fabricExplicitGEp.json
@@ -0,0 +1,92 @@
+[
+ {
+ "fabricExplicitGEp": {
+ "attributes": {
+ "dn": "uni/fabric/protpol/expgep-101-102",
+ "id": "12",
+ "name": "101-102",
+ "virtualIp": "10.0.112.64/32"
+ },
+ "children": [
+ {
+ "fabricNodePEp": {
+ "attributes": {
+ "id": "102",
+ "podId": "1",
+ "rn": "nodepep-102"
+ }
+ }
+ },
+ {
+ "fabricNodePEp": {
+ "attributes": {
+ "id": "101",
+ "podId": "1",
+ "rn": "nodepep-101"
+ }
+ }
+ }
+ ]
+ }
+ },
+ {
+ "fabricExplicitGEp": {
+ "attributes": {
+ "dn": "uni/fabric/protpol/expgep-111-112",
+ "id": "112",
+ "name": "111-112",
+ "virtualIp": "10.0.188.64/32"
+ },
+ "children": [
+ {
+ "fabricNodePEp": {
+ "attributes": {
+ "id": "111",
+ "podId": "1",
+ "rn": "nodepep-111"
+ }
+ }
+ },
+ {
+ "fabricNodePEp": {
+ "attributes": {
+ "id": "112",
+ "podId": "1",
+ "rn": "nodepep-112"
+ }
+ }
+ }
+ ]
+ }
+ },
+ {
+ "fabricExplicitGEp": {
+ "attributes": {
+ "dn": "uni/fabric/protpol/expgep-201-202",
+ "id": "212",
+ "name": "201-202",
+ "virtualIp": "10.0.188.64/32"
+ },
+ "children": [
+ {
+ "fabricNodePEp": {
+ "attributes": {
+ "id": "201",
+ "podId": "2",
+ "rn": "nodepep-201"
+ }
+ }
+ },
+ {
+ "fabricNodePEp": {
+ "attributes": {
+ "id": "202",
+ "podId": "2",
+ "rn": "nodepep-202"
+ }
+ }
+ }
+ ]
+ }
+ }
+]
diff --git a/tests/checks/switch_group_guideline_check/fabricNode.json b/tests/checks/switch_group_guideline_check/fabricNode.json
new file mode 100644
index 00000000..09e4c84c
--- /dev/null
+++ b/tests/checks/switch_group_guideline_check/fabricNode.json
@@ -0,0 +1,206 @@
+[
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-1/node-1",
+ "fabricSt": "commissioned",
+ "id": "1",
+ "name": "apic1",
+ "role": "controller",
+ "nodeType": "unspecified"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-1/node-2",
+ "fabricSt": "commissioned",
+ "id": "2",
+ "name": "apic2",
+ "role": "controller",
+ "nodeType": "unspecified"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-2/node-3",
+ "fabricSt": "commissioned",
+ "id": "3",
+ "name": "apic3",
+ "role": "controller",
+ "nodeType": "unspecified"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-1/node-101",
+ "fabricSt": "active",
+ "id": "101",
+ "name": "LF101",
+ "role": "leaf",
+ "nodeType": "unspecified"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-1/node-102",
+ "fabricSt": "active",
+ "id": "102",
+ "name": "LF102",
+ "role": "leaf",
+ "nodeType": "unspecified"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-2/node-201",
+ "fabricSt": "active",
+ "id": "201",
+ "name": "LF201",
+ "role": "leaf",
+ "nodeType": "unspecified"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-2/node-202",
+ "fabricSt": "active",
+ "id": "202",
+ "name": "LF202",
+ "role": "leaf",
+ "nodeType": "unspecified"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-1/node-111",
+ "fabricSt": "active",
+ "id": "111",
+ "name": "T2_LF111",
+ "role": "leaf",
+ "nodeType": "tier-2-leaf"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-1/node-112",
+ "fabricSt": "active",
+ "id": "112",
+ "name": "T2_LF112",
+ "role": "leaf",
+ "nodeType": "tier-2-leaf"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-1/node-121",
+ "fabricSt": "active",
+ "id": "121",
+ "name": "RL_LF121",
+ "role": "leaf",
+ "nodeType": "remote-leaf-wan"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-1/node-122",
+ "fabricSt": "active",
+ "id": "122",
+ "name": "RL_LF122",
+ "role": "leaf",
+ "nodeType": "remote-leaf-wan"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-1/node-1001",
+ "fabricSt": "active",
+ "id": "1001",
+ "name": "SP1001",
+ "role": "spine",
+ "nodeType": "unspecified"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-1/node-1002",
+ "fabricSt": "active",
+ "id": "1002",
+ "name": "SP1002",
+ "role": "spine",
+ "nodeType": "unspecified"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-1/node-1003",
+ "fabricSt": "active",
+ "id": "1003",
+ "name": "SP1003",
+ "role": "spine",
+ "nodeType": "unspecified"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-1/node-1004",
+ "fabricSt": "active",
+ "id": "1004",
+ "name": "SP1004",
+ "role": "spine",
+ "nodeType": "unspecified"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-2/node-2001",
+ "fabricSt": "active",
+ "id": "2001",
+ "name": "SP2001",
+ "role": "spine",
+ "nodeType": "unspecified"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-2/node-2002",
+ "fabricSt": "active",
+ "id": "2002",
+ "name": "SP2002",
+ "role": "spine",
+ "nodeType": "unspecified"
+ }
+ }
+ }
+]
diff --git a/tests/checks/switch_group_guideline_check/l3extRsNodeL3OutAtt_1001_1002_2001_2002.json b/tests/checks/switch_group_guideline_check/l3extRsNodeL3OutAtt_1001_1002_2001_2002.json
new file mode 100644
index 00000000..5ecfc3f7
--- /dev/null
+++ b/tests/checks/switch_group_guideline_check/l3extRsNodeL3OutAtt_1001_1002_2001_2002.json
@@ -0,0 +1,50 @@
+[
+ {
+ "l3extRsNodeL3OutAtt": {
+ "attributes": {
+ "dn": "uni/tn-infra/out-multipodL3Out/lnodep-LNodeP_121/rsnodeL3OutAtt-[topology/pod-1/node-121]",
+ "tDn": "topology/pod-1/node-121"
+ }
+ }
+ },
+ {
+ "l3extRsNodeL3OutAtt": {
+ "attributes": {
+ "dn": "uni/tn-infra/out-multipodL3Out/lnodep-LNodeP_122/rsnodeL3OutAtt-[topology/pod-1/node-122]",
+ "tDn": "topology/pod-1/node-122"
+ }
+ }
+ },
+ {
+ "l3extRsNodeL3OutAtt": {
+ "attributes": {
+ "dn": "uni/tn-infra/out-multipodL3Out/lnodep-LNodeP_1001/rsnodeL3OutAtt-[topology/pod-1/node-1001]",
+ "tDn": "topology/pod-1/node-1001"
+ }
+ }
+ },
+ {
+ "l3extRsNodeL3OutAtt": {
+ "attributes": {
+ "dn": "uni/tn-infra/out-multipodL3Out/lnodep-LNodeP_1002/rsnodeL3OutAtt-[topology/pod-1/node-1002]",
+ "tDn": "topology/pod-1/node-1002"
+ }
+ }
+ },
+ {
+ "l3extRsNodeL3OutAtt": {
+ "attributes": {
+ "dn": "uni/tn-infra/out-multipodL3Out/lnodep-LNodeP_2001/rsnodeL3OutAtt-[topology/pod-2/node-2001]",
+ "tDn": "topology/pod-2/node-2001"
+ }
+ }
+ },
+ {
+ "l3extRsNodeL3OutAtt": {
+ "attributes": {
+ "dn": "uni/tn-infra/out-multipodL3Out/lnodep-node-2002-profile/rsnodeL3OutAtt-[topology/pod-2/node-2002]",
+ "tDn": "topology/pod-2/node-2002"
+ }
+ }
+ }
+]
diff --git a/tests/checks/switch_group_guideline_check/l3extRsNodeL3OutAtt_1003_1004_2001_2002.json b/tests/checks/switch_group_guideline_check/l3extRsNodeL3OutAtt_1003_1004_2001_2002.json
new file mode 100644
index 00000000..f7af8e8f
--- /dev/null
+++ b/tests/checks/switch_group_guideline_check/l3extRsNodeL3OutAtt_1003_1004_2001_2002.json
@@ -0,0 +1,50 @@
+[
+ {
+ "l3extRsNodeL3OutAtt": {
+ "attributes": {
+ "dn": "uni/tn-infra/out-multipodL3Out/lnodep-LNodeP_121/rsnodeL3OutAtt-[topology/pod-1/node-121]",
+ "tDn": "topology/pod-1/node-121"
+ }
+ }
+ },
+ {
+ "l3extRsNodeL3OutAtt": {
+ "attributes": {
+ "dn": "uni/tn-infra/out-multipodL3Out/lnodep-LNodeP_122/rsnodeL3OutAtt-[topology/pod-1/node-122]",
+ "tDn": "topology/pod-1/node-122"
+ }
+ }
+ },
+ {
+ "l3extRsNodeL3OutAtt": {
+ "attributes": {
+ "dn": "uni/tn-infra/out-multipodL3Out/lnodep-LNodeP_1003/rsnodeL3OutAtt-[topology/pod-1/node-1003]",
+ "tDn": "topology/pod-1/node-1003"
+ }
+ }
+ },
+ {
+ "l3extRsNodeL3OutAtt": {
+ "attributes": {
+ "dn": "uni/tn-infra/out-multipodL3Out/lnodep-LNodeP_1004/rsnodeL3OutAtt-[topology/pod-1/node-1004]",
+ "tDn": "topology/pod-1/node-1004"
+ }
+ }
+ },
+ {
+ "l3extRsNodeL3OutAtt": {
+ "attributes": {
+ "dn": "uni/tn-infra/out-multipodL3Out/lnodep-LNodeP_2001/rsnodeL3OutAtt-[topology/pod-2/node-2001]",
+ "tDn": "topology/pod-2/node-2001"
+ }
+ }
+ },
+ {
+ "l3extRsNodeL3OutAtt": {
+ "attributes": {
+ "dn": "uni/tn-infra/out-multipodL3Out/lnodep-node-2002-profile/rsnodeL3OutAtt-[topology/pod-2/node-2002]",
+ "tDn": "topology/pod-2/node-2002"
+ }
+ }
+ }
+]
diff --git a/tests/checks/switch_group_guideline_check/lldpCtrlrAdjEp.json b/tests/checks/switch_group_guideline_check/lldpCtrlrAdjEp.json
new file mode 100644
index 00000000..139f5bdd
--- /dev/null
+++ b/tests/checks/switch_group_guideline_check/lldpCtrlrAdjEp.json
@@ -0,0 +1,62 @@
+[
+ {
+ "lldpCtrlrAdjEp": {
+ "attributes": {
+ "apicMode": "active",
+ "dn": "topology/pod-1/node-101/sys/lldp/inst/if-[eth1/1]/ctrlradj",
+ "id": "1",
+ "portRole": "active"
+ }
+ }
+ },
+ {
+ "lldpCtrlrAdjEp": {
+ "attributes": {
+ "apicMode": "active",
+ "dn": "topology/pod-1/node-101/sys/lldp/inst/if-[eth1/2]/ctrlradj",
+ "id": "2",
+ "portRole": "active"
+ }
+ }
+ },
+ {
+ "lldpCtrlrAdjEp": {
+ "attributes": {
+ "apicMode": "active",
+ "dn": "topology/pod-1/node-102/sys/lldp/inst/if-[eth1/2]/ctrlradj",
+ "id": "2",
+ "portRole": "backup"
+ }
+ }
+ },
+ {
+ "lldpCtrlrAdjEp": {
+ "attributes": {
+ "apicMode": "active",
+ "dn": "topology/pod-1/node-102/sys/lldp/inst/if-[eth1/1]/ctrlradj",
+ "id": "1",
+ "portRole": "backup"
+ }
+ }
+ },
+ {
+ "lldpCtrlrAdjEp": {
+ "attributes": {
+ "apicMode": "active",
+ "dn": "topology/pod-2/node-201/sys/lldp/inst/if-[eth1/3]/ctrlradj",
+ "id": "3",
+ "portRole": "active"
+ }
+ }
+ },
+ {
+ "lldpCtrlrAdjEp": {
+ "attributes": {
+ "apicMode": "active",
+ "dn": "topology/pod-2/node-202/sys/lldp/inst/if-[eth1/3]/ctrlradj",
+ "id": "3",
+ "portRole": "active"
+ }
+ }
+ }
+]
diff --git a/tests/checks/switch_group_guideline_check/maintMaintGrp_ALL.json b/tests/checks/switch_group_guideline_check/maintMaintGrp_ALL.json
new file mode 100644
index 00000000..74719f4e
--- /dev/null
+++ b/tests/checks/switch_group_guideline_check/maintMaintGrp_ALL.json
@@ -0,0 +1,94 @@
+[
+ {
+ "maintMaintGrp": {
+ "attributes": {
+ "dn": "uni/fabric/maintgrp-ALL",
+ "fwtype": "switch",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "ALL",
+ "type": "range"
+ },
+ "children": [
+ {
+ "maintRsMgrpp": {
+ "attributes": {
+ "tCl": "maintMaintP",
+ "tDn": "uni/fabric/maintpol-ALL",
+ "tnMaintMaintPName": "ALL"
+ }
+ }
+ },
+ {
+ "fabricNodeBlk": {
+ "attributes": {"name": "blk101-101", "from_": "101", "to_": "101"}
+ }
+ },
+ {
+ "fabricNodeBlk": {
+ "attributes": {"name": "blk102-102", "from_": "102", "to_": "102"}
+ }
+ },
+ {
+ "fabricNodeBlk": {
+ "attributes": {"name": "blk111-111", "from_": "111", "to_": "111"}
+ }
+ },
+ {
+ "fabricNodeBlk": {
+ "attributes": {"name": "blk112-112", "from_": "112", "to_": "112"}
+ }
+ },
+ {
+ "fabricNodeBlk": {
+ "attributes": {"name": "blk121-121", "from_": "121", "to_": "121"}
+ }
+ },
+ {
+ "fabricNodeBlk": {
+ "attributes": {"name": "blk122-122", "from_": "122", "to_": "122"}
+ }
+ },
+ {
+ "fabricNodeBlk": {
+ "attributes": {"name": "blk201-201", "from_": "201", "to_": "201"}
+ }
+ },
+ {
+ "fabricNodeBlk": {
+ "attributes": {"name": "blk202-202", "from_": "202", "to_": "202"}
+ }
+ },
+ {
+ "fabricNodeBlk": {
+ "attributes": {"name": "blk1001-1001", "from_": "1001", "to_": "1001"}
+ }
+ },
+ {
+ "fabricNodeBlk": {
+ "attributes": {"name": "blk1002-1002", "from_": "1002", "to_": "1002"}
+ }
+ },
+ {
+ "fabricNodeBlk": {
+ "attributes": {"name": "blk1003-1003", "from_": "1003", "to_": "1003"}
+ }
+ },
+ {
+ "fabricNodeBlk": {
+ "attributes": {"name": "blk1004-1004", "from_": "1004", "to_": "1004"}
+ }
+ },
+ {
+ "fabricNodeBlk": {
+ "attributes": {"name": "blk2001-2001", "from_": "2001", "to_": "2001"}
+ }
+ },
+ {
+ "fabricNodeBlk": {
+ "attributes": {"name": "blk2002-2002", "from_": "2002", "to_": "2002"}
+ }
+ }
+ ]
+ }
+ }
+]
diff --git a/tests/checks/switch_group_guideline_check/maintMaintGrp_BAD_GRP1_GRP2.json b/tests/checks/switch_group_guideline_check/maintMaintGrp_BAD_GRP1_GRP2.json
new file mode 100644
index 00000000..ed909d9e
--- /dev/null
+++ b/tests/checks/switch_group_guideline_check/maintMaintGrp_BAD_GRP1_GRP2.json
@@ -0,0 +1,116 @@
+[
+ {
+ "maintMaintGrp": {
+ "attributes": {
+ "dn": "uni/fabric/maintgrp-GRP1",
+ "fwtype": "switch",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "GRP1",
+ "type": "range"
+ },
+ "children": [
+ {
+ "maintRsMgrpp": {
+ "attributes": {
+ "tCl": "maintMaintP",
+ "tDn": "uni/fabric/maintpol-GRP1",
+ "tnMaintMaintPName": "GRP1"
+ }
+ }
+ },
+ {
+ "fabricNodeBlk": {
+ "attributes": {"name": "blk101-101", "from_": "101", "to_": "101"}
+ }
+ },
+ {
+ "fabricNodeBlk": {
+ "attributes": {"name": "blk102-102", "from_": "102", "to_": "102"}
+ }
+ },
+ {
+ "fabricNodeBlk": {
+ "attributes": {"name": "blk121-121", "from_": "121", "to_": "121"}
+ }
+ },
+ {
+ "fabricNodeBlk": {
+ "attributes": {"name": "blk122-122", "from_": "122", "to_": "122"}
+ }
+ },
+ {
+ "fabricNodeBlk": {
+ "attributes": {"name": "blk1001-1001", "from_": "1001", "to_": "1001"}
+ }
+ },
+ {
+ "fabricNodeBlk": {
+ "attributes": {"name": "blk1002-1002", "from_": "1002", "to_": "1002"}
+ }
+ }
+ ]
+ }
+ },
+ {
+ "maintMaintGrp": {
+ "attributes": {
+ "dn": "uni/fabric/maintgrp-GRP2",
+ "fwtype": "switch",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "GRP2",
+ "type": "range"
+ },
+ "children": [
+ {
+ "maintRsMgrpp": {
+ "attributes": {
+ "tCl": "maintMaintP",
+ "tDn": "uni/fabric/maintpol-GRP2",
+ "tnMaintMaintPName": "GRP2"
+ }
+ }
+ },
+ {
+ "fabricNodeBlk": {
+ "attributes": {"name": "blk111-111", "from_": "111", "to_": "111"}
+ }
+ },
+ {
+ "fabricNodeBlk": {
+ "attributes": {"name": "blk112-112", "from_": "112", "to_": "112"}
+ }
+ },
+ {
+ "fabricNodeBlk": {
+ "attributes": {"name": "blk201-201", "from_": "201", "to_": "201"}
+ }
+ },
+ {
+ "fabricNodeBlk": {
+ "attributes": {"name": "blk202-202", "from_": "202", "to_": "202"}
+ }
+ },
+ {
+ "fabricNodeBlk": {
+ "attributes": {"name": "blk1003-1003", "from_": "1003", "to_": "1003"}
+ }
+ },
+ {
+ "fabricNodeBlk": {
+ "attributes": {"name": "blk1004-1004", "from_": "1004", "to_": "1004"}
+ }
+ },
+ {
+ "fabricNodeBlk": {
+ "attributes": {"name": "blk2001-2001", "from_": "2001", "to_": "2001"}
+ }
+ },
+ {
+ "fabricNodeBlk": {
+ "attributes": {"name": "blk2002-2002", "from_": "2002", "to_": "2002"}
+ }
+ }
+ ]
+ }
+ }
+]
diff --git a/tests/checks/switch_group_guideline_check/maintMaintGrp_BAD_ONLY_POD1_SPINE_RR.json b/tests/checks/switch_group_guideline_check/maintMaintGrp_BAD_ONLY_POD1_SPINE_RR.json
new file mode 100644
index 00000000..dace96c7
--- /dev/null
+++ b/tests/checks/switch_group_guideline_check/maintMaintGrp_BAD_ONLY_POD1_SPINE_RR.json
@@ -0,0 +1,160 @@
+[
+ {
+ "maintMaintGrp": {
+ "attributes": {
+ "dn": "uni/fabric/maintgrp-SPINE_GRP1",
+ "fwtype": "switch",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "SPINE_GRP1",
+ "type": "range"
+ },
+ "children": [
+ {
+ "maintRsMgrpp": {
+ "attributes": {
+ "tCl": "maintMaintP",
+ "tDn": "uni/fabric/maintpol-SPINE_GRP1",
+ "tnMaintMaintPName": "SPINE_GRP1"
+ }
+ }
+ },
+ {
+ "fabricNodeBlk": {
+ "attributes": {"name": "blk1001-1001", "from_": "1001", "to_": "1001"}
+ }
+ },
+ {
+ "fabricNodeBlk": {
+ "attributes": {"name": "blk1002-1002", "from_": "1002", "to_": "1002"}
+ }
+ },
+ {
+ "fabricNodeBlk": {
+ "attributes": {"name": "blk1003-1003", "from_": "1003", "to_": "1003"}
+ }
+ },
+ {
+ "fabricNodeBlk": {
+ "attributes": {"name": "blk2001-2001", "from_": "2001", "to_": "2001"}
+ }
+ }
+ ]
+ }
+ },
+ {
+ "maintMaintGrp": {
+ "attributes": {
+ "dn": "uni/fabric/maintgrp-SPINE_GRP2",
+ "fwtype": "switch",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "SPINE_GRP2",
+ "type": "range"
+ },
+ "children": [
+ {
+ "maintRsMgrpp": {
+ "attributes": {
+ "tCl": "maintMaintP",
+ "tDn": "uni/fabric/maintpol-SPINE_GRP2",
+ "tnMaintMaintPName": "SPINE_GRP2"
+ }
+ }
+ },
+ {
+ "fabricNodeBlk": {
+ "attributes": {"name": "blk1004-1004", "from_": "1004", "to_": "1004"}
+ }
+ },
+ {
+ "fabricNodeBlk": {
+ "attributes": {"name": "blk2002-2002", "from_": "2002", "to_": "2002"}
+ }
+ }
+ ]
+ }
+ },
+ {
+ "maintMaintGrp": {
+ "attributes": {
+ "dn": "uni/fabric/maintgrp-ODD",
+ "fwtype": "switch",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "ODD",
+ "type": "range"
+ },
+ "children": [
+ {
+ "maintRsMgrpp": {
+ "attributes": {
+ "tCl": "maintMaintP",
+ "tDn": "uni/fabric/maintpol-ODD",
+ "tnMaintMaintPName": "ODD"
+ }
+ }
+ },
+ {
+ "fabricNodeBlk": {
+ "attributes": {"name": "blk101-101", "from_": "101", "to_": "101"}
+ }
+ },
+ {
+ "fabricNodeBlk": {
+ "attributes": {"name": "blk111-111", "from_": "111", "to_": "111"}
+ }
+ },
+ {
+ "fabricNodeBlk": {
+ "attributes": {"name": "blk121-121", "from_": "121", "to_": "121"}
+ }
+ },
+ {
+ "fabricNodeBlk": {
+ "attributes": {"name": "blk201-201", "from_": "201", "to_": "201"}
+ }
+ }
+ ]
+ }
+ },
+ {
+ "maintMaintGrp": {
+ "attributes": {
+ "dn": "uni/fabric/maintgrp-EVEN",
+ "fwtype": "switch",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "EVEN",
+ "type": "range"
+ },
+ "children": [
+ {
+ "maintRsMgrpp": {
+ "attributes": {
+ "tCl": "maintMaintP",
+ "tDn": "uni/fabric/maintpol-EVEN",
+ "tnMaintMaintPName": "EVEN"
+ }
+ }
+ },
+ {
+ "fabricNodeBlk": {
+ "attributes": {"name": "blk102-102", "from_": "102", "to_": "102"}
+ }
+ },
+ {
+ "fabricNodeBlk": {
+ "attributes": {"name": "blk112-112", "from_": "112", "to_": "112"}
+ }
+ },
+ {
+ "fabricNodeBlk": {
+ "attributes": {"name": "blk122-122", "from_": "122", "to_": "122"}
+ }
+ },
+ {
+ "fabricNodeBlk": {
+ "attributes": {"name": "blk202-202", "from_": "202", "to_": "202"}
+ }
+ }
+ ]
+ }
+ }
+]
diff --git a/tests/checks/switch_group_guideline_check/maintMaintGrp_EVEN_ODD.json b/tests/checks/switch_group_guideline_check/maintMaintGrp_EVEN_ODD.json
new file mode 100644
index 00000000..678fff38
--- /dev/null
+++ b/tests/checks/switch_group_guideline_check/maintMaintGrp_EVEN_ODD.json
@@ -0,0 +1,116 @@
+[
+ {
+ "maintMaintGrp": {
+ "attributes": {
+ "dn": "uni/fabric/maintgrp-ODD",
+ "fwtype": "switch",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "ODD",
+ "type": "range"
+ },
+ "children": [
+ {
+ "maintRsMgrpp": {
+ "attributes": {
+ "tCl": "maintMaintP",
+ "tDn": "uni/fabric/maintpol-ODD",
+ "tnMaintMaintPName": "ODD"
+ }
+ }
+ },
+ {
+ "fabricNodeBlk": {
+ "attributes": {"name": "blk101-101", "from_": "101", "to_": "101"}
+ }
+ },
+ {
+ "fabricNodeBlk": {
+ "attributes": {"name": "blk111-111", "from_": "111", "to_": "111"}
+ }
+ },
+ {
+ "fabricNodeBlk": {
+ "attributes": {"name": "blk121-121", "from_": "121", "to_": "121"}
+ }
+ },
+ {
+ "fabricNodeBlk": {
+ "attributes": {"name": "blk201-201", "from_": "201", "to_": "201"}
+ }
+ },
+ {
+ "fabricNodeBlk": {
+ "attributes": {"name": "blk1001-1001", "from_": "1001", "to_": "1001"}
+ }
+ },
+ {
+ "fabricNodeBlk": {
+ "attributes": {"name": "blk1003-1003", "from_": "1003", "to_": "1003"}
+ }
+ },
+ {
+ "fabricNodeBlk": {
+ "attributes": {"name": "blk2001-2001", "from_": "2001", "to_": "2001"}
+ }
+ }
+ ]
+ }
+ },
+ {
+ "maintMaintGrp": {
+ "attributes": {
+ "dn": "uni/fabric/maintgrp-EVEN",
+ "fwtype": "switch",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "EVEN",
+ "type": "range"
+ },
+ "children": [
+ {
+ "maintRsMgrpp": {
+ "attributes": {
+ "tCl": "maintMaintP",
+ "tDn": "uni/fabric/maintpol-EVEN",
+ "tnMaintMaintPName": "EVEN"
+ }
+ }
+ },
+ {
+ "fabricNodeBlk": {
+ "attributes": {"name": "blk102-102", "from_": "102", "to_": "102"}
+ }
+ },
+ {
+ "fabricNodeBlk": {
+ "attributes": {"name": "blk112-112", "from_": "112", "to_": "112"}
+ }
+ },
+ {
+ "fabricNodeBlk": {
+ "attributes": {"name": "blk122-122", "from_": "122", "to_": "122"}
+ }
+ },
+ {
+ "fabricNodeBlk": {
+ "attributes": {"name": "blk202-202", "from_": "202", "to_": "202"}
+ }
+ },
+ {
+ "fabricNodeBlk": {
+ "attributes": {"name": "blk1002-1002", "from_": "1002", "to_": "1002"}
+ }
+ },
+ {
+ "fabricNodeBlk": {
+ "attributes": {"name": "blk1004-1004", "from_": "1004", "to_": "1004"}
+ }
+ },
+ {
+ "fabricNodeBlk": {
+ "attributes": {"name": "blk2002-2002", "from_": "2002", "to_": "2002"}
+ }
+ }
+ ]
+ }
+ }
+]
diff --git a/tests/checks/switch_group_guideline_check/maintMaintGrp_SPINE_LEAF.json b/tests/checks/switch_group_guideline_check/maintMaintGrp_SPINE_LEAF.json
new file mode 100644
index 00000000..f07b60e3
--- /dev/null
+++ b/tests/checks/switch_group_guideline_check/maintMaintGrp_SPINE_LEAF.json
@@ -0,0 +1,116 @@
+[
+ {
+ "maintMaintGrp": {
+ "attributes": {
+ "dn": "uni/fabric/maintgrp-SPINE",
+ "fwtype": "switch",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "SPINE",
+ "type": "range"
+ },
+ "children": [
+ {
+ "maintRsMgrpp": {
+ "attributes": {
+ "tCl": "maintMaintP",
+ "tDn": "uni/fabric/maintpol-SPINE",
+ "tnMaintMaintPName": "SPINE"
+ }
+ }
+ },
+ {
+ "fabricNodeBlk": {
+ "attributes": {"name": "blk1001-1001", "from_": "1001", "to_": "1001"}
+ }
+ },
+ {
+ "fabricNodeBlk": {
+ "attributes": {"name": "blk1002-1002", "from_": "1002", "to_": "1002"}
+ }
+ },
+ {
+ "fabricNodeBlk": {
+ "attributes": {"name": "blk1003-1003", "from_": "1003", "to_": "1003"}
+ }
+ },
+ {
+ "fabricNodeBlk": {
+ "attributes": {"name": "blk1004-1004", "from_": "1004", "to_": "1004"}
+ }
+ },
+ {
+ "fabricNodeBlk": {
+ "attributes": {"name": "blk2001-2001", "from_": "2001", "to_": "2001"}
+ }
+ },
+ {
+ "fabricNodeBlk": {
+ "attributes": {"name": "blk2002-2002", "from_": "2002", "to_": "2002"}
+ }
+ }
+ ]
+ }
+ },
+ {
+ "maintMaintGrp": {
+ "attributes": {
+ "dn": "uni/fabric/maintgrp-LEAF",
+ "fwtype": "switch",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "LEAF",
+ "type": "range"
+ },
+ "children": [
+ {
+ "maintRsMgrpp": {
+ "attributes": {
+ "tCl": "maintMaintP",
+ "tDn": "uni/fabric/maintpol-LEAF",
+ "tnMaintMaintPName": "LEAF"
+ }
+ }
+ },
+ {
+ "fabricNodeBlk": {
+ "attributes": {"name": "blk101-101", "from_": "101", "to_": "101"}
+ }
+ },
+ {
+ "fabricNodeBlk": {
+ "attributes": {"name": "blk102-102", "from_": "102", "to_": "102"}
+ }
+ },
+ {
+ "fabricNodeBlk": {
+ "attributes": {"name": "blk111-111", "from_": "111", "to_": "111"}
+ }
+ },
+ {
+ "fabricNodeBlk": {
+ "attributes": {"name": "blk112-112", "from_": "112", "to_": "112"}
+ }
+ },
+ {
+ "fabricNodeBlk": {
+ "attributes": {"name": "blk121-121", "from_": "121", "to_": "121"}
+ }
+ },
+ {
+ "fabricNodeBlk": {
+ "attributes": {"name": "blk122-122", "from_": "122", "to_": "122"}
+ }
+ },
+ {
+ "fabricNodeBlk": {
+ "attributes": {"name": "blk201-201", "from_": "201", "to_": "201"}
+ }
+ },
+ {
+ "fabricNodeBlk": {
+ "attributes": {"name": "blk202-202", "from_": "202", "to_": "202"}
+ }
+ }
+ ]
+ }
+ }
+]
diff --git a/tests/checks/switch_group_guideline_check/test_switch_group_guideline_check.py b/tests/checks/switch_group_guideline_check/test_switch_group_guideline_check.py
new file mode 100644
index 00000000..332e85f2
--- /dev/null
+++ b/tests/checks/switch_group_guideline_check/test_switch_group_guideline_check.py
@@ -0,0 +1,193 @@
+import os
+import pytest
+import logging
+import importlib
+from helpers.utils import read_data
+
+script = importlib.import_module("aci-preupgrade-validation-script")
+
+log = logging.getLogger(__name__)
+dir = os.path.dirname(os.path.abspath(__file__))
+
+test_function = "switch_group_guideline_check"
+
+# icurl queries
+upgrade_grp = "maintMaintGrp.json?rsp-subtree=children"
+bgp_rr = "bgpRRNodePEp.json"
+ipn_spines = 'l3extRsNodeL3OutAtt.json?query-target-filter=wcard(l3extRsNodeL3OutAtt.dn,"tn-infra/")'
+apic_lldp = "lldpCtrlrAdjEp.json"
+vpc_pairs = "fabricExplicitGEp.json?rsp-subtree=children&rsp-subtree-class=fabricNodePEp"
+
+
+@pytest.mark.parametrize(
+ "icurl_outputs, fabric_nodes, expected_result, expected_data",
+ [
+ # PASS
+ # Upgrade Grp: EVEN and ODD
+ # Spines:
+ # [Pod 1] 1001-1004, RR: 1001,1002, IPN 1001,1002
+ # [Pod 2] 2001-2002, RR: 2001,2002, IPN 2001,2002
+ # APIC Leaves:
+ # [Pod 1] 101-102 (APIC 1, 2)
+ # [Pod 2] 201-202 (APIC 3)
+ # VPC Leaves:
+ # [Pod 1] 101-102, 111-112
+ # [Pod 2] 201-202
+ (
+ {
+ upgrade_grp: read_data(dir, "maintMaintGrp_EVEN_ODD.json"),
+ bgp_rr: read_data(dir, "bgpRRNodePEp_1001_1002_2001_2002.json"),
+ ipn_spines: read_data(dir, "l3extRsNodeL3OutAtt_1001_1002_2001_2002.json"),
+ apic_lldp: read_data(dir, "lldpCtrlrAdjEp.json"),
+ vpc_pairs: read_data(dir, "fabricExplicitGEp.json"),
+ },
+ read_data(dir, "fabricNode.json"),
+ script.PASS,
+ [],
+ ),
+ # FAIL - All HA broken
+ # Upgrade Grp: all in one
+ # Spines:
+ # [Pod 1] 1001-1004, RR: 1001,1002, IPN 1001,1002
+ # [Pod 2] 2001-2002, RR: 2001,2002, IPN 2001,2002
+ # APIC Leaves:
+ # [Pod 1] 101-102 (APIC 1, 2)
+ # [Pod 2] 201-202 (APIC 3)
+ # VPC Leaves:
+ # [Pod 1] 101-102, 111-112
+ # [Pod 2] 201-202
+ (
+ {
+ upgrade_grp: read_data(dir, "maintMaintGrp_ALL.json"),
+ bgp_rr: read_data(dir, "bgpRRNodePEp_1001_1002_2001_2002.json"),
+ ipn_spines: read_data(dir, "l3extRsNodeL3OutAtt_1001_1002_2001_2002.json"),
+ apic_lldp: read_data(dir, "lldpCtrlrAdjEp.json"),
+ vpc_pairs: read_data(dir, "fabricExplicitGEp.json"),
+ },
+ read_data(dir, "fabricNode.json"),
+ script.FAIL_O,
+ [
+ ["ALL", "1", "1001,1002,1003,1004", "All spine nodes in this pod are in the same group."],
+ ["ALL", "2", "2001,2002", "All spine nodes in this pod are in the same group."],
+ ["ALL", "1", "1001,1002", "All RR spine nodes in this pod are in the same group."],
+ ["ALL", "2", "2001,2002", "All RR spine nodes in this pod are in the same group."],
+ ["ALL", "1", "1001,1002", "All IPN/ISN spine nodes in this pod are in the same group."],
+ ["ALL", "2", "2001,2002", "All IPN/ISN spine nodes in this pod are in the same group."],
+ ["ALL", "1", "101,102", "All leaf nodes connected to APIC 1 are in the same group."],
+ ["ALL", "1", "101,102", "All leaf nodes connected to APIC 2 are in the same group."],
+ ["ALL", "2", "201,202", "All leaf nodes connected to APIC 3 are in the same group."],
+ ["ALL", "1", "101,102", "Both leaf nodes in the same vPC pair are in the same group."],
+ ["ALL", "1", "111,112", "Both leaf nodes in the same vPC pair are in the same group."],
+ ["ALL", "2", "201,202", "Both leaf nodes in the same vPC pair are in the same group."],
+ ],
+ ),
+ # FAIL - All HA broken
+ # Upgrade Grp: leaves in one group and spines in another
+ # Spines:
+ # [Pod 1] 1001-1004, RR: 1001,1002, IPN 1001,1002
+ # [Pod 2] 2001-2002, RR: 2001,2002, IPN 2001,2002
+ # APIC Leaves:
+ # [Pod 1] 101-102 (APIC 1, 2)
+ # [Pod 2] 201-202 (APIC 3)
+ # VPC Leaves:
+ # [Pod 1] 101-102, 111-112
+ # [Pod 2] 201-202
+ (
+ {
+ upgrade_grp: read_data(dir, "maintMaintGrp_SPINE_LEAF.json"),
+ bgp_rr: read_data(dir, "bgpRRNodePEp_1001_1002_2001_2002.json"),
+ ipn_spines: read_data(dir, "l3extRsNodeL3OutAtt_1001_1002_2001_2002.json"),
+ apic_lldp: read_data(dir, "lldpCtrlrAdjEp.json"),
+ vpc_pairs: read_data(dir, "fabricExplicitGEp.json"),
+ },
+ read_data(dir, "fabricNode.json"),
+ script.FAIL_O,
+ [
+ ["SPINE", "1", "1001,1002,1003,1004", "All spine nodes in this pod are in the same group."],
+ ["SPINE", "2", "2001,2002", "All spine nodes in this pod are in the same group."],
+ ["SPINE", "1", "1001,1002", "All RR spine nodes in this pod are in the same group."],
+ ["SPINE", "2", "2001,2002", "All RR spine nodes in this pod are in the same group."],
+ ["SPINE", "1", "1001,1002", "All IPN/ISN spine nodes in this pod are in the same group."],
+ ["SPINE", "2", "2001,2002", "All IPN/ISN spine nodes in this pod are in the same group."],
+ ["LEAF", "1", "101,102", "All leaf nodes connected to APIC 1 are in the same group."],
+ ["LEAF", "1", "101,102", "All leaf nodes connected to APIC 2 are in the same group."],
+ ["LEAF", "2", "201,202", "All leaf nodes connected to APIC 3 are in the same group."],
+ ["LEAF", "1", "101,102", "Both leaf nodes in the same vPC pair are in the same group."],
+ ["LEAF", "1", "111,112", "Both leaf nodes in the same vPC pair are in the same group."],
+ ["LEAF", "2", "201,202", "Both leaf nodes in the same vPC pair are in the same group."],
+ ],
+ ),
+ # FAIL - All HA except for pod1 spine broken
+ # Upgrade Grp:
+ # GRP1: 101,102,121,122,1001,1002
+ # GRP2: 111,112,201,202,1003,1004,2001,2003
+ # Spines:
+ # [Pod 1] 1001-1004, RR: 1001,1002, IPN 1001,1002
+ # [Pod 2] 2001-2002, RR: 2001,2002, IPN 2001,2002
+ # APIC Leaves:
+ # [Pod 1] 101-102 (APIC 1, 2)
+ # [Pod 2] 201-202 (APIC 3)
+ # VPC Leaves:
+ # [Pod 1] 101-102, 111-112
+ # [Pod 2] 201-202
+ (
+ {
+ upgrade_grp: read_data(dir, "maintMaintGrp_BAD_GRP1_GRP2.json"),
+ bgp_rr: read_data(dir, "bgpRRNodePEp_1001_1002_2001_2002.json"),
+ ipn_spines: read_data(dir, "l3extRsNodeL3OutAtt_1001_1002_2001_2002.json"),
+ apic_lldp: read_data(dir, "lldpCtrlrAdjEp.json"),
+ vpc_pairs: read_data(dir, "fabricExplicitGEp.json"),
+ },
+ read_data(dir, "fabricNode.json"),
+ script.FAIL_O,
+ [
+ ["GRP1", "1", "1001,1002", "All RR spine nodes in this pod are in the same group."],
+ ["GRP1", "1", "1001,1002", "All IPN/ISN spine nodes in this pod are in the same group."],
+ ["GRP1", "1", "101,102", "All leaf nodes connected to APIC 1 are in the same group."],
+ ["GRP1", "1", "101,102", "All leaf nodes connected to APIC 2 are in the same group."],
+ ["GRP1", "1", "101,102", "Both leaf nodes in the same vPC pair are in the same group."],
+ ["GRP2", "2", "2001,2002", "All spine nodes in this pod are in the same group."],
+ ["GRP2", "2", "2001,2002", "All RR spine nodes in this pod are in the same group."],
+ ["GRP2", "2", "2001,2002", "All IPN/ISN spine nodes in this pod are in the same group."],
+ ["GRP2", "2", "201,202", "All leaf nodes connected to APIC 3 are in the same group."],
+ ["GRP2", "1", "111,112", "Both leaf nodes in the same vPC pair are in the same group."],
+ ["GRP2", "2", "201,202", "Both leaf nodes in the same vPC pair are in the same group."],
+ ],
+ ),
+ # FAIL - Only pod1 spine RR HA is broken
+ # Upgrade Grp:
+ # SPINE_GRP1: 1001-1003, 2001
+ # SPINE_GRP2: 1004, 2002
+ # EVEN: even leaves
+ # ODD: odd leaves
+ # Spines:
+ # [Pod 1] 1001-1004, RR: 1001,1002, IPN 1003,1004
+ # [Pod 2] 2001-2002, RR: 2001,2002, IPN 2001,2002
+ # APIC Leaves:
+ # [Pod 1] 101-102 (APIC 1, 2)
+ # [Pod 2] 201-202 (APIC 3)
+ # VPC Leaves:
+ # [Pod 1] 101-102, 111-112
+ # [Pod 2] 201-202
+ (
+ {
+ upgrade_grp: read_data(dir, "maintMaintGrp_BAD_ONLY_POD1_SPINE_RR.json"),
+ bgp_rr: read_data(dir, "bgpRRNodePEp_1001_1002_2001_2002.json"),
+ ipn_spines: read_data(dir, "l3extRsNodeL3OutAtt_1003_1004_2001_2002.json"),
+ apic_lldp: read_data(dir, "lldpCtrlrAdjEp.json"),
+ vpc_pairs: read_data(dir, "fabricExplicitGEp.json"),
+ },
+ read_data(dir, "fabricNode.json"),
+ script.FAIL_O,
+ [
+ ["SPINE_GRP1", "1", "1001,1002", "All RR spine nodes in this pod are in the same group."],
+ ],
+ ),
+ ],
+)
+def test_logic(run_check, mock_icurl, fabric_nodes, expected_result, expected_data):
+ result = run_check(
+ fabric_nodes=fabric_nodes,
+ )
+ assert result.result == expected_result
+ assert result.data == expected_data
diff --git a/tests/checks/switch_ssd_check/faultInst.json b/tests/checks/switch_ssd_check/faultInst.json
new file mode 100644
index 00000000..4d3166b7
--- /dev/null
+++ b/tests/checks/switch_ssd_check/faultInst.json
@@ -0,0 +1,28 @@
+[
+ {
+ "faultInst": {
+ "attributes": {
+ "cause": "equipment-flash-worn-out",
+ "changeSet": "acc:read-write, cap:244198, deltape:0, descr:flash, gbb:0, id:1, lba:0, lifetime:155, majorAlarm:yes, mfgTm:2025-11-03T04:22:06.834+00:00, minorAlarm:no, model:Micron_M550_MTFDDAT256MAY, operSt:ok, peCycles:4285, readErr:39, rev:MU03, ser:14270C7D9F13, tbw:336.453735, type:flash, vendor:Micron, warning:no, wlc:0",
+ "code": "F3073",
+ "descr": "SSD has reached 90% lifetime endurance limit. Please replace Switch/Supervisor with ID 0 as soon as possible",
+ "dn": "topology/pod-1/node-205/sys/ch/supslot-1/sup/flash/fault-F3073",
+ "rule": "eqpt-flash-flash-worn-out",
+ "subject": "flash-worn-out"
+ }
+ }
+ },
+ {
+ "faultInst": {
+ "attributes": {
+ "cause": "equipment-flash-warning",
+ "changeSet": "acc:read-write, cap:61057, deltape:23, descr:flash, gbb:0, id:1, lba:0, lifetime:85, majorAlarm:no, mfgTm:2020-09-22T02:21:45.675+00:00, minorAlarm:yes, model:Micron_M600_MTFDDAT064MBF, operSt:ok, peCycles:4290, readErr:0, rev:MC04, ser:MSA20400892, tbw:21.279228, type:flash, vendor:Micron, warning:yes, wlc:0",
+ "code": "F3074",
+ "descr": "SSD has reached 80% lifetime and is nearing its endurance limit. Please plan for Switch/Supervisor replacement soon",
+ "dn": "topology/pod-1/node-101/sys/ch/supslot-1/sup/flash/fault-F3074",
+ "rule": "eqpt-flash-flash-minor-alarm",
+ "subject": "flash-minor-alarm"
+ }
+ }
+ }
+]
diff --git a/tests/checks/switch_ssd_check/test_switch_ssd_check.py b/tests/checks/switch_ssd_check/test_switch_ssd_check.py
new file mode 100644
index 00000000..1f7202f5
--- /dev/null
+++ b/tests/checks/switch_ssd_check/test_switch_ssd_check.py
@@ -0,0 +1,54 @@
+import os
+import pytest
+import logging
+import importlib
+from helpers.utils import read_data
+
+script = importlib.import_module("aci-preupgrade-validation-script")
+Result = script.Result
+
+log = logging.getLogger(__name__)
+dir = os.path.dirname(os.path.abspath(__file__))
+
+test_function = "switch_ssd_check"
+
+# icurl queries
+faultInst = 'faultInst.json?query-target-filter=or(eq(faultInst.code,"F3073"),eq(faultInst.code,"F3074"))'
+
+
+@pytest.mark.parametrize(
+ "icurl_outputs, expected_result, expected_data",
+ [
+ (
+ {faultInst: []},
+ script.PASS,
+ [],
+ ),
+ (
+ {faultInst: read_data(dir, "faultInst.json")},
+ script.FAIL_O,
+ [
+ [
+ "F3073",
+ "1",
+ "205",
+ "Micron_M550_MTFDDAT256MAY",
+ "90%",
+ "Contact Cisco TAC for replacement procedure",
+ ],
+ [
+ "F3074",
+ "1",
+ "101",
+ "Micron_M600_MTFDDAT064MBF",
+ "80%",
+ "Monitor (no impact to upgrades)",
+ ],
+ ],
+ ),
+ ],
+)
+def test_logic(run_check, mock_icurl, expected_result, expected_data):
+ result = run_check()
+ assert result.result == expected_result
+ assert result.data == expected_data
diff --git a/tests/checks/switch_status_check/fabricNode_NEG.json b/tests/checks/switch_status_check/fabricNode_NEG.json
new file mode 100644
index 00000000..3dffa268
--- /dev/null
+++ b/tests/checks/switch_status_check/fabricNode_NEG.json
@@ -0,0 +1,122 @@
+[
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-1/node-1",
+ "fabricSt": "commissioned",
+ "id": "1",
+ "name": "apic1",
+ "role": "controller",
+ "nodeType": "unspecified"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-1/node-101",
+ "fabricSt": "active",
+ "id": "101",
+ "name": "LF101",
+ "role": "leaf",
+ "nodeType": "unspecified"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-1/node-102",
+ "fabricSt": "active",
+ "id": "102",
+ "name": "LF102",
+ "role": "leaf",
+ "nodeType": "unspecified"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-1/node-103",
+ "fabricSt": "active",
+ "id": "103",
+ "name": "LF103",
+ "role": "leaf",
+ "nodeType": "unspecified"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-1/node-111",
+ "fabricSt": "active",
+ "id": "111",
+ "name": "T2_LF111",
+ "role": "leaf",
+ "nodeType": "tier-2-leaf"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-1/node-112",
+ "fabricSt": "active",
+ "id": "112",
+ "name": "T2_LF112",
+ "role": "leaf",
+ "nodeType": "tier-2-leaf"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-1/node-121",
+ "fabricSt": "active",
+ "id": "121",
+ "name": "RL_LF121",
+ "role": "leaf",
+ "nodeType": "remote-leaf-wan"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-1/node-122",
+ "fabricSt": "active",
+ "id": "122",
+ "name": "RL_LF122",
+ "role": "leaf",
+ "nodeType": "remote-leaf-wan"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-1/node-1001",
+ "fabricSt": "active",
+ "id": "1001",
+ "name": "SP1001",
+ "role": "spine",
+ "nodeType": "unspecified"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-1/node-1002",
+ "fabricSt": "active",
+ "id": "1002",
+ "name": "SP1002",
+ "role": "spine",
+ "nodeType": "unspecified"
+ }
+ }
+ }
+]
diff --git a/tests/checks/switch_status_check/fabricNode_POS.json b/tests/checks/switch_status_check/fabricNode_POS.json
new file mode 100644
index 00000000..1a009a17
--- /dev/null
+++ b/tests/checks/switch_status_check/fabricNode_POS.json
@@ -0,0 +1,122 @@
+[
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-1/node-1",
+ "fabricSt": "commissioned",
+ "id": "1",
+ "name": "apic1",
+ "role": "controller",
+ "nodeType": "unspecified"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-1/node-101",
+ "fabricSt": "active",
+ "id": "101",
+ "name": "LF101",
+ "role": "leaf",
+ "nodeType": "unspecified"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-1/node-102",
+ "fabricSt": "active",
+ "id": "102",
+ "name": "LF102",
+ "role": "leaf",
+ "nodeType": "unspecified"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-1/node-103",
+ "fabricSt": "inactive",
+ "id": "103",
+ "name": "LF103",
+ "role": "leaf",
+ "nodeType": "unspecified"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-1/node-111",
+ "fabricSt": "active",
+ "id": "111",
+ "name": "T2_LF111",
+ "role": "leaf",
+ "nodeType": "tier-2-leaf"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-1/node-112",
+ "fabricSt": "inactive",
+ "id": "112",
+ "name": "T2_LF112",
+ "role": "leaf",
+ "nodeType": "tier-2-leaf"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-1/node-121",
+ "fabricSt": "inactive",
+ "id": "121",
+ "name": "RL_LF121",
+ "role": "leaf",
+ "nodeType": "remote-leaf-wan"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-1/node-122",
+ "fabricSt": "active",
+ "id": "122",
+ "name": "RL_LF122",
+ "role": "leaf",
+ "nodeType": "remote-leaf-wan"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-1/node-1001",
+ "fabricSt": "active",
+ "id": "1001",
+ "name": "SP1001",
+ "role": "spine",
+ "nodeType": "unspecified"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-1/node-1002",
+ "fabricSt": "active",
+ "id": "1002",
+ "name": "SP1002",
+ "role": "spine",
+ "nodeType": "unspecified"
+ }
+ }
+ }
+]
diff --git a/tests/checks/switch_status_check/fabricRsDecommissionNode.json b/tests/checks/switch_status_check/fabricRsDecommissionNode.json
new file mode 100644
index 00000000..9fb2e770
--- /dev/null
+++ b/tests/checks/switch_status_check/fabricRsDecommissionNode.json
@@ -0,0 +1,12 @@
+[
+ {
+ "fabricRsDecommissionNode": {
+ "attributes": {
+ "dn": "uni/fabric/outofsvc/rsdecommissionNode-[topology/pod-1/node-112]",
+ "debug": "yes",
+ "tDn": "topology/pod-1/node-112",
+ "targetId": "112"
+ }
+ }
+ }
+]
diff --git a/tests/checks/switch_status_check/test_switch_status_check.py b/tests/checks/switch_status_check/test_switch_status_check.py
new file mode 100644
index 00000000..e16f5012
--- /dev/null
+++ b/tests/checks/switch_status_check/test_switch_status_check.py
@@ -0,0 +1,46 @@
+import os
+import pytest
+import logging
+import importlib
+from helpers.utils import read_data
+
+script = importlib.import_module("aci-preupgrade-validation-script")
+
+log = logging.getLogger(__name__)
+dir = os.path.dirname(os.path.abspath(__file__))
+
+test_function = "switch_status_check"
+
+# icurl queries
+gir_nodes = 'fabricRsDecommissionNode.json?&query-target-filter=eq(fabricRsDecommissionNode.debug,"yes")'
+
+
+@pytest.mark.parametrize(
+ "icurl_outputs, fabric_nodes, expected_result, expected_data",
+ [
+ # FAIL - Some switches are not active
+ (
+ {gir_nodes: read_data(dir, "fabricRsDecommissionNode.json")},
+ read_data(dir, "fabricNode_POS.json"),
+ script.FAIL_UF,
+ [
+ ["1", "103", "inactive"],
+ ["1", "112", "inactive (Maintenance)"],
+ ["1", "121", "inactive"],
+ ],
+ ),
+ # PASS - All switches are active
+ (
+ {gir_nodes: []},
+ read_data(dir, "fabricNode_NEG.json"),
+ script.PASS,
+ [],
+ ),
+ ],
+)
+def test_logic(run_check, mock_icurl, fabric_nodes, expected_result, expected_data):
+ result = run_check(
+ fabric_nodes=fabric_nodes,
+ )
+ assert result.result == expected_result
+ assert result.data == expected_data
diff --git a/tests/telemetryStatsServerP_object_check/telemetryStatsServerP_neg.json b/tests/checks/telemetryStatsServerP_object_check/telemetryStatsServerP_neg.json
similarity index 100%
rename from tests/telemetryStatsServerP_object_check/telemetryStatsServerP_neg.json
rename to tests/checks/telemetryStatsServerP_object_check/telemetryStatsServerP_neg.json
diff --git a/tests/telemetryStatsServerP_object_check/telemetryStatsServerP_pos.json b/tests/checks/telemetryStatsServerP_object_check/telemetryStatsServerP_pos.json
similarity index 100%
rename from tests/telemetryStatsServerP_object_check/telemetryStatsServerP_pos.json
rename to tests/checks/telemetryStatsServerP_object_check/telemetryStatsServerP_pos.json
diff --git a/tests/telemetryStatsServerP_object_check/test_telemetryStatsServerP_object_check.py b/tests/checks/telemetryStatsServerP_object_check/test_telemetryStatsServerP_object_check.py
similarity index 86%
rename from tests/telemetryStatsServerP_object_check/test_telemetryStatsServerP_object_check.py
rename to tests/checks/telemetryStatsServerP_object_check/test_telemetryStatsServerP_object_check.py
index 9c234348..10d6fef9 100644
--- a/tests/telemetryStatsServerP_object_check/test_telemetryStatsServerP_object_check.py
+++ b/tests/checks/telemetryStatsServerP_object_check/test_telemetryStatsServerP_object_check.py
@@ -9,6 +9,7 @@
log = logging.getLogger(__name__)
dir = os.path.dirname(os.path.abspath(__file__))
+test_function = "telemetryStatsServerP_object_check"
# icurl queries
telemetryStatsServerPs = "telemetryStatsServerP.json"
@@ -79,11 +80,9 @@
),
],
)
-def test_logic(mock_icurl, sw_cversion, tversion, expected_result):
- result = script.telemetryStatsServerP_object_check(
- 1,
- 1,
- script.AciVersion(sw_cversion),
- script.AciVersion(tversion) if tversion else None,
+def test_logic(run_check, mock_icurl, sw_cversion, tversion, expected_result):
+ result = run_check(
+ sw_cversion=script.AciVersion(sw_cversion),
+ tversion=script.AciVersion(tversion) if tversion else None,
)
- assert result == expected_result
+ assert result.result == expected_result
diff --git a/tests/tep-to-tep_atomic_counter_check/dbgAcPath_max.json b/tests/checks/tep-to-tep_atomic_counter_check/dbgAcPath_max.json
similarity index 100%
rename from tests/tep-to-tep_atomic_counter_check/dbgAcPath_max.json
rename to tests/checks/tep-to-tep_atomic_counter_check/dbgAcPath_max.json
diff --git a/tests/tep-to-tep_atomic_counter_check/dbgAcPath_na.json b/tests/checks/tep-to-tep_atomic_counter_check/dbgAcPath_na.json
similarity index 100%
rename from tests/tep-to-tep_atomic_counter_check/dbgAcPath_na.json
rename to tests/checks/tep-to-tep_atomic_counter_check/dbgAcPath_na.json
diff --git a/tests/tep-to-tep_atomic_counter_check/dbgAcPath_pass.json b/tests/checks/tep-to-tep_atomic_counter_check/dbgAcPath_pass.json
similarity index 100%
rename from tests/tep-to-tep_atomic_counter_check/dbgAcPath_pass.json
rename to tests/checks/tep-to-tep_atomic_counter_check/dbgAcPath_pass.json
diff --git a/tests/tep-to-tep_atomic_counter_check/test_tep_to_tep_ac_count_check.py b/tests/checks/tep-to-tep_atomic_counter_check/test_tep_to_tep_ac_count_check.py
similarity index 71%
rename from tests/tep-to-tep_atomic_counter_check/test_tep_to_tep_ac_count_check.py
rename to tests/checks/tep-to-tep_atomic_counter_check/test_tep_to_tep_ac_count_check.py
index 447adaf1..6db17907 100644
--- a/tests/tep-to-tep_atomic_counter_check/test_tep_to_tep_ac_count_check.py
+++ b/tests/checks/tep-to-tep_atomic_counter_check/test_tep_to_tep_ac_count_check.py
@@ -9,6 +9,8 @@
log = logging.getLogger(__name__)
dir = os.path.dirname(os.path.abspath(__file__))
+test_function = "tep_to_tep_ac_counter_check"
+
# icurl queries
atomic_counter_api = 'dbgAcPath.json'
atomic_counter_api += '?rsp-subtree-include=count'
@@ -17,26 +19,23 @@
@pytest.mark.parametrize(
"icurl_outputs, expected_result",
[
- ##FAILING = COUNT > 1600
+ # FAILING = COUNT > 1600
(
- {atomic_counter_api: read_data(dir, "dbgAcPath_max.json"),
- },
+ {atomic_counter_api: read_data(dir, "dbgAcPath_max.json")},
script.FAIL_UF,
),
- ##PASSING = COUNT > 0 < = 1600
+ # PASSING = COUNT > 0 < = 1600
(
- {atomic_counter_api: read_data(dir, "dbgAcPath_pass.json"),
- },
+ {atomic_counter_api: read_data(dir, "dbgAcPath_pass.json")},
script.PASS,
),
- ##N/A = COUNT EQUAL 0
+ # N/A = COUNT EQUAL 0
(
- {atomic_counter_api: read_data(dir, "dbgAcPath_na.json"),
- },
+ {atomic_counter_api: read_data(dir, "dbgAcPath_na.json")},
script.NA,
),
],
)
-def test_logic(mock_icurl, expected_result):
- result = script.tep_to_tep_ac_counter_check(1, 1)
- assert result == expected_result
\ No newline at end of file
+def test_logic(run_check, mock_icurl, expected_result):
+ result = run_check()
+ assert result.result == expected_result
diff --git a/tests/unsupported_fec_configuration_ex_check/test_unsupported_fec_configuration_ex_check.py b/tests/checks/unsupported_fec_configuration_ex_check/test_unsupported_fec_configuration_ex_check.py
similarity index 82%
rename from tests/unsupported_fec_configuration_ex_check/test_unsupported_fec_configuration_ex_check.py
rename to tests/checks/unsupported_fec_configuration_ex_check/test_unsupported_fec_configuration_ex_check.py
index 0990c762..d9fd9a86 100644
--- a/tests/unsupported_fec_configuration_ex_check/test_unsupported_fec_configuration_ex_check.py
+++ b/tests/checks/unsupported_fec_configuration_ex_check/test_unsupported_fec_configuration_ex_check.py
@@ -9,6 +9,7 @@
log = logging.getLogger(__name__)
dir = os.path.dirname(os.path.abspath(__file__))
+test_function = "unsupported_fec_configuration_ex_check"
# icurl queries
topSystems = 'topSystem.json'
@@ -16,6 +17,7 @@
topSystems += '&rsp-subtree-filter=or(eq(l1PhysIf.fecMode,"ieee-rs-fec"),eq(l1PhysIf.fecMode,"cons16-rs-fec"),eq(eqptCh.model,"N9K-C93180YC-EX"))'
topSystems += '&rsp-subtree-include=required'
+
@pytest.mark.parametrize(
"icurl_outputs, sw_cversion, tversion, expected_result",
[
@@ -63,11 +65,9 @@
),
],
)
-def test_logic(mock_icurl, sw_cversion, tversion, expected_result):
- result = script.unsupported_fec_configuration_ex_check(
- 1,
- 1,
- script.AciVersion(sw_cversion) if sw_cversion else None,
- script.AciVersion(tversion) if tversion else None,
+def test_logic(run_check, mock_icurl, sw_cversion, tversion, expected_result):
+ result = run_check(
+ sw_cversion=script.AciVersion(sw_cversion) if sw_cversion else None,
+ tversion=script.AciVersion(tversion) if tversion else None,
)
- assert result == expected_result
\ No newline at end of file
+ assert result.result == expected_result
diff --git a/tests/unsupported_fec_configuration_ex_check/topSystem_neg.json b/tests/checks/unsupported_fec_configuration_ex_check/topSystem_neg.json
similarity index 100%
rename from tests/unsupported_fec_configuration_ex_check/topSystem_neg.json
rename to tests/checks/unsupported_fec_configuration_ex_check/topSystem_neg.json
diff --git a/tests/unsupported_fec_configuration_ex_check/topSystem_pos.json b/tests/checks/unsupported_fec_configuration_ex_check/topSystem_pos.json
similarity index 100%
rename from tests/unsupported_fec_configuration_ex_check/topSystem_pos.json
rename to tests/checks/unsupported_fec_configuration_ex_check/topSystem_pos.json
diff --git a/tests/uplink_limit_check/eqptPortP_NEG.json b/tests/checks/uplink_limit_check/eqptPortP_NEG.json
similarity index 100%
rename from tests/uplink_limit_check/eqptPortP_NEG.json
rename to tests/checks/uplink_limit_check/eqptPortP_NEG.json
diff --git a/tests/uplink_limit_check/eqptPortP_POS.json b/tests/checks/uplink_limit_check/eqptPortP_POS.json
similarity index 100%
rename from tests/uplink_limit_check/eqptPortP_POS.json
rename to tests/checks/uplink_limit_check/eqptPortP_POS.json
diff --git a/tests/uplink_limit_check/eqptPortP_empty.json b/tests/checks/uplink_limit_check/eqptPortP_empty.json
similarity index 100%
rename from tests/uplink_limit_check/eqptPortP_empty.json
rename to tests/checks/uplink_limit_check/eqptPortP_empty.json
diff --git a/tests/uplink_limit_check/test_uplink_limit_check.py b/tests/checks/uplink_limit_check/test_uplink_limit_check.py
similarity index 82%
rename from tests/uplink_limit_check/test_uplink_limit_check.py
rename to tests/checks/uplink_limit_check/test_uplink_limit_check.py
index 0cf9dc82..128ae316 100644
--- a/tests/uplink_limit_check/test_uplink_limit_check.py
+++ b/tests/checks/uplink_limit_check/test_uplink_limit_check.py
@@ -8,6 +8,7 @@
log = logging.getLogger(__name__)
dir = os.path.dirname(os.path.abspath(__file__))
+test_function = "uplink_limit_check"
# icurl queries
eqptPortP = 'eqptPortP.json?query-target-filter=eq(eqptPortP.ctrl,"uplink")'
@@ -50,8 +51,9 @@
)
],
)
-def test_logic(mock_icurl, cver, tver, expected_result):
- result = script.uplink_limit_check(
- 1, 1, script.AciVersion(cver), script.AciVersion(tver)
+def test_logic(run_check, mock_icurl, cver, tver, expected_result):
+ result = run_check(
+ cversion=script.AciVersion(cver),
+ tversion=script.AciVersion(tver),
)
- assert result == expected_result
+ assert result.result == expected_result
diff --git a/tests/validate_32_64_bit_image_check/firmwareFirmware_empty.json b/tests/checks/validate_32_64_bit_image_check/firmwareFirmware_empty.json
similarity index 100%
rename from tests/validate_32_64_bit_image_check/firmwareFirmware_empty.json
rename to tests/checks/validate_32_64_bit_image_check/firmwareFirmware_empty.json
diff --git a/tests/validate_32_64_bit_image_check/firmwareFirmware_neg.json b/tests/checks/validate_32_64_bit_image_check/firmwareFirmware_neg.json
similarity index 100%
rename from tests/validate_32_64_bit_image_check/firmwareFirmware_neg.json
rename to tests/checks/validate_32_64_bit_image_check/firmwareFirmware_neg.json
diff --git a/tests/validate_32_64_bit_image_check/firmwareFirmware_pos.json b/tests/checks/validate_32_64_bit_image_check/firmwareFirmware_pos.json
similarity index 100%
rename from tests/validate_32_64_bit_image_check/firmwareFirmware_pos.json
rename to tests/checks/validate_32_64_bit_image_check/firmwareFirmware_pos.json
diff --git a/tests/validate_32_64_bit_image_check/firmwareFirmware_pos2.json b/tests/checks/validate_32_64_bit_image_check/firmwareFirmware_pos2.json
similarity index 100%
rename from tests/validate_32_64_bit_image_check/firmwareFirmware_pos2.json
rename to tests/checks/validate_32_64_bit_image_check/firmwareFirmware_pos2.json
diff --git a/tests/validate_32_64_bit_image_check/firmwareFirmware_pos3.json b/tests/checks/validate_32_64_bit_image_check/firmwareFirmware_pos3.json
similarity index 100%
rename from tests/validate_32_64_bit_image_check/firmwareFirmware_pos3.json
rename to tests/checks/validate_32_64_bit_image_check/firmwareFirmware_pos3.json
diff --git a/tests/validate_32_64_bit_image_check/firmwareFirmware_pos4.json b/tests/checks/validate_32_64_bit_image_check/firmwareFirmware_pos4.json
similarity index 100%
rename from tests/validate_32_64_bit_image_check/firmwareFirmware_pos4.json
rename to tests/checks/validate_32_64_bit_image_check/firmwareFirmware_pos4.json
diff --git a/tests/validate_32_64_bit_image_check/test_validate_32_64_bit_image_check.py b/tests/checks/validate_32_64_bit_image_check/test_validate_32_64_bit_image_check.py
similarity index 59%
rename from tests/validate_32_64_bit_image_check/test_validate_32_64_bit_image_check.py
rename to tests/checks/validate_32_64_bit_image_check/test_validate_32_64_bit_image_check.py
index 75eac752..2371d5ad 100644
--- a/tests/validate_32_64_bit_image_check/test_validate_32_64_bit_image_check.py
+++ b/tests/checks/validate_32_64_bit_image_check/test_validate_32_64_bit_image_check.py
@@ -9,97 +9,88 @@
log = logging.getLogger(__name__)
dir = os.path.dirname(os.path.abspath(__file__))
+test_function = "validate_32_64_bit_image_check"
+
# icurl queries
-firmware_60_api = 'firmwareFirmware.json'
-firmware_60_api += '?query-target-filter=eq(firmwareFirmware.fullVersion,"n9000-16.0(3e)")'
+firmware_60_api = "firmwareFirmware.json"
+firmware_60_api += '?query-target-filter=eq(firmwareFirmware.fullVersion,"n9000-16.0(3e)")'
# icurl queries
-firmware_52_api = 'firmwareFirmware.json'
-firmware_52_api += '?query-target-filter=eq(firmwareFirmware.fullVersion,"n9000-15.2(7d)")'
+firmware_52_api = "firmwareFirmware.json"
+firmware_52_api += '?query-target-filter=eq(firmwareFirmware.fullVersion,"n9000-15.2(7d)")'
+
@pytest.mark.parametrize(
"icurl_outputs, cversion, tversion, expected_result",
[
- ## NO TVERSION - MANUAL
+ # NO TVERSION - MANUAL
(
- {firmware_60_api: read_data(dir, "firmwareFirmware_pos.json"),
- },
+ {firmware_60_api: read_data(dir, "firmwareFirmware_pos.json")},
"5.2(1a)",
None,
script.MANUAL,
),
- ## APIC not yet upgraded to 6.0(2)+ - POST
+ # APIC not yet upgraded to 6.0(2)+ - POST
(
- {firmware_60_api: read_data(dir, "firmwareFirmware_pos.json"),
- },
+ {firmware_60_api: read_data(dir, "firmwareFirmware_pos.json")},
"5.2(1a)",
"6.0(3e)",
script.POST,
),
- ## FAILING = AFFECTED VERSION + ONLY 64 BIT Image
+ # FAILING = AFFECTED VERSION + ONLY 64 BIT Image
(
- {firmware_60_api: read_data(dir, "firmwareFirmware_pos.json"),
- },
+ {firmware_60_api: read_data(dir, "firmwareFirmware_pos.json")},
"6.0(3e)",
"6.0(3e)",
script.FAIL_UF,
),
- ## FAILING = AFFECTED VERSION + Images were uploaded before upgrade
+ # FAILING = AFFECTED VERSION + Images were uploaded before upgrade
(
- {firmware_60_api: read_data(dir, "firmwareFirmware_pos2.json"),
- },
+ {firmware_60_api: read_data(dir, "firmwareFirmware_pos2.json")},
"6.0(3e)",
"6.0(3e)",
script.FAIL_UF,
),
- ## FAILING = AFFECTED VERSION + 32-bit image shows NA
+ # FAILING = AFFECTED VERSION + 32-bit image shows NA
(
- {firmware_60_api: read_data(dir, "firmwareFirmware_pos3.json"),
- },
+ {firmware_60_api: read_data(dir, "firmwareFirmware_pos3.json")},
"6.0(3e)",
"6.0(3e)",
script.FAIL_UF,
),
- ## FAILING = AFFECTED VERSION + 64-bit image shows NA
+ # FAILING = AFFECTED VERSION + 64-bit image shows NA
(
- {firmware_60_api: read_data(dir, "firmwareFirmware_pos4.json"),
- },
+ {firmware_60_api: read_data(dir, "firmwareFirmware_pos4.json")},
"6.0(3e)",
"6.0(3e)",
script.FAIL_UF,
),
- ## FAILING = AFFECTED VERSION + AFFECTED MO NON EXISTING
+ # FAILING = AFFECTED VERSION + AFFECTED MO NON EXISTING
(
- {firmware_60_api: read_data(dir, "firmwareFirmware_empty.json"),
- },
+ {firmware_60_api: read_data(dir, "firmwareFirmware_empty.json")},
"6.0(3e)",
"6.0(3e)",
script.FAIL_UF,
),
- ## PASSING = AFFECTED VERSION + NON-AFFECTED MO
+ # PASSING = AFFECTED VERSION + NON-AFFECTED MO
(
- {firmware_60_api: read_data(dir, "firmwareFirmware_neg.json"),
- },
+ {firmware_60_api: read_data(dir, "firmwareFirmware_neg.json")},
"6.0(3e)",
"6.0(3e)",
script.PASS,
),
- ## PASSING = NON-AFFECTED VERSION + AFFECTED MO
+ # PASSING = NON-AFFECTED VERSION + AFFECTED MO
(
- {firmware_52_api: read_data(dir, "firmwareFirmware_empty.json"),
- },
+ {firmware_52_api: read_data(dir, "firmwareFirmware_empty.json")},
"5.2(1a)",
"5.2(7d)",
script.NA,
),
-
],
)
-def test_logic(mock_icurl, cversion, tversion, expected_result):
- result = script.validate_32_64_bit_image_check(
- 1,
- 1,
- script.AciVersion(cversion),
- script.AciVersion(tversion) if tversion else None
+def test_logic(run_check, mock_icurl, cversion, tversion, expected_result):
+ result = run_check(
+ cversion=script.AciVersion(cversion),
+ tversion=script.AciVersion(tversion) if tversion else None
)
- assert result == expected_result
\ No newline at end of file
+ assert result.result == expected_result
diff --git a/tests/vmm_active_uplinks_check/fvUplinkOrderCont_neg.json b/tests/checks/vmm_active_uplinks_check/fvUplinkOrderCont_neg.json
similarity index 100%
rename from tests/vmm_active_uplinks_check/fvUplinkOrderCont_neg.json
rename to tests/checks/vmm_active_uplinks_check/fvUplinkOrderCont_neg.json
diff --git a/tests/vmm_active_uplinks_check/fvUplinkOrderCont_not_exist.json b/tests/checks/vmm_active_uplinks_check/fvUplinkOrderCont_not_exist.json
similarity index 100%
rename from tests/vmm_active_uplinks_check/fvUplinkOrderCont_not_exist.json
rename to tests/checks/vmm_active_uplinks_check/fvUplinkOrderCont_not_exist.json
diff --git a/tests/vmm_active_uplinks_check/fvUplinkOrderCont_pos.json b/tests/checks/vmm_active_uplinks_check/fvUplinkOrderCont_pos.json
similarity index 100%
rename from tests/vmm_active_uplinks_check/fvUplinkOrderCont_pos.json
rename to tests/checks/vmm_active_uplinks_check/fvUplinkOrderCont_pos.json
diff --git a/tests/vmm_active_uplinks_check/test_vmm_active_uplinks_check.py b/tests/checks/vmm_active_uplinks_check/test_vmm_active_uplinks_check.py
similarity index 78%
rename from tests/vmm_active_uplinks_check/test_vmm_active_uplinks_check.py
rename to tests/checks/vmm_active_uplinks_check/test_vmm_active_uplinks_check.py
index 846f3438..5920efd5 100644
--- a/tests/vmm_active_uplinks_check/test_vmm_active_uplinks_check.py
+++ b/tests/checks/vmm_active_uplinks_check/test_vmm_active_uplinks_check.py
@@ -9,9 +9,10 @@
log = logging.getLogger(__name__)
dir = os.path.dirname(os.path.abspath(__file__))
+test_function = "vmm_active_uplinks_check"
# icurl queries
-uplink_api = 'fvUplinkOrderCont.json'
+uplink_api = "fvUplinkOrderCont.json"
uplink_api += '?query-target-filter=eq(fvUplinkOrderCont.active,"")'
@@ -32,6 +33,6 @@
),
],
)
-def test_logic(mock_icurl, expected_result):
- result = script.vmm_active_uplinks_check(1, 1)
- assert result == expected_result
+def test_logic(run_check, mock_icurl, expected_result):
+ result = run_check()
+ assert result.result == expected_result
diff --git a/tests/checks/vpc_paired_switches_check/fabricNode.json b/tests/checks/vpc_paired_switches_check/fabricNode.json
new file mode 100644
index 00000000..73e7d5b8
--- /dev/null
+++ b/tests/checks/vpc_paired_switches_check/fabricNode.json
@@ -0,0 +1,145 @@
+[
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-1/node-1",
+ "id": "1",
+ "name": "apic1",
+ "role": "controller",
+ "nodeType": "unspecified"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-1/node-2",
+ "id": "2",
+ "name": "apic2",
+ "role": "controller",
+ "nodeType": "unspecified"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-1/node-3",
+ "id": "3",
+ "name": "apic3",
+ "role": "controller",
+ "nodeType": "unspecified"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-1/node-101",
+ "id": "101",
+ "name": "LF101",
+ "role": "leaf",
+ "nodeType": "unspecified"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-1/node-102",
+ "id": "102",
+ "name": "LF102",
+ "role": "leaf",
+ "nodeType": "unspecified"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-1/node-103",
+ "id": "103",
+ "name": "LF103",
+ "role": "leaf",
+ "nodeType": "unspecified"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-1/node-104",
+ "id": "104",
+ "name": "LF104",
+ "role": "leaf",
+ "nodeType": "unspecified"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-1/node-111",
+ "id": "111",
+ "name": "T2_LF111",
+ "role": "leaf",
+ "nodeType": "tier-2-leaf"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-1/node-112",
+ "id": "112",
+ "name": "T2_LF112",
+ "role": "leaf",
+ "nodeType": "tier-2-leaf"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-1/node-121",
+ "id": "121",
+ "name": "RL_LF121",
+ "role": "leaf",
+ "nodeType": "remote-leaf-wan"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-1/node-122",
+ "id": "122",
+ "name": "RL_LF122",
+ "role": "leaf",
+ "nodeType": "remote-leaf-wan"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-1/node-1001",
+ "id": "1001",
+ "name": "SP1001",
+ "role": "spine",
+ "nodeType": "unspecified"
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "dn": "topology/pod-1/node-1002",
+ "id": "1002",
+ "name": "SP1002",
+ "role": "spine",
+ "nodeType": "unspecified"
+ }
+ }
+ }
+]
diff --git a/tests/checks/vpc_paired_switches_check/test_vpc_paired_switches_check.py b/tests/checks/vpc_paired_switches_check/test_vpc_paired_switches_check.py
new file mode 100644
index 00000000..c6bb8e70
--- /dev/null
+++ b/tests/checks/vpc_paired_switches_check/test_vpc_paired_switches_check.py
@@ -0,0 +1,38 @@
+import os
+import pytest
+import logging
+import importlib
+from helpers.utils import read_data
+
+script = importlib.import_module("aci-preupgrade-validation-script")
+
+log = logging.getLogger(__name__)
+dir = os.path.dirname(os.path.abspath(__file__))
+
+test_function = "vpc_paired_switches_check"
+
+
+@pytest.mark.parametrize(
+ "vpc_node_ids, expected_result, expected_data",
+ [
+ # all leaf switches are in vPC
+ (
+ ["101", "102", "103", "104", "111", "112", "121", "122"],
+ script.PASS,
+ [],
+ ),
+ # not all leaf switches are in vPC
+ (
+ ["101", "102", "111", "112"],
+ script.MANUAL,
+ [["103", "LF103"], ["104", "LF104"], ["121", "RL_LF121"], ["122", "RL_LF122"]],
+ ),
+ ],
+)
+def test_logic(run_check, vpc_node_ids, expected_result, expected_data):
+ result = run_check(
+ vpc_node_ids=vpc_node_ids,
+ fabric_nodes=read_data(dir, "fabricNode.json"),
+ )
+ assert result.result == expected_result
+ assert result.data == expected_data
diff --git a/tests/vzany_vzany_service_epg_check/test_vzany_vzany_service_epg_check.py b/tests/checks/vzany_vzany_service_epg_check/test_vzany_vzany_service_epg_check.py
similarity index 92%
rename from tests/vzany_vzany_service_epg_check/test_vzany_vzany_service_epg_check.py
rename to tests/checks/vzany_vzany_service_epg_check/test_vzany_vzany_service_epg_check.py
index 91072c23..44f7f1ca 100644
--- a/tests/vzany_vzany_service_epg_check/test_vzany_vzany_service_epg_check.py
+++ b/tests/checks/vzany_vzany_service_epg_check/test_vzany_vzany_service_epg_check.py
@@ -9,6 +9,7 @@
log = logging.getLogger(__name__)
dir = os.path.dirname(os.path.abspath(__file__))
+test_function = "vzany_vzany_service_epg_check"
# icurl queries
vzRsSubjGraphAtts = "vzRsSubjGraphAtt.json"
@@ -120,11 +121,9 @@
),
],
)
-def test_logic(mock_icurl, cversion, tversion, expected_result):
- result = script.vzany_vzany_service_epg_check(
- 1,
- 1,
- script.AciVersion(cversion),
- script.AciVersion(tversion) if tversion else None,
+def test_logic(run_check, mock_icurl, cversion, tversion, expected_result):
+ result = run_check(
+ cversion=script.AciVersion(cversion),
+ tversion=script.AciVersion(tversion) if tversion else None,
)
- assert result == expected_result
+ assert result.result == expected_result
diff --git a/tests/vzany_vzany_service_epg_check/vzRsSubjGraphAtt.json b/tests/checks/vzany_vzany_service_epg_check/vzRsSubjGraphAtt.json
similarity index 100%
rename from tests/vzany_vzany_service_epg_check/vzRsSubjGraphAtt.json
rename to tests/checks/vzany_vzany_service_epg_check/vzRsSubjGraphAtt.json
diff --git a/tests/vzany_vzany_service_epg_check/vzRtAny_vzAny_prov_cons_diff_VRFs.json b/tests/checks/vzany_vzany_service_epg_check/vzRtAny_vzAny_prov_cons_diff_VRFs.json
similarity index 100%
rename from tests/vzany_vzany_service_epg_check/vzRtAny_vzAny_prov_cons_diff_VRFs.json
rename to tests/checks/vzany_vzany_service_epg_check/vzRtAny_vzAny_prov_cons_diff_VRFs.json
diff --git a/tests/vzany_vzany_service_epg_check/vzRtAny_vzAny_prov_only.json b/tests/checks/vzany_vzany_service_epg_check/vzRtAny_vzAny_prov_only.json
similarity index 100%
rename from tests/vzany_vzany_service_epg_check/vzRtAny_vzAny_prov_only.json
rename to tests/checks/vzany_vzany_service_epg_check/vzRtAny_vzAny_prov_only.json
diff --git a/tests/vzany_vzany_service_epg_check/vzRtAny_vzAny_vzAny.json b/tests/checks/vzany_vzany_service_epg_check/vzRtAny_vzAny_vzAny.json
similarity index 100%
rename from tests/vzany_vzany_service_epg_check/vzRtAny_vzAny_vzAny.json
rename to tests/checks/vzany_vzany_service_epg_check/vzRtAny_vzAny_vzAny.json
diff --git a/tests/vzany_vzany_service_epg_check/vzRtAny_vzAny_vzAny_2_VRFs.json b/tests/checks/vzany_vzany_service_epg_check/vzRtAny_vzAny_vzAny_2_VRFs.json
similarity index 100%
rename from tests/vzany_vzany_service_epg_check/vzRtAny_vzAny_vzAny_2_VRFs.json
rename to tests/checks/vzany_vzany_service_epg_check/vzRtAny_vzAny_vzAny_2_VRFs.json
diff --git a/tests/conftest.py b/tests/conftest.py
index 92ba9a81..348c7d70 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -3,7 +3,7 @@
import pytest
import logging
import importlib
-from subprocess import CalledProcessError
+from itertools import product
TEST_DIR = os.path.dirname(os.path.abspath(__file__))
PROJECT_DIR = os.path.dirname(TEST_DIR)
@@ -16,7 +16,7 @@
@pytest.fixture(scope="session", autouse=True)
def init():
- script.initialize()
+ script.init_system()
@pytest.fixture
@@ -93,8 +93,7 @@ def mock_icurl(monkeypatch, icurl_outputs):
def _mock_icurl(apitype, query, page=0, page_size=100000):
output = icurl_outputs.get(query)
if output is None:
- log.error("Query `%s` not found in test data", query)
- data = {"totalCount": "0", "imdata": []}
+ raise KeyError("Query `{}` not found in test data".format(query))
elif isinstance(output, list):
# icurl_outputs option 1 - output is imdata which is empty
if not output:
@@ -116,116 +115,365 @@ def _mock_icurl(apitype, query, page=0, page_size=100000):
else:
data = {"totalCount": output["totalCount"], "imdata": []}
- script._icurl_error_handler(data['imdata'])
+ script._icurl_error_handler(data["imdata"])
return data
monkeypatch.setattr(script, "_icurl", _mock_icurl)
@pytest.fixture
-def conn_failure():
- return False
-
-
-@pytest.fixture
-def conn_cmds():
- '''
- Set of test parameters for mocked `Connection.cmd()`.
-
- ex)
- ```
- {
- : [{
- "cmd": "ls -aslh /firmware/fwrepos/fwrepo/aci-apic-dk9.6.0.5h.bin",
- "output": """\
- ls -aslh /firmware/fwrepos/fwrepo/aci-apic-dk9.6.0.5h.bin
- 6.1G -rwxr-xr-x 1 root root 6.1G Apr 3 16:36 /firmware/fwrepos/fwrepo/aci-apic-dk9.6.0.5h.bin
- f2-apic1#
- """,
- "exception": None
- }]
+def expected_common_data(request):
+ data = { # default
+ "username": "admin",
+ "password": "mypassword",
+ "cversion": script.AciVersion("6.1(1a)"),
+ "tversion": script.AciVersion("6.2(1a)"),
+ "sw_cversion": script.AciVersion("6.0(9d)"),
+ "vpc_node_ids": ["101", "102"],
+ "fabric_nodes": [
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.1",
+ "dn": "topology/pod-1/node-1",
+ "fabricSt": "commissioned",
+ "id": "1",
+ "model": "APIC-SERVER-L2",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "apic1",
+ "nodeType": "unspecified",
+ "role": "controller",
+ "version": "6.1(1a)",
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.2",
+ "dn": "topology/pod-1/node-2",
+ "fabricSt": "commissioned",
+ "id": "2",
+ "model": "APIC-SERVER-L2",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "apic2",
+ "nodeType": "unspecified",
+ "role": "controller",
+ "version": "6.1(1a)",
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.3",
+ "dn": "topology/pod-2/node-3",
+ "fabricSt": "commissioned",
+ "id": "3",
+ "model": "APIC-SERVER-L2",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "apic3",
+ "nodeType": "unspecified",
+ "role": "controller",
+ "version": "6.1(1a)",
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.101",
+ "dn": "topology/pod-1/node-101",
+ "fabricSt": "active",
+ "id": "101",
+ "model": "N9K-C93180YC-FX",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "leaf101",
+ "nodeType": "unspecified",
+ "role": "leaf",
+ "version": "n9000-16.1(1a)",
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.102",
+ "dn": "topology/pod-1/node-102",
+ "fabricSt": "active",
+ "id": "102",
+ "model": "N9K-C93180YC-FX",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "leaf102",
+ "nodeType": "unspecified",
+ "role": "leaf",
+ "version": "n9000-16.1(1a)",
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.111",
+ "dn": "topology/pod-1/node-1001",
+ "fabricSt": "active",
+ "id": "1001",
+ "model": "N9K-C9504",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "spine1001",
+ "nodeType": "unspecified",
+ "role": "spine",
+ "version": "n9000-16.1(1a)",
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.201",
+ "dn": "topology/pod-2/node-201",
+ "fabricSt": "active",
+ "id": "201",
+ "model": "N9K-C93180YC-FX3",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "leaf201",
+ "nodeType": "unspecified",
+ "role": "leaf",
+ "version": "n9000-16.0(9d)",
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.211",
+ "dn": "topology/pod-2/node-2001",
+ "fabricSt": "active",
+ "id": "2001",
+ "model": "N9K-C9504",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "spine2001",
+ "nodeType": "unspecified",
+ "role": "spine",
+ "version": "n9000-16.1(1a)",
+ }
+ }
+ },
+ ],
}
- ```
-
- The real output from `Connection.cmd()` (i.e. `Connection.output`) contains many ANSI characters. In this fixture, those characters are not considered.
- '''
- return {}
-
-
-class MockConnection(script.Connection):
- conn_failure = False
- conn_cmds = None
-
- def connect(self):
- """
- `Connection.connect()` is just instantiating `pexepect.spawn()` which does not
- initiate the SSH connection yet. Not exception likely happens here.
- """
- if self.conn_failure:
- raise Exception("Simulated exception at connect()")
-
- def cmd(self, command, **kargs):
- """
- `Connection.cmd()` initiates the SSH connection (if not done yet) and sends the command.
- Each check typically has multiple `cmd()` with different commands.
- To cover that, this mock func uses a dictionary `conn_cmds` as the test data.
- """
- _conn_cmds = self.conn_cmds[self.hostname]
- for conn_cmd in _conn_cmds:
- if command == conn_cmd["cmd"]:
- if conn_cmd["exception"]:
- raise conn_cmd["exception"]
- self.output = conn_cmd["output"]
- break
+ param = getattr(request, "param", {})
+ for key in data:
+ if param.get(key, "non_falsy_default") != "non_falsy_default":
+ data[key] = param[key]
+ return data
+
+
+@pytest.fixture(scope="session")
+def result_objects_factory():
+ def _result_objects_factory(profile, requested_status=None):
+ result_props = [
+ # result (status)
+ [
+ script.PASS,
+ script.FAIL_O,
+ script.FAIL_UF,
+ script.MANUAL,
+ script.POST,
+ script.NA,
+ script.ERROR,
+ ],
+ # msg
+ [
+ "",
+ "test msg",
+ "long test msg " + "x" * 120, # > 120 char
+ ],
+ # headers
+ [
+ [],
+ ["H1", "H2", "H3"],
+ ],
+ # data
+ [
+ [],
+ [["Data1", "Data2", "Data3"], ["Data4", "Data5", "Data6"], ["Loooooong Data7", "Data8", "Data9"]],
+ ],
+ # unformatted_headers
+ [
+ [],
+ ["Unformatted_H1"],
+ ],
+ # unformatted_data
+ [
+ [],
+ [["Data1"], ["Data2"]],
+ ],
+ # recommended_action
+ [
+ "",
+ "This is your recommendation to remediate the issue",
+ ],
+ # doc_url
+ [
+ "",
+ "https://fake_doc_url.local/path1/#section1",
+ ],
+ ]
+
+ def _generate_result_obj(result_prop):
+ return script.Result(
+ result=result_prop[0],
+ msg=result_prop[1],
+ headers=result_prop[2],
+ data=result_prop[3],
+ unformatted_headers=result_prop[4],
+ unformatted_data=result_prop[5],
+ recommended_action=result_prop[6],
+ doc_url=result_prop[7],
+ )
+
+ def _get_requested_status(requested_status):
+ if not isinstance(requested_status, list):
+ requested_status = [requested_status]
+ all_status = result_props[0]
+ if not requested_status:
+ return all_status
+ return [status for status in all_status if status in requested_status]
+
+ if profile == "fullmesh":
+ raw_product = product(*result_props)
+ return [_generate_result_obj(prop) for prop in raw_product]
+ elif profile == "fail_full":
+ # All props populated (mainly for FAIL_O, FAIL_UF, MANUAL)
+ status_list = [script.FAIL_O, script.FAIL_UF, script.MANUAL]
+ if requested_status:
+ status_list = _get_requested_status(requested_status)
+ return [
+ _generate_result_obj(
+ [
+ status, # result
+ result_props[1][1], # msg
+ result_props[2][1], # headers
+ result_props[3][1], # data
+ result_props[4][1], # unformatted_headers
+ result_props[5][1], # unformatted_data
+ result_props[6][1], # recommended_action
+ result_props[7][1], # doc_url
+ ]
+ )
+ for status in status_list
+ ]
+ elif profile == "fail_simple":
+ # No msg nor unformatted_headers/data (mojority of FAIL_O, FAIL_UF, MANUAL)
+ status_list = [script.FAIL_O, script.FAIL_UF, script.MANUAL]
+ if requested_status:
+ status_list = _get_requested_status(requested_status)
+ return [
+ _generate_result_obj(
+ [
+ status, # result
+ result_props[1][0], # msg
+ result_props[2][1], # headers
+ result_props[3][1], # data
+ result_props[4][0], # unformatted_headers
+ result_props[5][0], # unformatted_data
+ result_props[6][1], # recommended_action
+ result_props[7][1], # doc_url
+ ]
+ )
+ for status in status_list
+ ]
+ elif profile == "pass":
+ # Only rec_action and doc (mainly for PASS)
+ status_list = [script.PASS]
+ if requested_status:
+ status_list = _get_requested_status(requested_status)
+ return [
+ _generate_result_obj(
+ [
+ status, # result
+ result_props[1][0], # msg
+ result_props[2][0], # headers
+ result_props[3][0], # data
+ result_props[4][0], # unformatted_headers
+ result_props[5][0], # unformatted_data
+ result_props[6][1], # recommended_action
+ result_props[7][1], # doc_url
+ ]
+ )
+ for status in status_list
+ ]
+ elif profile == "only_msg":
+ # Only msg (mainly for PASS, NA, MANUAL, POST, ERROR)
+ status_list = [script.PASS, script.NA, script.MANUAL, script.POST, script.ERROR]
+ if requested_status:
+ status_list = _get_requested_status(requested_status)
+ return [
+ _generate_result_obj(
+ [
+ status, # result
+ result_props[1][1], # msg
+ result_props[2][0], # headers
+ result_props[3][0], # data
+ result_props[4][0], # unformatted_headers
+ result_props[5][0], # unformatted_data
+ result_props[6][0], # recommended_action
+ result_props[7][0], # doc_url
+ ]
+ )
+ for status in status_list
+ ]
+ elif profile == "only_long_msg":
+ # Only long msg (mainly for NA and ERROR)
+ status_list = [script.NA, script.ERROR]
+ if requested_status:
+ status_list = _get_requested_status(requested_status)
+ return [
+ _generate_result_obj(
+ [
+ status, # result
+ result_props[1][2], # msg
+ result_props[2][0], # headers
+ result_props[3][0], # data
+ result_props[4][0], # unformatted_headers
+ result_props[5][0], # unformatted_data
+ result_props[6][0], # recommended_action
+ result_props[7][0], # doc_url
+ ]
+ )
+ for status in status_list
+ ]
else:
- log.error("Command `%s` not found in test data `conn_cmds`", command)
- raise Exception("FAILURE IN PYTEST")
+ raise ValueError("Unexpected profile - {}".format(profile))
+ return _result_objects_factory
-@pytest.fixture
-def mock_conn(monkeypatch, conn_failure, conn_cmds):
- MockConnection.conn_failure = conn_failure
- MockConnection.conn_cmds = conn_cmds
- monkeypatch.setattr(script, "Connection", MockConnection)
+@pytest.fixture(scope="session")
+def check_factory():
+ def _check_factory(check_id, check_title, result_obj):
+ @script.check_wrapper(check_title=check_title)
+ def _check(**kwargs):
+ if result_obj.result == script.ERROR:
+ raise Exception(result_obj.msg)
+ else:
+ return result_obj
+
+ _check.__name__ = check_id # Set the function name for the check
+ return _check
-@pytest.fixture
-def cmd_outputs():
- """
- Mocked output for `run_cmd` function.
- This is used to avoid executing real commands in tests.
- """
- return {
- "ls -aslh /firmware/fwrepos/fwrepo/aci-apic-dk9.6.0.5h.bin": {
- "splitlines": False,
- "output": "6.1G -rwxr-xr-x 1 root root 6.1G Apr 3 16:36 /firmware/fwrepos/fwrepo/aci-apic-dk9.6.0.5h.bin\napic1#",
- }
- }
+ return _check_factory
-@pytest.fixture
-def mock_run_cmd(monkeypatch, cmd_outputs):
- """
- Mock the `run_cmd` function to avoid executing real commands.
- This is useful for tests that do not require actual command execution.
- """
- def _mock_run_cmd(cmd, splitlines=False):
- details = cmd_outputs.get(cmd)
- if details is None:
- log.error("Command `%s` not found in test data", cmd)
- return ""
- if details.get("CalledProcessError"):
- raise CalledProcessError(127, cmd)
-
- splitlines = details.get("splitlines", False)
- output = details.get("output")
- if output is None:
- log.error("Output for cmd `%s` not found in test data", cmd)
- output = ""
+@pytest.fixture(scope="session")
+def check_funcs_factory(check_factory):
+ def _check_funcs_factory(result_objects):
+ check_funcs = []
+ for idx, result_obj in enumerate(result_objects):
+ check_id = "fake_{}_check".format(idx)
+ check_title = "Fake Check {}".format(idx)
+ check_func = check_factory(check_id, check_title, result_obj)
+ check_funcs.append(check_func)
+ return check_funcs
- log.debug("Mocked run_cmd called with args: %s, kwargs: %s", cmd, splitlines)
- if splitlines:
- return output.splitlines()
- else:
- return output
- monkeypatch.setattr(script, "run_cmd", _mock_run_cmd)
+ return _check_funcs_factory
diff --git a/tests/fabric_link_redundancy_check/fabricNode.json b/tests/fabric_link_redundancy_check/fabricNode.json
deleted file mode 100644
index 7bb924e2..00000000
--- a/tests/fabric_link_redundancy_check/fabricNode.json
+++ /dev/null
@@ -1,101 +0,0 @@
-[
- {
- "fabricNode": {
- "attributes": {
- "dn": "topology/pod-1/node-101",
- "id": "101",
- "name": "LF101",
- "role": "leaf",
- "nodeType": "unspecified"
- }
- }
- },
- {
- "fabricNode": {
- "attributes": {
- "dn": "topology/pod-1/node-102",
- "id": "102",
- "name": "LF102",
- "role": "leaf",
- "nodeType": "unspecified"
- }
- }
- },
- {
- "fabricNode": {
- "attributes": {
- "dn": "topology/pod-1/node-103",
- "id": "103",
- "name": "LF103",
- "role": "leaf",
- "nodeType": "unspecified"
- }
- }
- },
- {
- "fabricNode": {
- "attributes": {
- "dn": "topology/pod-1/node-111",
- "id": "111",
- "name": "T2_LF111",
- "role": "leaf",
- "nodeType": "tier-2-leaf"
- }
- }
- },
- {
- "fabricNode": {
- "attributes": {
- "dn": "topology/pod-1/node-112",
- "id": "112",
- "name": "T2_LF112",
- "role": "leaf",
- "nodeType": "tier-2-leaf"
- }
- }
- },
- {
- "fabricNode": {
- "attributes": {
- "dn": "topology/pod-1/node-121",
- "id": "121",
- "name": "RL_LF121",
- "role": "leaf",
- "nodeType": "remote-leaf-wan"
- }
- }
- },
- {
- "fabricNode": {
- "attributes": {
- "dn": "topology/pod-1/node-122",
- "id": "122",
- "name": "RL_LF122",
- "role": "leaf",
- "nodeType": "remote-leaf-wan"
- }
- }
- },
- {
- "fabricNode": {
- "attributes": {
- "dn": "topology/pod-1/node-1001",
- "id": "1001",
- "name": "SP1001",
- "role": "spine",
- "nodeType": "unspecified"
- }
- }
- },
- {
- "fabricNode": {
- "attributes": {
- "dn": "topology/pod-1/node-1002",
- "id": "1002",
- "name": "SP1002",
- "role": "spine",
- "nodeType": "unspecified"
- }
- }
- }
-]
diff --git a/tests/fabric_link_redundancy_check/test_fabric_link_redundancy_check.py b/tests/fabric_link_redundancy_check/test_fabric_link_redundancy_check.py
deleted file mode 100644
index 3570cce5..00000000
--- a/tests/fabric_link_redundancy_check/test_fabric_link_redundancy_check.py
+++ /dev/null
@@ -1,61 +0,0 @@
-import os
-import pytest
-import logging
-import importlib
-from helpers.utils import read_data
-
-script = importlib.import_module("aci-preupgrade-validation-script")
-
-log = logging.getLogger(__name__)
-dir = os.path.dirname(os.path.abspath(__file__))
-
-
-fabric_nodes_api = 'fabricNode.json'
-fabric_nodes_api += '?query-target-filter=and(or(eq(fabricNode.role,"leaf"),eq(fabricNode.role,"spine")),eq(fabricNode.fabricSt,"active"))'
-
-# icurl queries
-lldp_adj_api = 'lldpAdjEp.json'
-lldp_adj_api += '?query-target-filter=wcard(lldpAdjEp.sysDesc,"topology/pod")'
-
-
-@pytest.mark.parametrize(
- "icurl_outputs, expected_result",
- [
- # FAILING = T1 leaf101 single-homed, T1 leaf102 none, T1 leaf103 multi-homed
- (
- {
- fabric_nodes_api: read_data(dir, "fabricNode.json"),
- lldp_adj_api: read_data(dir, "lldpAdjEp_pos_spine_only.json"),
- },
- script.FAIL_O,
- ),
- # FAILING = T1 leafs multi-homed, T2 leaf111 single-homed, T2 leaf112 multi-homed
- (
- {
- fabric_nodes_api: read_data(dir, "fabricNode.json"),
- lldp_adj_api: read_data(dir, "lldpAdjEp_pos_t1_only.json"),
- },
- script.FAIL_O,
- ),
- # FAILING = T1 leaf101 single-homed, T1 leaf102 none, T1 leaf103 multi-homed
- # T2 leaf111 single-homed, T2 leaf112 multi-homed
- (
- {
- fabric_nodes_api: read_data(dir, "fabricNode.json"),
- lldp_adj_api: read_data(dir, "lldpAdjEp_pos_spine_t1.json"),
- },
- script.FAIL_O,
- ),
- # PASSING = ALL LEAF SWITCHES ARE MULTI-HOMED except for RL
- (
- {
- fabric_nodes_api: read_data(dir, "fabricNode.json"),
- lldp_adj_api: read_data(dir, "lldpAdjEp_neg.json"),
- },
- script.PASS,
- ),
- ],
-)
-def test_logic(mock_icurl , expected_result):
- result = script.fabric_link_redundancy_check(1, 1)
- assert result == expected_result
diff --git a/tests/fabricdomain_name_check/test_fabricdomain_name_check.py b/tests/fabricdomain_name_check/test_fabricdomain_name_check.py
deleted file mode 100644
index 61ae397b..00000000
--- a/tests/fabricdomain_name_check/test_fabricdomain_name_check.py
+++ /dev/null
@@ -1,79 +0,0 @@
-import os
-import pytest
-import logging
-import importlib
-from helpers.utils import read_data
-
-script = importlib.import_module("aci-preupgrade-validation-script")
-
-log = logging.getLogger(__name__)
-dir = os.path.dirname(os.path.abspath(__file__))
-
-
-# icurl queries
-topSystem = 'topSystem.json?query-target-filter=eq(topSystem.role,"controller")'
-
-
-@pytest.mark.parametrize(
- "icurl_outputs, cversion, tversion, expected_result",
- [
- # # char test
- (
- {topSystem: read_data(dir, "topSystem_1POS.json")},
- "5.2(3g)",
- "6.0(2h)",
- script.FAIL_O,
- ),
- (
- {topSystem: read_data(dir, "topSystem_1POS.json")},
- "6.0(3a)",
- "6.0(2h)",
- script.FAIL_O,
- ),
- # ; char test
- (
- {topSystem: read_data(dir, "topSystem_2POS.json")},
- "5.2(3g)",
- "6.0(2h)",
- script.FAIL_O,
- ),
- (
- {topSystem: read_data(dir, "topSystem_2POS.json")},
- "6.0(3a)",
- "6.0(2h)",
- script.FAIL_O,
- ),
- # Neither ; or # in fabricDomain
- (
- {topSystem: read_data(dir, "topSystem_NEG.json")},
- "5.2(3g)",
- "6.0(2h)",
- script.PASS,
- ),
- # only affected 6.0(2h), regardless of special chars
- (
- {topSystem: read_data(dir, "topSystem_1POS.json")},
- "5.2(3g)",
- "6.0(1j)",
- script.PASS,
- ),
- # Eventual 6.0(3) has fix
- (
- {topSystem: read_data(dir, "topSystem_1POS.json")},
- "5.2(3g)",
- "6.0(3a)",
- script.PASS,
- ),
- (
- {topSystem: read_data(dir, "topSystem_1POS.json")},
- "6.0(3a)",
- "6.0(4a)",
- script.PASS,
- ),
- ],
-)
-def test_logic(mock_icurl, cversion, tversion, expected_result):
- result = script.fabricdomain_name_check(
- 1, 1, script.AciVersion(cversion), script.AciVersion(tversion)
- )
- assert result == expected_result
diff --git a/tests/fabricdomain_name_check/topSystem_1POS.json b/tests/fabricdomain_name_check/topSystem_1POS.json
deleted file mode 100644
index e0764f06..00000000
--- a/tests/fabricdomain_name_check/topSystem_1POS.json
+++ /dev/null
@@ -1,35 +0,0 @@
-[
- {
- "topSystem": {
- "attributes": {
- "address": "10.0.0.1",
- "fabricId": "1",
- "id": "1",
- "fabricDomain": "fabric;4",
- "role": "controller"
- }
- }
- },
- {
- "topSystem": {
- "attributes": {
- "address": "10.0.0.2",
- "fabricId": "1",
- "id": "2",
- "fabricDomain": "fabric;4",
- "role": "controller"
- }
- }
- },
- {
- "topSystem": {
- "attributes": {
- "address": "10.0.0.3",
- "fabricId": "1",
- "id": "3",
- "fabricDomain": "fabric;4",
- "role": "controller"
- }
- }
- }
-]
diff --git a/tests/fabricdomain_name_check/topSystem_2POS.json b/tests/fabricdomain_name_check/topSystem_2POS.json
deleted file mode 100644
index e353b36a..00000000
--- a/tests/fabricdomain_name_check/topSystem_2POS.json
+++ /dev/null
@@ -1,35 +0,0 @@
-[
- {
- "topSystem": {
- "attributes": {
- "address": "10.0.0.1",
- "fabricId": "1",
- "id": "1",
- "fabricDomain": "fabric#4",
- "role": "controller"
- }
- }
- },
- {
- "topSystem": {
- "attributes": {
- "address": "10.0.0.2",
- "fabricId": "1",
- "id": "2",
- "fabricDomain": "fabric#4",
- "role": "controller"
- }
- }
- },
- {
- "topSystem": {
- "attributes": {
- "address": "10.0.0.3",
- "fabricId": "1",
- "id": "3",
- "fabricDomain": "fabric#4",
- "role": "controller"
- }
- }
- }
-]
diff --git a/tests/fabricdomain_name_check/topSystem_NEG.json b/tests/fabricdomain_name_check/topSystem_NEG.json
deleted file mode 100644
index 7a1c1e73..00000000
--- a/tests/fabricdomain_name_check/topSystem_NEG.json
+++ /dev/null
@@ -1,35 +0,0 @@
-[
- {
- "topSystem": {
- "attributes": {
- "address": "10.0.0.1",
- "fabricId": "1",
- "id": "1",
- "fabricDomain": "fabric4",
- "role": "controller"
- }
- }
- },
- {
- "topSystem": {
- "attributes": {
- "address": "10.0.0.2",
- "fabricId": "1",
- "id": "2",
- "fabricDomain": "fabric4",
- "role": "controller"
- }
- }
- },
- {
- "topSystem": {
- "attributes": {
- "address": "10.0.0.3",
- "fabricId": "1",
- "id": "3",
- "fabricDomain": "fabric4",
- "role": "controller"
- }
- }
- }
-]
diff --git a/tests/fc_ex_model_check/fabricNode_NEG.json b/tests/fc_ex_model_check/fabricNode_NEG.json
deleted file mode 100644
index 0637a088..00000000
--- a/tests/fc_ex_model_check/fabricNode_NEG.json
+++ /dev/null
@@ -1 +0,0 @@
-[]
\ No newline at end of file
diff --git a/tests/fc_ex_model_check/fabricNode_POS.json b/tests/fc_ex_model_check/fabricNode_POS.json
deleted file mode 100644
index 0d12e7ce..00000000
--- a/tests/fc_ex_model_check/fabricNode_POS.json
+++ /dev/null
@@ -1,26 +0,0 @@
-[
- {
- "fabricNode": {
- "attributes": {
- "dn": "topology/pod-1/node-101",
- "model": "N9K-C93180YC-EX"
- }
- }
- },
- {
- "fabricNode": {
- "attributes": {
- "dn": "topology/pod-1/node-102",
- "model": "N9K-C93108TC-EX"
- }
- }
- },
- {
- "fabricNode": {
- "attributes": {
- "dn": "topology/pod-1/node-103",
- "model": "N9K-C93108LC-EX"
- }
- }
- }
-]
\ No newline at end of file
diff --git a/tests/fc_ex_model_check/fcEntity_NEG.json b/tests/fc_ex_model_check/fcEntity_NEG.json
deleted file mode 100644
index 0637a088..00000000
--- a/tests/fc_ex_model_check/fcEntity_NEG.json
+++ /dev/null
@@ -1 +0,0 @@
-[]
\ No newline at end of file
diff --git a/tests/fc_ex_model_check/fcEntity_POS.json b/tests/fc_ex_model_check/fcEntity_POS.json
deleted file mode 100644
index 162a77cc..00000000
--- a/tests/fc_ex_model_check/fcEntity_POS.json
+++ /dev/null
@@ -1,18 +0,0 @@
-[
- {
- "fcEntity": {
- "attributes": {
- "adminSt": "enabled",
- "dn": "topology/pod-1/node-102/sys/fc"
- }
- }
- },
- {
- "fcEntity": {
- "attributes": {
- "adminSt": "enabled",
- "dn": "topology/pod-1/node-101/sys/fc"
- }
- }
- }
-]
diff --git a/tests/fc_ex_model_check/test_fc_ex_model_check.py b/tests/fc_ex_model_check/test_fc_ex_model_check.py
deleted file mode 100644
index 1cd0833a..00000000
--- a/tests/fc_ex_model_check/test_fc_ex_model_check.py
+++ /dev/null
@@ -1,65 +0,0 @@
-import os
-import pytest
-import logging
-import importlib
-from helpers.utils import read_data
-
-script = importlib.import_module("aci-preupgrade-validation-script")
-
-log = logging.getLogger(__name__)
-dir = os.path.dirname(os.path.abspath(__file__))
-
-
-# icurl queries
-
-fcEntity_api = 'fcEntity.json'
-fabricNode_api = 'fabricNode.json'
-fabricNode_api += '?query-target-filter=wcard(fabricNode.model,".*EX")'
-
-@pytest.mark.parametrize(
- "icurl_outputs, tversion, expected_result",
- [
- ## FABRIC HAS EX NODES and FC/FCOE CONFIG
- (
- {fcEntity_api: read_data(dir, "fcEntity_POS.json"),
- fabricNode_api: read_data(dir, "fabricNode_POS.json")},
- "6.1(1f)",
- script.FAIL_O,
- ),
- (
- {fcEntity_api: read_data(dir, "fcEntity_POS.json"),
- fabricNode_api: read_data(dir, "fabricNode_POS.json")},
- "6.0(7e)",
- script.FAIL_O,
- ),
- # TVERSION NOT AFFECTED
- (
- {fcEntity_api: read_data(dir, "fcEntity_POS.json"),
- fabricNode_api: read_data(dir, "fabricNode_POS.json")},
- "6.0(1f)",
- script.PASS,
- ),
- ## FABRIC DOES NOT HAVE EX NODES
- (
- {fcEntity_api: read_data(dir, "fcEntity_NEG.json"),
- fabricNode_api: read_data(dir, "fabricNode_NEG.json")},
- "6.1(1f)",
- script.PASS,
- ),
- (
- {fcEntity_api: read_data(dir, "fcEntity_NEG.json"),
- fabricNode_api: read_data(dir, "fabricNode_NEG.json")},
- "6.0(7e)",
- script.PASS,
- ),
- (
- {fcEntity_api: read_data(dir, "fcEntity_POS.json"),
- fabricNode_api: read_data(dir, "fabricNode_NEG.json")},
- "6.0(7e)",
- script.PASS,
- ),
- ],
-)
-def test_logic(mock_icurl, tversion, expected_result):
- result = script.fc_ex_model_check(1, 1, script.AciVersion(tversion))
- assert result == expected_result
diff --git a/tests/mini_aci_6_0_2/test_mini_aci_6_0_2_check.py b/tests/mini_aci_6_0_2/test_mini_aci_6_0_2_check.py
deleted file mode 100644
index c93578e2..00000000
--- a/tests/mini_aci_6_0_2/test_mini_aci_6_0_2_check.py
+++ /dev/null
@@ -1,71 +0,0 @@
-import os
-import pytest
-import logging
-import importlib
-from helpers.utils import read_data
-
-script = importlib.import_module("aci-preupgrade-validation-script")
-
-log = logging.getLogger(__name__)
-dir = os.path.dirname(os.path.abspath(__file__))
-
-
-# icurl queries
-topSystems = 'topSystem.json?query-target-filter=wcard(topSystem.role,"controller")'
-
-
-@pytest.mark.parametrize(
- "icurl_outputs, cversion, tversion, expected_result",
- [
- (
- {topSystems: read_data(dir, "topSystem_controller_neg.json")},
- "3.2(1a)",
- "5.2(6a)",
- script.PASS,
- ),
- (
- {topSystems: read_data(dir, "topSystem_controller_neg.json")},
- "6.0(2e)",
- "6.0(5d)",
- script.PASS,
- ),
- (
- {topSystems: read_data(dir, "topSystem_controller_neg.json")},
- "5.2(3a)",
- "6.0(3d)",
- script.PASS,
- ),
- (
- {topSystems: read_data(dir, "topSystem_controller_pos.json")},
- "4.1(1a)",
- "5.2(7f)",
- script.PASS,
- ),
- (
- {topSystems: read_data(dir, "topSystem_controller_pos.json")},
- "4.2(2a)",
- "6.0(2c)",
- script.FAIL_UF,
- ),
- (
- {topSystems: read_data(dir, "topSystem_controller_pos.json")},
- "6.0(1a)",
- "6.0(2c)",
- script.FAIL_UF,
- ),
- (
- {topSystems: read_data(dir, "topSystem_controller_pos.json")},
- "6.0(2c)",
- "6.0(5c)",
- script.PASS,
- ),
- ],
-)
-def test_logic(mock_icurl, cversion, tversion, expected_result):
- result = script.mini_aci_6_0_2_check(
- 1,
- 1,
- script.AciVersion(cversion),
- script.AciVersion(tversion) if tversion else None,
- )
- assert result == expected_result
diff --git a/tests/n9k_c93108tc_fx3p_interface_down_check/fabricNode_FX3H.json b/tests/n9k_c93108tc_fx3p_interface_down_check/fabricNode_FX3H.json
deleted file mode 100644
index f1bdd0b5..00000000
--- a/tests/n9k_c93108tc_fx3p_interface_down_check/fabricNode_FX3H.json
+++ /dev/null
@@ -1,26 +0,0 @@
-[
- {
- "fabricNode": {
- "attributes": {
- "dn": "topology/pod-2/node-113",
- "fabricSt": "active",
- "id": "113",
- "model": "N9K-C93108TC-FX3H",
- "monPolDn": "uni/fabric/monfab-default",
- "name": "leaf113"
- }
- }
- },
- {
- "fabricNode": {
- "attributes": {
- "dn": "topology/pod-2/node-114",
- "fabricSt": "active",
- "id": "114",
- "model": "N9K-C93108TC-FX3H",
- "monPolDn": "uni/fabric/monfab-default",
- "name": "leaf114"
- }
- }
- }
-]
diff --git a/tests/n9k_c93108tc_fx3p_interface_down_check/fabricNode_FX3P.json b/tests/n9k_c93108tc_fx3p_interface_down_check/fabricNode_FX3P.json
deleted file mode 100644
index 39538a04..00000000
--- a/tests/n9k_c93108tc_fx3p_interface_down_check/fabricNode_FX3P.json
+++ /dev/null
@@ -1,26 +0,0 @@
-[
- {
- "fabricNode": {
- "attributes": {
- "dn": "topology/pod-1/node-102",
- "fabricSt": "active",
- "id": "101",
- "model": "N9K-C93108TC-FX3P",
- "monPolDn": "uni/fabric/monfab-default",
- "name": "leaf101"
- }
- }
- },
- {
- "fabricNode": {
- "attributes": {
- "dn": "topology/pod-1/node-102",
- "fabricSt": "active",
- "id": "102",
- "model": "N9K-C93108TC-FX3P",
- "monPolDn": "uni/fabric/monfab-default",
- "name": "leaf102"
- }
- }
- }
-]
diff --git a/tests/n9k_c93108tc_fx3p_interface_down_check/fabricNode_FX3P3H.json b/tests/n9k_c93108tc_fx3p_interface_down_check/fabricNode_FX3P3H.json
deleted file mode 100644
index b9d7780e..00000000
--- a/tests/n9k_c93108tc_fx3p_interface_down_check/fabricNode_FX3P3H.json
+++ /dev/null
@@ -1,50 +0,0 @@
-[
- {
- "fabricNode": {
- "attributes": {
- "dn": "topology/pod-1/node-102",
- "fabricSt": "active",
- "id": "101",
- "model": "N9K-C93108TC-FX3P",
- "monPolDn": "uni/fabric/monfab-default",
- "name": "leaf101"
- }
- }
- },
- {
- "fabricNode": {
- "attributes": {
- "dn": "topology/pod-1/node-102",
- "fabricSt": "active",
- "id": "102",
- "model": "N9K-C93108TC-FX3P",
- "monPolDn": "uni/fabric/monfab-default",
- "name": "leaf102"
- }
- }
- },
- {
- "fabricNode": {
- "attributes": {
- "dn": "topology/pod-2/node-113",
- "fabricSt": "active",
- "id": "113",
- "model": "N9K-C93108TC-FX3H",
- "monPolDn": "uni/fabric/monfab-default",
- "name": "leaf113"
- }
- }
- },
- {
- "fabricNode": {
- "attributes": {
- "dn": "topology/pod-2/node-114",
- "fabricSt": "active",
- "id": "114",
- "model": "N9K-C93108TC-FX3H",
- "monPolDn": "uni/fabric/monfab-default",
- "name": "leaf114"
- }
- }
- }
-]
diff --git a/tests/n9k_c93108tc_fx3p_interface_down_check/test_n9k_c93108tc_fx3p_interface_down_check.py b/tests/n9k_c93108tc_fx3p_interface_down_check/test_n9k_c93108tc_fx3p_interface_down_check.py
deleted file mode 100644
index 2c35bff6..00000000
--- a/tests/n9k_c93108tc_fx3p_interface_down_check/test_n9k_c93108tc_fx3p_interface_down_check.py
+++ /dev/null
@@ -1,50 +0,0 @@
-import os
-import pytest
-import logging
-import importlib
-from helpers.utils import read_data
-
-script = importlib.import_module("aci-preupgrade-validation-script")
-
-log = logging.getLogger(__name__)
-dir = os.path.dirname(os.path.abspath(__file__))
-
-
-# icurl queries
-api = 'fabricNode.json?query-target-filter=or(eq(fabricNode.model,"N9K-C93108TC-FX3P"),eq(fabricNode.model,"N9K-C93108TC-FX3H"))'
-
-
-@pytest.mark.parametrize(
- "icurl_outputs, tversion, expected_result",
- [
- # Version not supplied
- ({api: []}, None, script.MANUAL),
- # Version not affected
- ({api: read_data(dir, "fabricNode_FX3P3H.json")}, "5.2(8h)", script.PASS),
- ({api: read_data(dir, "fabricNode_FX3P3H.json")}, "5.3(2b)", script.PASS),
- ({api: read_data(dir, "fabricNode_FX3P3H.json")}, "6.0(4c)", script.PASS),
- # Affected version, no FX3P or FX3H
- ({api: []}, "5.2(8g)", script.PASS),
- ({api: []}, "5.3(1d)", script.PASS),
- ({api: []}, "6.0(2h)", script.PASS),
- # Affected version, FX3P
- ({api: read_data(dir, "fabricNode_FX3P.json")}, "5.2(8g)", script.FAIL_O),
- ({api: read_data(dir, "fabricNode_FX3P.json")}, "5.3(1d)", script.FAIL_O),
- ({api: read_data(dir, "fabricNode_FX3P.json")}, "6.0(2h)", script.FAIL_O),
- # Affected version, FX3H
- ({api: read_data(dir, "fabricNode_FX3H.json")}, "5.2(8g)", script.FAIL_O),
- ({api: read_data(dir, "fabricNode_FX3H.json")}, "5.3(1d)", script.FAIL_O),
- ({api: read_data(dir, "fabricNode_FX3H.json")}, "6.0(2h)", script.FAIL_O),
- # Affected version, FX3P and FX3H
- ({api: read_data(dir, "fabricNode_FX3P3H.json")}, "5.2(8g)", script.FAIL_O),
- ({api: read_data(dir, "fabricNode_FX3P3H.json")}, "5.3(1d)", script.FAIL_O),
- ({api: read_data(dir, "fabricNode_FX3P3H.json")}, "6.0(2h)", script.FAIL_O),
- ],
-)
-def test_logic(mock_icurl, tversion, expected_result):
- result = script.n9k_c93108tc_fx3p_interface_down_check(
- 1,
- 1,
- script.AciVersion(tversion) if tversion else None,
- )
- assert result == expected_result
diff --git a/tests/observer_db_size_check/test_observer_db_size_check.py b/tests/observer_db_size_check/test_observer_db_size_check.py
deleted file mode 100644
index 9a856635..00000000
--- a/tests/observer_db_size_check/test_observer_db_size_check.py
+++ /dev/null
@@ -1,128 +0,0 @@
-import os
-import pytest
-import logging
-import importlib
-from helpers.utils import read_data
-
-script = importlib.import_module("aci-preupgrade-validation-script")
-
-log = logging.getLogger(__name__)
-dir = os.path.dirname(os.path.abspath(__file__))
-
-topSystem_api = 'topSystem.json'
-topSystem_api += '?query-target-filter=eq(topSystem.role,"controller")'
-
-topSystem = read_data(dir, "topSystem.json")
-apic_ips = [
- mo["topSystem"]["attributes"]["address"]
- for mo in topSystem["imdata"]
-]
-
-ls_cmd = "ls -lh /data2/dbstats | awk '{print $5, $9}'"
-ls_output_neg = """\
-
-11M observer_8.db
-11M observer_9.db
-11M observer_10.db
-11M observer_template.db
-apic1#
-"""
-ls_output_pos = """\
-
-1.0G observer_8.db
-12G observer_9.db
-999M observer_10.db
-11M observer_template.db
-apic1#
-"""
-ls_output_no_such_file = """\
-ls: cannot access /data2/dbstats: No such file or directory
-apic1#
-"""
-
-@pytest.mark.parametrize(
- "icurl_outputs, conn_failure, conn_cmds, expected_result",
- [
- # Connection failure
- (
- {topSystem_api: read_data(dir, "topSystem.json")},
- True,
- [],
- script.ERROR,
- ),
- # Simulatated exception at `ls` command
- (
- {topSystem_api: read_data(dir, "topSystem.json")},
- False,
- {
- apic_ip: [
- {
- "cmd": ls_cmd,
- "output": "",
- "exception": Exception("Simulated exception at `ls` command"),
- }
- ]
- for apic_ip in apic_ips
- },
- script.ERROR,
- ),
- # dbstats dir not found/not accessible
- (
- {topSystem_api: read_data(dir, "topSystem.json")},
- False,
- {
- apic_ip: [
- {
- "cmd": ls_cmd,
- "output": "\n".join([ls_cmd, ls_output_no_such_file]),
- "exception": None,
- }
- ]
- for apic_ip in apic_ips
- },
- script.ERROR,
- ),
- # dbstats dir found, all DBs under 1G
- (
- {topSystem_api: read_data(dir, "topSystem.json")},
- False,
- {
- apic_ip: [
- {
- "cmd": ls_cmd,
- "output": "\n".join([ls_cmd, ls_output_neg]),
- "exception": None,
- }
- ]
- for apic_ip in apic_ips
- },
- script.PASS,
- ),
- # dbstats dir found, found DBs over 1G
- (
- {topSystem_api: read_data(dir, "topSystem.json")},
- False,
- {
- apic_ip: [
- {
- "cmd": ls_cmd,
- "output": "\n".join([ls_cmd, ls_output_pos]),
- "exception": None,
- }
- ]
- for apic_ip in apic_ips
- },
- script.FAIL_UF,
- ),
- # ERROR, topsystem failure
- (
- {topSystem_api: read_data(dir, "topSystem_empty.json")},
- False,
- [],
- script.ERROR,
- ),
- ],
-)
-def test_logic(mock_icurl, mock_conn, expected_result):
- result = script.observer_db_size_check(1, 1, "fake_username", "fake_password")
- assert result == expected_result
diff --git a/tests/observer_db_size_check/topSystem.json b/tests/observer_db_size_check/topSystem.json
deleted file mode 100644
index 5b265e92..00000000
--- a/tests/observer_db_size_check/topSystem.json
+++ /dev/null
@@ -1,35 +0,0 @@
-{
- "totalCount": "3",
- "imdata": [
- {
- "topSystem": {
- "attributes": {
- "address": "10.0.0.1",
- "dn": "topology/pod-1/node-1/sys",
- "id": "1",
- "name": "fab5-apic1"
- }
- }
- },
- {
- "topSystem": {
- "attributes": {
- "address": "10.0.0.2",
- "dn": "topology/pod-1/node-2/sys",
- "id": "2",
- "name": "fab5-apic2"
- }
- }
- },
- {
- "topSystem": {
- "attributes": {
- "address": "10.0.0.3",
- "dn": "topology/pod-1/node-3/sys",
- "id": "3",
- "name": "fab5-apic3"
- }
- }
- }
- ]
-}
\ No newline at end of file
diff --git a/tests/observer_db_size_check/topSystem_empty.json b/tests/observer_db_size_check/topSystem_empty.json
deleted file mode 100644
index 0637a088..00000000
--- a/tests/observer_db_size_check/topSystem_empty.json
+++ /dev/null
@@ -1 +0,0 @@
-[]
\ No newline at end of file
diff --git a/tests/stale_decomissioned_spine_check/fabricRsDecommissionNode_NEG.json b/tests/stale_decomissioned_spine_check/fabricRsDecommissionNode_NEG.json
deleted file mode 100644
index 0637a088..00000000
--- a/tests/stale_decomissioned_spine_check/fabricRsDecommissionNode_NEG.json
+++ /dev/null
@@ -1 +0,0 @@
-[]
\ No newline at end of file
diff --git a/tests/stale_decomissioned_spine_check/test_stale_decomissioned_spine_check.py b/tests/stale_decomissioned_spine_check/test_stale_decomissioned_spine_check.py
deleted file mode 100644
index fade8136..00000000
--- a/tests/stale_decomissioned_spine_check/test_stale_decomissioned_spine_check.py
+++ /dev/null
@@ -1,66 +0,0 @@
-import os
-import pytest
-import logging
-import importlib
-from helpers.utils import read_data
-
-script = importlib.import_module("aci-preupgrade-validation-script")
-
-log = logging.getLogger(__name__)
-dir = os.path.dirname(os.path.abspath(__file__))
-
-
-# icurl queries
-decomissioned_api ='fabricRsDecommissionNode.json'
-
-active_spine_api = 'topSystem.json'
-active_spine_api += '?query-target-filter=eq(topSystem.role,"spine")'
-
-@pytest.mark.parametrize(
- "icurl_outputs, tversion, expected_result",
- [
- # TVERSION not supplied
- (
- {
- active_spine_api: read_data(dir, "topSystem.json"),
- decomissioned_api: read_data(dir,"fabricRsDecommissionNode_NEG.json")
- },
- None,
- script.MANUAL,
- ),
- # No decom objects
- (
- {
- active_spine_api: read_data(dir, "topSystem.json"),
- decomissioned_api: read_data(dir,"fabricRsDecommissionNode_NEG.json")
- },
- "5.2(5e)",
- script.PASS,
- ),
- # Spine has stale decom object, and going to affected version
- (
- {
- active_spine_api: read_data(dir, "topSystem.json"),
- decomissioned_api: read_data(dir,"fabricRsDecommissionNode_POS.json")
- },
- "5.2(6a)",
- script.FAIL_O,
- ),
- # Fixed Target Version
- (
- {
- active_spine_api: read_data(dir, "topSystem.json"),
- decomissioned_api: read_data(dir,"fabricRsDecommissionNode_POS.json")
- },
- "6.0(4a)",
- script.PASS,
- ),
- ],
-)
-def test_logic(mock_icurl, tversion, expected_result):
- result = script.stale_decomissioned_spine_check(
- 1,
- 1,
- script.AciVersion(tversion) if tversion else None,
- )
- assert result == expected_result
diff --git a/tests/stale_decomissioned_spine_check/topSystem.json b/tests/stale_decomissioned_spine_check/topSystem.json
deleted file mode 100644
index 38d8d14b..00000000
--- a/tests/stale_decomissioned_spine_check/topSystem.json
+++ /dev/null
@@ -1,24 +0,0 @@
-[
- {
- "topSystem": {
- "attributes": {
- "dn": "topology/pod-1/node-201/sys",
- "id": "201",
- "state": "in-service",
- "role": "spine",
- "name": "spine1"
- }
- }
- },
- {
- "topSystem": {
- "attributes": {
- "dn": "topology/pod-1/node-106/sys",
- "id": "106",
- "state": "in-service",
- "role": "spine",
- "name": "spine2"
- }
- }
- }
- ]
\ No newline at end of file
diff --git a/tests/standby_sup_sync_check/test_standby_sup_sync_check.py b/tests/standby_sup_sync_check/test_standby_sup_sync_check.py
deleted file mode 100644
index f11a0a42..00000000
--- a/tests/standby_sup_sync_check/test_standby_sup_sync_check.py
+++ /dev/null
@@ -1,174 +0,0 @@
-import os
-import pytest
-import logging
-import importlib
-from helpers.utils import read_data
-
-script = importlib.import_module("aci-preupgrade-validation-script")
-
-log = logging.getLogger(__name__)
-dir = os.path.dirname(os.path.abspath(__file__))
-
-# icurl queries
-eqptSupC_api = 'eqptSupC.json'
-eqptSupC_api += '?query-target-filter=eq(eqptSupC.rdSt,"standby")'
-
-"""
-Bug cversion/tversion matrix based on image size
-
-4.2(7t)+ - fixed versions LT 2 Gigs: 4.2(7t)+
-5.2(5d)+ - fixed versions LT 2 Gigs: 5.2(7f)+
-5.3(1d)+ - fixed versions LT 2 Gigs: 5.3(1d)+
-6.0(1g)+ - fixed versions LT 2 Gigs: 6.0(1g), 6.0(1j). 32-bit only: 6.0(2h), 6.0(2j). 64-bit: NONE
-6.1(1f)+ - fixed versions LT 2 Gigs: NONE
-"""
-
-@pytest.mark.parametrize(
- "icurl_outputs, cversion, tversion, expected_result",
- [
- ## NO TVERSION - MANUAL
- (
- {eqptSupC_api: read_data(dir, "eqptSupC_POS.json"),
- },
- "5.2(1a)",
- None,
- script.MANUAL,
- ),
-
- ### CVERSION 4.2
- ## cversion 4.2 -nofix, tversion 4.2 -fix LT 2G
- (
- {eqptSupC_api: read_data(dir, "eqptSupC_POS.json"),
- },
- "4.2(7a)",
- "4.2(8d)",
- script.PASS,
- ),
- ## cversion 4.2 -nofix, tversion 5.2 -fix but over 2G
- (
- {eqptSupC_api: read_data(dir, "eqptSupC_POS.json"),
- },
- "4.2(7a)",
- "5.2(5d)",
- script.FAIL_UF,
- ),
- ## cversion 4.2 -nofix, tversion 5.2 -fix and LT 2G
- (
- {eqptSupC_api: read_data(dir, "eqptSupC_POS.json"),
- },
- "4.2(7a)",
- "5.2(7f)",
- script.PASS,
- ),
- ## cversion 4.2 -nofix, tversion 5.3 -fix and LT 2G
- (
- {eqptSupC_api: read_data(dir, "eqptSupC_POS.json"),
- },
- "4.2(7a)",
- "5.3(1d)",
- script.PASS,
- ),
- ## cversion 4.2 -nofix, tversion 6.0 -fix and LT 2G
- (
- {eqptSupC_api: read_data(dir, "eqptSupC_POS.json"),
- },
- "4.2(7a)",
- "6.0(8d)",
- script.FAIL_UF,
- ),
- ## cversion 4.2 -nofix, tversion 6.1 -fix and LT 2G
- (
- {eqptSupC_api: read_data(dir, "eqptSupC_POS.json"),
- },
- "4.2(7a)",
- "6.1(1f)",
- script.FAIL_UF,
- ),
- ## cversion 4.2 -fix, tversion 6.0 -fix but over 2G
- (
- {eqptSupC_api: read_data(dir, "eqptSupC_POS.json"),
- },
- "4.2(7t)",
- "6.0(6h)",
- script.PASS,
- ),
- ## cversion 4.2 -fix, tversion 6.1 -fix but over 2G
- (
- {eqptSupC_api: read_data(dir, "eqptSupC_POS.json"),
- },
- "4.2(7t)",
- "6.1(1f)",
- script.PASS,
- ),
-
-
- ### CVERSION 5.2
- ## cversion 5.2 -nofix, tversion 5.2 -fix but over 2G
- (
- {eqptSupC_api: read_data(dir, "eqptSupC_POS.json"),
- },
- "5.2(4a)",
- "5.2(7a)",
- script.FAIL_UF,
- ),
- ## cversion 5.2 -nofix, tversion 5.2 -fix LT 2G
- (
- {eqptSupC_api: read_data(dir, "eqptSupC_POS.json"),
- },
- "5.2(4a)",
- "5.2(7f)",
- script.PASS,
- ),
- ## cversion 5.2 -nofix, tversion 5.3 -fix LT 2G
- (
- {eqptSupC_api: read_data(dir, "eqptSupC_POS.json"),
- },
- "5.2(4a)",
- "5.3(1d)",
- script.PASS,
- ),
- ## cversion 5.2 -nofix, tversion 6.0 -fix but over 2G
- (
- {eqptSupC_api: read_data(dir, "eqptSupC_POS.json"),
- },
- "5.2(4a)",
- "6.0(8d)",
- script.FAIL_UF,
- ),
- ## cversion 5.2 -nofix, tversion 6.1 -fix but over 2G
- (
- {eqptSupC_api: read_data(dir, "eqptSupC_POS.json"),
- },
- "5.2(4a)",
- "6.1(1f)",
- script.FAIL_UF,
- ),
- ## cversion 5.2 -fix, tversion 6.1 -fix but over 2G
- (
- {eqptSupC_api: read_data(dir, "eqptSupC_POS.json"),
- },
- "5.2(5d)",
- "6.1(1f)",
- script.PASS,
- ),
-
-
- ## NO STANDBY SUPS
- (
- {eqptSupC_api: read_data(dir, "eqptSupC_NEG.json"),
- },
- "4.2(7a)",
- "6.1(1f)",
- script.PASS,
- ),
-
- ],
-)
-def test_logic(mock_icurl, cversion, tversion, expected_result):
- result = script.standby_sup_sync_check(
- 1,
- 1,
- script.AciVersion(cversion),
- script.AciVersion(tversion) if tversion else None
- )
- assert result == expected_result
\ No newline at end of file
diff --git a/tests/static_route_overlap_check/test_static_route_overlap_check.py b/tests/static_route_overlap_check/test_static_route_overlap_check.py
deleted file mode 100644
index 270641ef..00000000
--- a/tests/static_route_overlap_check/test_static_route_overlap_check.py
+++ /dev/null
@@ -1,67 +0,0 @@
-import os
-import pytest
-import logging
-import importlib
-from helpers.utils import read_data
-
-script = importlib.import_module("aci-preupgrade-validation-script")
-
-log = logging.getLogger(__name__)
-dir = os.path.dirname(os.path.abspath(__file__))
-
-
-# icurl queries
-staticRoutes = 'ipRouteP.json?query-target-filter=and(wcard(ipRouteP.dn,"/32"))'
-staticroute_vrf = 'l3extRsEctx.json'
-bds_in_vrf = 'fvRsCtx.json'
-subnets_in_bd = 'fvSubnet.json'
-
-
-@pytest.mark.parametrize(
- "icurl_outputs, cversion, tversion, expected_result",
- [
- ##FAIL = AFFECTED VERSION + AFFECTED MO
- (
- {staticRoutes: read_data(dir, "ipRouteP_pos.json"),
- staticroute_vrf: read_data(dir, "l3extRsEctx.json"),
- bds_in_vrf: read_data(dir, "fvRsCtx.json"),
- subnets_in_bd: read_data(dir, "fvSubnet.json")},
- "4.2(7f)", "5.2(4d)", script.FAIL_O,
- ),
- ##FAIL = AFFECTED VERSION + AFFECTED MO
- (
- {staticRoutes: read_data(dir, "ipRouteP_pos.json"),
- staticroute_vrf: read_data(dir, "l3extRsEctx.json"),
- bds_in_vrf: read_data(dir, "fvRsCtx.json"),
- subnets_in_bd: read_data(dir, "fvSubnet.json")},
- "5.1(1a)", "5.2(4d)", script.FAIL_O,
- ),
- ##PASS = AFFECTED VERSION + NON-AFFECTED MO
- (
- {staticRoutes: read_data(dir, "ipRouteP_neg.json"),
- staticroute_vrf: read_data(dir, "l3extRsEctx.json"),
- bds_in_vrf: read_data(dir, "fvRsCtx.json"),
- subnets_in_bd: read_data(dir, "fvSubnet.json")},
- "4.2(7f)", "5.2(4d)", script.PASS,
- ),
- ## PASS = AFFECTED VERSION + AFFECTED MO NON EXISTING
- (
- {staticRoutes: read_data(dir, "ipRouteP_empty.json"),
- staticroute_vrf: read_data(dir, "l3extRsEctx.json"),
- bds_in_vrf: read_data(dir, "fvRsCtx.json"),
- subnets_in_bd: read_data(dir, "fvSubnet.json")},
- "4.2(7f)", "5.2(4d)", script.PASS,
- ),
- ## PASS = NON-AFFECTED VERSION + AFFECTED MO
- (
- {staticRoutes: read_data(dir, "ipRouteP_pos.json"),
- staticroute_vrf: read_data(dir, "l3extRsEctx.json"),
- bds_in_vrf: read_data(dir, "fvRsCtx.json"),
- subnets_in_bd: read_data(dir, "fvSubnet.json")},
- "4.2(7f)", "5.2(6e)", script.PASS,
- ),
- ],
-)
-def test_logic(mock_icurl, cversion, tversion, expected_result):
- result = script.static_route_overlap_check(1, 1, script.AciVersion(cversion), script.AciVersion(tversion))
- assert result == expected_result
diff --git a/tests/subnet_scope_check/test_subnet_scope_check.py b/tests/subnet_scope_check/test_subnet_scope_check.py
deleted file mode 100644
index 2b9a53c7..00000000
--- a/tests/subnet_scope_check/test_subnet_scope_check.py
+++ /dev/null
@@ -1,63 +0,0 @@
-import os
-import pytest
-import logging
-import importlib
-from helpers.utils import read_data
-
-script = importlib.import_module("aci-preupgrade-validation-script")
-
-log = logging.getLogger(__name__)
-dir = os.path.dirname(os.path.abspath(__file__))
-
-
-# icurl queries
-bd_api = 'fvBD.json'
-bd_api += '?rsp-subtree=children&rsp-subtree-class=fvSubnet&rsp-subtree-include=required'
-
-epg_api = 'fvAEPg.json?'
-epg_api += 'rsp-subtree=children&rsp-subtree-class=fvSubnet&rsp-subtree-include=required'
-
-@pytest.mark.parametrize(
- "icurl_outputs, cversion, expected_result",
- [
- (
- {bd_api: read_data(dir, "fvBD.json"),
- epg_api: read_data(dir, "fvAEPg_empty.json"),
- "fvRsBd.json": read_data(dir, "fvRsBd.json")},
- "4.2(6a)",
- script.NA,
- ),
- (
- {bd_api: read_data(dir, "fvBD.json"),
- epg_api: read_data(dir, "fvAEPg_pos.json"),
- "fvRsBd.json": read_data(dir, "fvRsBd.json")},
- "4.2(6a)",
- script.FAIL_O,
- ),
- (
- {bd_api: read_data(dir, "fvBD.json"),
- epg_api: read_data(dir, "fvAEPg_pos.json"),
- "fvRsBd.json": read_data(dir, "fvRsBd.json")},
- "5.1(1a)",
- script.FAIL_O,
- ),
- (
- {bd_api: read_data(dir, "fvBD.json"),
- epg_api: read_data(dir, "fvAEPg_neg.json"),
- "fvRsBd.json": read_data(dir, "fvRsBd.json")},
- "5.1(1a)",
- script.PASS,
- ),
- (
- {bd_api: read_data(dir, "fvBD.json"),
- epg_api: read_data(dir, "fvAEPg_neg.json"),
- "fvRsBd.json": read_data(dir, "fvRsBd.json")},
- "5.2(8h)",
- script.PASS,
- ),
-
- ],
-)
-def test_logic(mock_icurl, cversion, expected_result):
- result = script.subnet_scope_check(1, 1, script.AciVersion(cversion))
- assert result == expected_result
diff --git a/tests/switch_bootflash_usage_check/test_switch_bootflash_usage_check.py b/tests/switch_bootflash_usage_check/test_switch_bootflash_usage_check.py
deleted file mode 100644
index 5b160642..00000000
--- a/tests/switch_bootflash_usage_check/test_switch_bootflash_usage_check.py
+++ /dev/null
@@ -1,46 +0,0 @@
-import os
-import pytest
-import logging
-import importlib
-from helpers.utils import read_data
-
-script = importlib.import_module("aci-preupgrade-validation-script")
-
-log = logging.getLogger(__name__)
-dir = os.path.dirname(os.path.abspath(__file__))
-
-
-# icurl queries
-partitions = 'eqptcapacityFSPartition.json'
-partitions += '?query-target-filter=eq(eqptcapacityFSPartition.path,"/bootflash")'
-
-download_sts = 'maintUpgJob.json'
-download_sts += '?query-target-filter=and(eq(maintUpgJob.dnldStatus,"downloaded")'
-download_sts += ',eq(maintUpgJob.desiredVersion,"n9000-16.0(2h)"))'
-
-@pytest.mark.parametrize(
- "icurl_outputs, tversion, expected_result",
- [
- (
- {partitions: read_data(dir, "eqptcapacityFSPartition.json"),
- download_sts: read_data(dir, "maintUpgJob_not_downloaded.json")},
- "6.0(2h)",
- script.FAIL_UF,
- ),
- (
- {partitions: read_data(dir, "eqptcapacityFSPartition.json"),
- download_sts: read_data(dir, "maintUpgJob_pre_downloaded.json")},
- "6.0(2h)",
- script.PASS,
- ),
- (
- {partitions: read_data(dir, "eqptcapacityFSPartition.json"),
- download_sts: read_data(dir, "maintUpgJob_old_ver_no_prop.json")},
- "6.0(2h)",
- script.FAIL_UF,
- ),
- ],
-)
-def test_logic(mock_icurl, tversion, expected_result):
- result = script.switch_bootflash_usage_check(1, 1, script.AciVersion(tversion))
- assert result == expected_result
diff --git a/tests/test_AciResult.py b/tests/test_AciResult.py
index c427712d..b4b9e6cd 100644
--- a/tests/test_AciResult.py
+++ b/tests/test_AciResult.py
@@ -1,27 +1,29 @@
import pytest
import importlib
-import json
from six import string_types
script = importlib.import_module("aci-preupgrade-validation-script")
+AciResult = script.AciResult
+Result = script.Result
@pytest.mark.parametrize(
- "func_name, name, description, result, recommended_action, reason, doc_url, column, row, unformatted_column, unformatted_rows, expected_show, expected_criticality, expected_passed",
+ "func_name, name, result_obj, expected_show, expected_criticality, expected_passed",
[
# Check 1: NA
(
"fake_func_name_NA_test",
"NA",
- "",
- script.NA,
- "",
- "",
- "",
- ["col1", "col2"],
- [["row1", "row2"], ["row3", "row4"]],
- ["col1", "col2"],
- [["row1", "row2"], ["row3", "row4"]],
+ Result(
+ result=script.NA,
+ recommended_action="",
+ msg="",
+ doc_url="",
+ headers=["col1", "col2"],
+ data=[["row1", "row2"], ["row3", "row4"]],
+ unformatted_headers=["col1", "col2"],
+ unformatted_data=[["row1", "row2"], ["row3", "row4"]],
+ ),
False,
"informational",
"passed"
@@ -30,15 +32,16 @@
(
"fake_func_name_PASS_test",
"PASS",
- "",
- script.PASS,
- "",
- "",
- "",
- [],
- [],
- [],
- [],
+ Result(
+ result=script.PASS,
+ recommended_action="",
+ msg="",
+ doc_url="",
+ headers=[],
+ data=[],
+ unformatted_headers=[],
+ unformatted_data=[],
+ ),
True,
"informational",
"passed"
@@ -47,15 +50,16 @@
(
"fake_func_name_POST_test",
"POST",
- "",
- script.POST,
- "reboot",
- "test reason",
- "https://test_doc_url.html",
- ["col1", "col2"],
- [["row1", "row2"], ["row3", "row4"]],
- ["col1", "col2"],
- [["row1", "row2"], ["row3", "row4"]],
+ Result(
+ result=script.POST,
+ recommended_action="reboot",
+ msg="test reason",
+ doc_url="https://test_doc_url.html",
+ headers=["col1", "col2"],
+ data=[["row1", "row2"], ["row3", "row4"]],
+ unformatted_headers=["col1", "col2"],
+ unformatted_data=[["row1", "row2"], ["row3", "row4"]],
+ ),
False,
"informational",
"failed"
@@ -64,15 +68,16 @@
(
"fake_func_name_MANUAL_test",
"MANUAL",
- "",
- script.MANUAL,
- "reboot",
- "test reason",
- "https://test_doc_url.html",
- ["col1", "col2"],
- [["row1", "row2"], ["row3", "row4"]],
- ["col1", "col2"],
- [["row1", "row2"], ["row3", "row4"]],
+ Result(
+ result=script.MANUAL,
+ recommended_action="reboot",
+ msg="test reason",
+ doc_url="https://test_doc_url.html",
+ headers=["col1", "col2"],
+ data=[["row1", "row2"], ["row3", "row4"]],
+ unformatted_headers=["col1", "col2"],
+ unformatted_data=[["row1", "row2"], ["row3", "row4"]],
+ ),
True,
"warning",
"failed"
@@ -81,15 +86,16 @@
(
"fake_func_name_ERROR_test",
"ERROR",
- "",
- script.ERROR,
- "reboot",
- "test reason",
- "https://test_doc_url.html",
- ["col1", "col2"],
- [["row1", "row2"], ["row3", "row4"]],
- ["col1", "col2"],
- [["row1", "row2"], ["row3", "row4"]],
+ Result(
+ result=script.ERROR,
+ recommended_action="reboot",
+ msg="test reason",
+ doc_url="https://test_doc_url.html",
+ headers=["col1", "col2"],
+ data=[["row1", "row2"], ["row3", "row4"]],
+ unformatted_headers=["col1", "col2"],
+ unformatted_data=[["row1", "row2"], ["row3", "row4"]],
+ ),
True,
"major",
"failed"
@@ -98,15 +104,16 @@
(
"fake_func_name_FAIL_UF_test",
"FAIL_UF",
- "",
- script.FAIL_UF,
- "reboot",
- "test reason",
- "https://test_doc_url.html",
- ["col1", "col2"],
- [["row1", "row2"], ["row3", "row4"]],
- ["col1", "col2"],
- [["row1", "row2"], ["row3", "row4"]],
+ Result(
+ result=script.FAIL_UF,
+ recommended_action="reboot",
+ msg="test reason",
+ doc_url="https://test_doc_url.html",
+ headers=["col1", "col2"],
+ data=[["row1", "row2"], ["row3", "row4"]],
+ unformatted_headers=["col1", "col2"],
+ unformatted_data=[["row1", "row2"], ["row3", "row4"]],
+ ),
True,
"critical",
"failed"
@@ -115,15 +122,16 @@
(
"fake_func_name_FAIL_O_test",
"FAIL_O",
- "",
- script.FAIL_O,
- "reboot",
- "test reason",
- "https://test_doc_url.html",
- ["col1", "col2", "col3"],
- [["row1", "row2", "row3"], ["row4", "row5", "row6"]],
- ["col4", "col5"],
- [["row1", "row2"], ["row3", "row4"]],
+ Result(
+ result=script.FAIL_O,
+ recommended_action="reboot",
+ msg="test reason",
+ doc_url="https://test_doc_url.html",
+ headers=["col1", "col2", "col3"],
+ data=[["row1", "row2", "row3"], ["row4", "row5", "row6"]],
+ unformatted_headers=["col4", "col5"],
+ unformatted_data=[["row1", "row2"], ["row3", "row4"]],
+ ),
True,
"critical",
"failed"
@@ -132,15 +140,16 @@
(
"fake_func_name_FAIL_O_formatted_only_test",
"FAIL_O Formatted only",
- "",
- script.FAIL_O,
- "reboot",
- "test reason",
- "https://test_doc_url.html",
- ["col1", "col2", "col3"],
- [["row1", None, 3], ["row4", None, 3]],
- [],
- [],
+ Result(
+ result=script.FAIL_O,
+ recommended_action="reboot",
+ msg="test reason",
+ doc_url="https://test_doc_url.html",
+ headers=["col1", "col2", "col3"],
+ data=[["row1", None, 3], ["row4", None, 3]],
+ unformatted_headers=[],
+ unformatted_data=[],
+ ),
True,
"critical",
"failed"
@@ -149,15 +158,16 @@
(
"fake_func_name_FAIL_O_unformatted_only_test",
"FAIL_O Unformatted only",
- "",
- script.FAIL_O,
- "reboot",
- "test reason",
- "https://test_doc_url.html",
- [],
- [],
- ["col1", "col2", "col3"],
- [["row1", None, 3], ["row4", None, 3]],
+ Result(
+ result=script.FAIL_O,
+ recommended_action="reboot",
+ msg="test reason",
+ doc_url="https://test_doc_url.html",
+ headers=[],
+ data=[],
+ unformatted_headers=["col1", "col2", "col3"],
+ unformatted_data=[["row1", None, 3], ["row4", None, 3]],
+ ),
True,
"critical",
"failed"
@@ -167,35 +177,24 @@
def test_AciResult(
func_name,
name,
- description,
- result,
- recommended_action,
- reason,
- doc_url,
- column,
- row,
- unformatted_column,
- unformatted_rows,
+ result_obj,
expected_show,
expected_criticality,
expected_passed,
):
- synth = script.AciResult(func_name, name, description)
- synth.updateWithResults(result, recommended_action, reason, doc_url, column, row, unformatted_column, unformatted_rows)
- file = synth.writeResult()
- with open(file, "r") as f:
- data = json.load(f)
- assert data["ruleId"] == func_name
- assert data["showValidation"] == expected_show
- assert data["severity"] == expected_criticality
- assert data["ruleStatus"] == expected_passed
- for entry in data["failureDetails"]["data"]:
+ synth = AciResult(func_name, name, result_obj)
+ assert synth.ruleId == func_name
+ assert synth.showValidation == expected_show
+ assert synth.severity == expected_criticality
+ assert synth.ruleStatus == expected_passed
+ for entry in synth.failureDetails["data"]:
for vals in entry.values():
assert isinstance(vals, string_types)
- for entry in data["failureDetails"]["unformatted_data"]:
+ for entry in synth.failureDetails["unformatted_data"]:
for vals in entry.values():
assert isinstance(vals, string_types)
+
@pytest.mark.parametrize(
"headers, data",
[
@@ -206,12 +205,13 @@ def test_AciResult(
)
def test_invalid_headers_or_data(headers, data):
with pytest.raises(TypeError):
- synth = script.AciResult("func_name", "Check Title", "A Description")
- synth.craftData(
+ synth = AciResult("func_name", "Check Title")
+ synth.convert_data(
column=headers,
rows=data,
)
+
@pytest.mark.parametrize(
"headers, data",
[
@@ -235,8 +235,8 @@ def test_invalid_headers_or_data(headers, data):
)
def test_mismatched_lengths(headers, data):
with pytest.raises(ValueError):
- synth = script.AciResult("func_name", "Check Title", "A Description")
- synth.craftData(
+ synth = AciResult("func_name", "Check Title")
+ synth.convert_data(
column=headers,
rows=data,
)
diff --git a/tests/test_CheckManager.py b/tests/test_CheckManager.py
new file mode 100644
index 00000000..e5c7e794
--- /dev/null
+++ b/tests/test_CheckManager.py
@@ -0,0 +1,353 @@
+import pytest
+import importlib
+import logging
+import time
+import json
+import os
+
+script = importlib.import_module("aci-preupgrade-validation-script")
+AciVersion = script.AciVersion
+AciResult = script.AciResult
+Result = script.Result
+CheckManager = script.CheckManager
+check_wrapper = script.check_wrapper
+
+
+# ----------------------------
+# Fixtures, Helper Functions
+# ----------------------------
+def assert_aci_result_file_with_error(cm, check_id, check_title, msg):
+ filepath = cm.rm.get_result_filepath(check_id)
+ with open(filepath, "r") as f:
+ aci_result = json.load(f)
+ assert aci_result["ruleId"] == check_id
+ assert aci_result["name"] == check_title
+ assert aci_result["ruleStatus"] == AciResult.FAIL
+ assert aci_result["severity"] == "major"
+ assert aci_result["reason"] == msg
+ assert aci_result["failureDetails"]["failType"] == script.ERROR
+
+
+@pytest.fixture
+def mock_generate_thread(monkeypatch, request):
+ """Mock thread in ThreadManager to raise an exception in thread.start().
+
+ This is to test exception handleing in ThreadManager._start_thread().
+ """
+
+ check_id = request.param.get("check_id")
+ exception = request.param.get("exception")
+
+ def thread_start_with_exception(timeout=5.0):
+ raise exception
+
+ def _mock_generate_thread(self, target, args=(), kwargs=None):
+ if kwargs is None:
+ kwargs = {}
+ thread = script.CustomThread(target=target, name=target.__name__, args=args, kwargs=kwargs)
+ thread.daemon = True
+ # Mock only a specifieid thread instance. Otherwise, exception is raised in
+ # the monitoring thread which doesn't use _start_thread().
+ if thread.name == check_id:
+ thread.start = thread_start_with_exception
+ return thread
+
+ monkeypatch.setattr(script.ThreadManager, "_generate_thread", _mock_generate_thread)
+
+
+# ----------------------------
+# Tests
+# ----------------------------
+class TestCheckManager:
+ @pytest.fixture(scope="class")
+ def expected_result_objects(self, result_objects_factory):
+ return result_objects_factory("fullmesh")
+
+ @pytest.fixture(scope="class")
+ def check_funcs(self, check_funcs_factory, expected_result_objects):
+ return check_funcs_factory(expected_result_objects)
+
+ @pytest.fixture(scope="class")
+ def cm(self, check_funcs):
+ _cm = CheckManager()
+ _cm.check_funcs = check_funcs
+ return _cm
+
+ def test_initialize_checks(self, caplog, cm):
+ caplog.set_level(logging.CRITICAL) # Skip logging as it's too noisy for this
+
+ cm.initialize_checks()
+
+ # With init, only titles should be available
+ assert cm.get_check_title("fake_0_check") == "Fake Check 0"
+ assert cm.get_check_title("fake_10_check") == "Fake Check 10"
+ assert cm.get_check_result("fake_0_check") is None
+ assert cm.get_check_result("fake_10_check") is None
+
+ # Check number of initialized checks in result files
+ result_files = os.listdir(script.JSON_DIR)
+ assert len(result_files) == cm.total_checks
+
+ # Check the filename of result files and their `ruleStatus`
+ for check_id in cm.check_ids:
+ filepath = cm.rm.get_result_filepath(check_id)
+ assert os.path.exists(filepath), "Missing result file: {}".format(filepath)
+
+ with open(filepath, "r") as f:
+ aci_result = json.load(f)
+ # At initialize, ruleStatus must be always in-progress
+ assert aci_result["ruleStatus"] == AciResult.IN_PROGRESS
+
+ def test_run_checks(self, caplog, cm, expected_common_data, expected_result_objects):
+ caplog.set_level(logging.CRITICAL) # Skip logging as it's too noisy for this
+
+ cm.run_checks(expected_common_data)
+
+ # With run_checks, both titles and result obj should be available
+ assert cm.get_check_title("fake_0_check") == "Fake Check 0"
+ assert cm.get_check_title("fake_10_check") == "Fake Check 10"
+ assert cm.get_check_result("fake_0_check") is expected_result_objects[0]
+ assert cm.get_check_result("fake_10_check") is expected_result_objects[10]
+
+ # Check the result files
+ for check_id in cm.check_ids:
+ filepath = cm.rm.get_result_filepath(check_id)
+ assert os.path.exists(filepath), "Missing result file: {}".format(filepath)
+
+ with open(filepath, "r") as f:
+ aci_result = json.load(f)
+ assert aci_result["ruleId"] == check_id
+ assert aci_result["name"] == cm.get_check_title(check_id)
+ assert aci_result["ruleStatus"] in (AciResult.PASS, AciResult.FAIL)
+ r = cm.get_check_result(check_id)
+
+ # recommended_action: additional note is added with a certain condition
+ if aci_result["ruleStatus"] == AciResult.FAIL and r.unformatted_headers and r.unformatted_data:
+ assert aci_result["recommended_action"].startswith(r.recommended_action + "\n Note")
+ else:
+ assert aci_result["recommended_action"] == r.recommended_action
+
+ # docUrl
+ assert aci_result["docUrl"] == r.doc_url
+
+ # failureDetails: matters only when ruleStatus is FAIL
+ if aci_result["ruleStatus"] == AciResult.FAIL:
+ assert aci_result["failureDetails"]["failType"] == r.result
+ try:
+ data = AciResult.convert_data(r.headers, r.data)
+ assert aci_result["failureDetails"]["header"] == r.headers
+ assert aci_result["failureDetails"]["data"] == data
+ except Exception:
+ assert aci_result["failureDetails"]["failType"] == script.ERROR
+ assert aci_result["failureDetails"]["header"] == []
+ assert aci_result["failureDetails"]["data"] == []
+
+ if r.unformatted_headers and r.unformatted_data:
+ try:
+ unformatted_data = AciResult.convert_data(r.unformatted_headers, r.unformatted_data)
+ assert aci_result["failureDetails"]["unformatted_header"] == r.unformatted_headers
+ assert aci_result["failureDetails"]["unformatted_data"] == unformatted_data
+ except Exception:
+ assert aci_result["failureDetails"]["failType"] == script.ERROR
+ assert aci_result["failureDetails"]["unformatted_header"] == []
+ assert aci_result["failureDetails"]["unformatted_data"] == []
+
+
+@pytest.mark.parametrize(
+ "api_only, debug_function, expected_total",
+ [
+ (False, None, len(CheckManager.api_checks) + len(CheckManager.ssh_checks) + len(CheckManager.cli_checks)),
+ (True, None, len(CheckManager.api_checks)),
+ (False, CheckManager.api_checks[0].__name__, 1),
+ (True, CheckManager.api_checks[0].__name__, 1),
+ (False, CheckManager.ssh_checks[0].__name__, 1),
+ (True, CheckManager.ssh_checks[0].__name__, 0), # api_only for non-api check = 0
+ ],
+)
+def test_total_checks(api_only, debug_function, expected_total):
+ cm = CheckManager(api_only, debug_function)
+ assert cm.total_checks == expected_total
+
+
+def test_exception_in_initialize():
+ """Exception in initialize is not captured by CheckManager.
+ The exception should go up to the script's main() and abort the script
+ because there is likely some fundamental issue in the system"""
+ cm = CheckManager()
+ cm.initialize_check = lambda x, y: 1 / 0 # Zero Division Error for a quick exception
+ with pytest.raises(ZeroDivisionError):
+ cm.initialize_checks()
+
+
+def test_memerror_in_check():
+ @check_wrapper(check_title="Memory Error Check")
+ def memerr_check(**kwargs):
+ raise MemoryError
+
+ cm = CheckManager()
+ cm.check_funcs = [memerr_check]
+ cm.initialize_checks()
+ cm.run_checks({"fake_common_data": True})
+
+ assert_aci_result_file_with_error(
+ cm, "memerr_check", "Memory Error Check", "Not enough memory to complete this check."
+ )
+
+
+def test_exception_in_check():
+ @check_wrapper(check_title="Bad Check")
+ def bad_check(**kwargs):
+ raise Exception("This is a test exception")
+
+ cm = CheckManager()
+ cm.check_funcs = [bad_check]
+ cm.initialize_checks()
+ cm.run_checks({"fake_common_data": True})
+
+ assert_aci_result_file_with_error(cm, "bad_check", "Bad Check", "Unexpected Error: This is a test exception")
+
+
+def test_exception_in_finalize_check_due_to_bad_check():
+ """Exceptions in `finalize_check` due to bad return value from each check.
+
+ This exception happens in `try` of `check_wrapper`, but `finalize_check`
+ in the corresponding `except` works as it calls `finalize_check` again
+ with a valid `Result` object with status ERROR.
+ """
+
+ @check_wrapper(check_title="Non-Result Obj Check")
+ def non_result_check(**kwargs):
+ return "Not Result Obj" # instead of `Result` obj
+
+ @check_wrapper(check_title="Invalid Result Check")
+ def invalid_result_check(**kwargs):
+ # length of header and each row of data must be the same
+ return Result(result=script.FAIL_O, headers=["H1", "H2"], data=[["D1"], ["D2"]])
+
+ cm = CheckManager()
+ cm.check_funcs = [non_result_check, invalid_result_check]
+ cm.initialize_checks()
+ cm.run_checks({"fake_common_data": True})
+
+ checks = [
+ {
+ "id": "non_result_check",
+ "title": "Non-Result Obj Check",
+ "msg": "Unexpected Error: The result of non_result_check is not a `Result` object",
+ },
+ {
+ "id": "invalid_result_check",
+ "title": "Invalid Result Check",
+ "msg": "Unexpected Error: Row length (1), data: ['D1'] does not match column length (2).",
+ },
+ ]
+ for check in checks:
+ assert_aci_result_file_with_error(cm, check["id"], check["title"], check["msg"])
+
+
+def test_exception_in_finalize_check():
+ """Exception in `finalize_check` itself.
+
+ This could happen when the filesystem is full, permission denied to write
+ the result file etc.
+ """
+
+ # Check exception is caught in try of check_wrapper, then finalize_check
+ # fails in the corresponding except.
+ @check_wrapper(check_title="Bad Check With Bad Finalizer")
+ def bad_check_with_bad_finalizer(**kwargs):
+ raise Exception("Bad check to test finalize_check failure")
+
+ # Check is good but finalize_check failed in try of check_wrapper, then it
+ # fails in the corresponding except again.
+ @check_wrapper(check_title="Good Check With Bad Finalizer")
+ def good_check_with_bad_finalizer(**kwargs):
+ return Result(result=script.PASS)
+
+ cm = CheckManager()
+ cm.finalize_check = lambda x, y: 1 / 0 # Zero Division Error for a quick exception
+ cm.check_funcs = [bad_check_with_bad_finalizer, good_check_with_bad_finalizer]
+ cm.initialize_checks()
+ with pytest.raises(ZeroDivisionError):
+ cm.run_checks({"fake_common_data": True})
+
+
+@pytest.mark.parametrize(
+ "mock_generate_thread",
+ [
+ {"check_id": "good_check_with_thread_start_failure", "exception": RuntimeError("can't start new thread")},
+ {"check_id": "good_check_with_thread_start_failure", "exception": RuntimeError("unknown runtime error")},
+ {"check_id": "good_check_with_thread_start_failure", "exception": Exception("unknown exception")},
+ ],
+ indirect=True,
+)
+def test_exception_in_starting_thread(mock_generate_thread):
+ @check_wrapper(check_title="Good Check With Failure in Starting Thread")
+ def good_check_with_thread_start_failure(**kwargs):
+ return Result(result=script.PASS)
+
+ cm = CheckManager()
+ cm.check_funcs = [good_check_with_thread_start_failure]
+ cm.initialize_checks()
+ cm.run_checks({"fake_common_data": True})
+
+ assert_aci_result_file_with_error(
+ cm,
+ "good_check_with_thread_start_failure",
+ "Good Check With Failure in Starting Thread",
+ "Skipped due to a failure in starting a thread for this check.",
+ )
+
+
+@pytest.mark.parametrize(
+ "mock_generate_thread",
+ [
+ {"check_id": "good_check_with_start_failure_and_exc_in_callback", "exception": Exception("unknown exception")},
+ ],
+ indirect=True,
+)
+def test_exception_in_finalize_check_on_thread_failure(mock_generate_thread):
+ """Exception in failure callback. Should not catch the exception and let the script fail"""
+ @check_wrapper(check_title="Good Check With Failure in Starting Thread")
+ def good_check_with_start_failure_and_exc_in_callback(**kwargs):
+ return Result(result=script.PASS)
+
+ cm = CheckManager()
+ cm.finalize_check_on_thread_failure = lambda x: 1 / 0 # Zero Division Error for a quick exception
+ cm.check_funcs = [good_check_with_start_failure_and_exc_in_callback]
+ cm.initialize_checks()
+ with pytest.raises(ZeroDivisionError):
+ cm.run_checks({"fake_common_data": True})
+
+
+def test_monitor_timeout():
+ @check_wrapper(check_title="Timeout Check")
+ def timeout_check(**kwargs):
+ time.sleep(60)
+
+ timeout = 1 # sec
+ cm = CheckManager(timeout=timeout)
+ cm.check_funcs = [timeout_check]
+ cm.initialize_checks()
+ cm.run_checks({"fake_common_data": True})
+ assert cm.timeout_event.is_set()
+
+ assert_aci_result_file_with_error(
+ cm, "timeout_check", "Timeout Check", "Timeout. Unable to finish in time ({} sec).".format(timeout)
+ )
+
+
+def test_exception_in_finalize_check_on_thread_timeout():
+ """Exception in failure callback. Should not catch the exception and let the script fail"""
+ @check_wrapper(check_title="Timeout Check")
+ def timeout_check_with_exc_in_callback(**kwargs):
+ time.sleep(60)
+
+ timeout = 1 # sec
+ cm = CheckManager(timeout=timeout)
+ cm.finalize_check_on_thread_timeout = lambda x: 1 / 0 # Zero Division Error for a quick exception
+ cm.check_funcs = [timeout_check_with_exc_in_callback]
+ cm.initialize_checks()
+ with pytest.raises(ZeroDivisionError):
+ cm.run_checks({"fake_common_data": True})
+ assert cm.timeout_event.is_set()
diff --git a/tests/test_ResultManager.py b/tests/test_ResultManager.py
new file mode 100644
index 00000000..893b99af
--- /dev/null
+++ b/tests/test_ResultManager.py
@@ -0,0 +1,123 @@
+import importlib
+import json
+
+script = importlib.import_module("aci-preupgrade-validation-script")
+AciResult = script.AciResult
+Result = script.Result
+
+
+def _test_init_result(rm, fake_checks):
+ for fake_check in fake_checks:
+ rm.init_result(**fake_check)
+
+ assert len(rm.titles) == len(fake_checks)
+
+ for check_id in rm.titles:
+ expected_title = [check["check_title"] for check in fake_checks if check["check_id"] == check_id][0]
+ assert rm.titles[check_id] == expected_title
+
+ filepath = rm.get_result_filepath(check_id)
+ with open(filepath, "r") as f:
+ aci_result = json.load(f)
+ assert aci_result["ruleId"] == check_id
+ assert aci_result["name"] == rm.titles[check_id]
+ assert aci_result["ruleStatus"] == AciResult.IN_PROGRESS
+ assert aci_result["severity"] == "informational"
+ assert aci_result["recommended_action"] == ""
+ assert aci_result["docUrl"] == ""
+ assert aci_result["failureDetails"]["failType"] == ""
+ assert aci_result["failureDetails"]["header"] == []
+ assert aci_result["failureDetails"]["data"] == []
+ assert aci_result["failureDetails"]["unformatted_header"] == []
+ assert aci_result["failureDetails"]["unformatted_data"] == []
+
+
+def _test_update_result(rm, fake_checks):
+ for fake_check in fake_checks:
+ rm.update_result(**fake_check)
+
+ inited_fake_checks = [check for check in fake_checks if check["check_id"] in rm.titles]
+ assert len(rm.results) == len(inited_fake_checks)
+
+ for check_id in rm.results:
+ expected_result_obj = [check["result_obj"] for check in fake_checks if check["check_id"] == check_id][0]
+ r = rm.results[check_id]
+ assert r == expected_result_obj
+
+ filepath = rm.get_result_filepath(check_id)
+ with open(filepath, "r") as f:
+ aci_result = json.load(f)
+ assert aci_result["ruleId"] == check_id
+ assert aci_result["name"] == rm.titles[check_id]
+ assert aci_result["ruleStatus"] in (AciResult.PASS, AciResult.FAIL)
+ if r.unformatted_data:
+ assert aci_result["recommended_action"].startswith(r.recommended_action)
+ else:
+ assert aci_result["recommended_action"] == r.recommended_action
+ assert aci_result["docUrl"] == r.doc_url
+ assert aci_result["failureDetails"]["failType"] == "" if r.result == script.PASS else r.result
+ assert aci_result["failureDetails"]["header"] == r.headers
+ assert aci_result["failureDetails"]["data"] == AciResult.convert_data(r.headers, r.data)
+ assert aci_result["failureDetails"]["unformatted_header"] == r.unformatted_headers
+ assert aci_result["failureDetails"]["unformatted_data"] == AciResult.convert_data(r.unformatted_headers, r.unformatted_data)
+
+
+def test_ResultManager():
+ rm = script.ResultManager()
+ fake_checks_for_init = [
+ {"check_id": "puv_1_check", "check_title": "PUV 1"},
+ {"check_id": "puv_2_check", "check_title": "PUV 2"},
+ ]
+ fake_checks_for_update = [
+ {
+ "check_id": "puv_1_check",
+ "result_obj": Result(
+ result=script.PASS,
+ recommended_action="",
+ msg="",
+ doc_url="",
+ headers=[],
+ data=[],
+ unformatted_headers=[],
+ unformatted_data=[],
+ ),
+ },
+ {
+ "check_id": "puv_2_check",
+ "result_obj": Result(
+ result=script.FAIL_UF,
+ recommended_action="reboot",
+ msg="test reason",
+ doc_url="https://test_doc_url.html",
+ headers=["col1", "col2"],
+ data=[["row1", "row2"], ["row3", "row4"]],
+ unformatted_headers=["col1", "col2"],
+ unformatted_data=[["row1", "row2"], ["row3", "row4"]],
+ ),
+ },
+ {
+ "check_id": "no_init_check",
+ "result_obj": Result(
+ result=script.FAIL_UF,
+ recommended_action="reboot",
+ msg="test reason",
+ doc_url="https://test_doc_url.html",
+ headers=["col1", "col2"],
+ data=[["row1", "row2"], ["row3", "row4"]],
+ unformatted_headers=["col1", "col2"],
+ unformatted_data=[["row1", "row2"], ["row3", "row4"]],
+ ),
+ },
+ ]
+ _test_init_result(rm, fake_checks_for_init)
+ _test_update_result(rm, fake_checks_for_update)
+
+ summary = rm.get_summary()
+ assert len(summary) == 8 # [PASS, FAIL_O, FAIL_UF, MANUAL, POST, NA, ERROR, 'TOTAL']
+ for key in summary:
+ if key == "TOTAL":
+ expected_num = len([c for c in fake_checks_for_update if c["check_id"] != "no_init_check"])
+ else:
+ expected_num = len([c for c in fake_checks_for_update if c["result_obj"].result == key and c["check_id"] != "no_init_check"])
+
+ assert summary[key] == expected_num
diff --git a/tests/test_ThreadManager.py b/tests/test_ThreadManager.py
new file mode 100644
index 00000000..4b02f3bb
--- /dev/null
+++ b/tests/test_ThreadManager.py
@@ -0,0 +1,49 @@
+from __future__ import print_function
+import importlib
+import time
+
+script = importlib.import_module("aci-preupgrade-validation-script")
+
+
+global_timeout = False
+
+
+def task1(data=""):
+ time.sleep(2.5)
+ if not global_timeout:
+ print("Thread task1: Finishing with data {}".format(data))
+
+
+def task2(data=""):
+ time.sleep(0.5)
+ if not global_timeout:
+ print("Thread task2: Finishing with data {}".format(data))
+
+
+def task3(data=""):
+ time.sleep(0.2)
+ if not global_timeout:
+ print("Thread task3: Finishing with data {}".format(data))
+
+
+def test_ThreadManager(capsys):
+ global global_timeout
+ tm = script.ThreadManager(
+ funcs=[task1, task2, task3],
+ common_kwargs={"data": "common_data"},
+ monitor_timeout=1,
+ callback_on_timeout=lambda x: print("Timeout. Abort {}".format(x))
+ )
+ tm.start()
+ tm.join()
+
+ if tm.is_timeout():
+ global_timeout = True
+
+ expected_output = """\
+Thread task3: Finishing with data common_data
+Thread task2: Finishing with data common_data
+Timeout. Abort task1
+"""
+ captured = capsys.readouterr()
+ assert captured.out == expected_output
diff --git a/tests/test_common_data.py b/tests/test_common_data.py
new file mode 100644
index 00000000..b9276282
--- /dev/null
+++ b/tests/test_common_data.py
@@ -0,0 +1,634 @@
+import pytest
+import importlib
+import logging
+import json
+import sys
+
+script = importlib.import_module("aci-preupgrade-validation-script")
+AciVersion = script.AciVersion
+
+
+# ------------------------------
+# Data and fixtures
+# ------------------------------
+
+
+@pytest.fixture(autouse=True)
+def mock_get_credentials(monkeypatch):
+ """Mock the get_credentials function to return a fixed username and password."""
+
+ def _mock_get_credentials():
+ return ("admin", "mypassword")
+
+ monkeypatch.setattr(script, "get_credentials", _mock_get_credentials)
+
+
+@pytest.fixture(autouse=True)
+def mock_get_target_version(monkeypatch):
+ """
+ Mock `get_target_version()` to return a fixed target version.
+ Used when the script is run without the `-t` option which is simulated by
+ `arg_tversion`.
+ Not using `mock_icurl` because this function involves a user interaction to
+ select a version.
+ """
+
+ def _mock_get_target_version(arg_tversion):
+ if arg_tversion:
+ script.prints("Target APIC version is overridden to %s\n" % arg_tversion)
+ try:
+ target_version = AciVersion(arg_tversion)
+ except ValueError as e:
+ script.prints(e)
+ sys.exit(1)
+ return target_version
+ return AciVersion("6.2(1a)")
+
+ monkeypatch.setattr(script, "get_target_version", _mock_get_target_version)
+
+
+_icurl_outputs = {
+ "fabricNode.json": [
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.1",
+ "dn": "topology/pod-1/node-1",
+ "fabricSt": "commissioned",
+ "id": "1",
+ "model": "APIC-SERVER-L2",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "apic1",
+ "nodeType": "unspecified",
+ "role": "controller",
+ "version": "6.1(1a)",
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.2",
+ "dn": "topology/pod-1/node-2",
+ "fabricSt": "commissioned",
+ "id": "2",
+ "model": "APIC-SERVER-L2",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "apic2",
+ "nodeType": "unspecified",
+ "role": "controller",
+ "version": "6.1(1a)",
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.3",
+ "dn": "topology/pod-2/node-3",
+ "fabricSt": "commissioned",
+ "id": "3",
+ "model": "APIC-SERVER-L2",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "apic3",
+ "nodeType": "unspecified",
+ "role": "controller",
+ "version": "6.1(1a)",
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.101",
+ "dn": "topology/pod-1/node-101",
+ "fabricSt": "active",
+ "id": "101",
+ "model": "N9K-C93180YC-FX",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "leaf101",
+ "nodeType": "unspecified",
+ "role": "leaf",
+ "version": "n9000-16.1(1a)",
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.102",
+ "dn": "topology/pod-1/node-102",
+ "fabricSt": "active",
+ "id": "102",
+ "model": "N9K-C93180YC-FX",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "leaf102",
+ "nodeType": "unspecified",
+ "role": "leaf",
+ "version": "n9000-16.1(1a)",
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.111",
+ "dn": "topology/pod-1/node-1001",
+ "fabricSt": "active",
+ "id": "1001",
+ "model": "N9K-C9504",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "spine1001",
+ "nodeType": "unspecified",
+ "role": "spine",
+ "version": "n9000-16.1(1a)",
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.201",
+ "dn": "topology/pod-2/node-201",
+ "fabricSt": "active",
+ "id": "201",
+ "model": "N9K-C93180YC-FX3",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "leaf201",
+ "nodeType": "unspecified",
+ "role": "leaf",
+ "version": "n9000-16.0(9d)",
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.211",
+ "dn": "topology/pod-2/node-2001",
+ "fabricSt": "active",
+ "id": "2001",
+ "model": "N9K-C9504",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "spine2001",
+ "nodeType": "unspecified",
+ "role": "spine",
+ "version": "n9000-16.1(1a)",
+ }
+ }
+ },
+ ],
+ "fabricNodePEp.json": [
+ {"fabricNodePEp": {"attributes": {"dn": "uni/fabric/protpol/expgep-101-102/nodepep-101", "id": "101"}}},
+ {"fabricNodePEp": {"attributes": {"dn": "uni/fabric/protpol/expgep-101-102/nodepep-102", "id": "102"}}},
+ ],
+}
+
+_icurl_outputs_old = {
+ # fabricNode.version in older versions like 3.2 shows an invalid version like "A"
+ # for controller and empty for active switches.
+ "fabricNode.json": [
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.1",
+ "dn": "topology/pod-1/node-1",
+ "fabricSt": "commissioned",
+ "id": "1",
+ "model": "APIC-SERVER-L2",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "apic1",
+ "nodeType": "unspecified",
+ "role": "controller",
+ "version": "A",
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.2",
+ "dn": "topology/pod-1/node-2",
+ "fabricSt": "commissioned",
+ "id": "2",
+ "model": "APIC-SERVER-L2",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "apic2",
+ "nodeType": "unspecified",
+ "role": "controller",
+ "version": "A",
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.3",
+ "dn": "topology/pod-2/node-3",
+ "fabricSt": "commissioned",
+ "id": "3",
+ "model": "APIC-SERVER-L2",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "apic3",
+ "nodeType": "unspecified",
+ "role": "controller",
+ "version": "A",
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.101",
+ "dn": "topology/pod-1/node-101",
+ "fabricSt": "active",
+ "id": "101",
+ "model": "N9K-C93180YC-FX",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "leaf101",
+ "nodeType": "unspecified",
+ "role": "leaf",
+ "version": "",
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.102",
+ "dn": "topology/pod-1/node-102",
+ "fabricSt": "active",
+ "id": "102",
+ "model": "N9K-C93180YC-FX",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "leaf102",
+ "nodeType": "unspecified",
+ "role": "leaf",
+ "version": "",
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.111",
+ "dn": "topology/pod-1/node-1001",
+ "fabricSt": "active",
+ "id": "1001",
+ "model": "N9K-C9504",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "spine1001",
+ "nodeType": "unspecified",
+ "role": "spine",
+ "version": "",
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.201",
+ "dn": "topology/pod-2/node-201",
+ "fabricSt": "active",
+ "id": "201",
+ "model": "N9K-C93180YC-FX3",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "leaf201",
+ "nodeType": "unspecified",
+ "role": "leaf",
+ "version": "",
+ }
+ }
+ },
+ {
+ "fabricNode": {
+ "attributes": {
+ "address": "10.0.0.211",
+ "dn": "topology/pod-2/node-2001",
+ "fabricSt": "active",
+ "id": "2001",
+ "model": "N9K-C9504",
+ "monPolDn": "uni/fabric/monfab-default",
+ "name": "spine2001",
+ "nodeType": "unspecified",
+ "role": "spine",
+ "version": "",
+ }
+ }
+ },
+ ],
+ "fabricNodePEp.json": _icurl_outputs["fabricNodePEp.json"],
+ "topology/pod-1/node-1/sys/ctrlrfwstatuscont/ctrlrrunning.json": [
+ {
+ "firmwareCtrlrRunning": {
+ "attributes": {
+ "dn": "topology/pod-1/node-1/sys/ctrlrfwstatuscont/ctrlrrunning",
+ "type": "controller",
+ "version": "3.2(7f)"
+ }
+ }
+ }
+ ],
+ "firmwareRunning.json": [
+ {
+ "firmwareRunning": {
+ "attributes": {
+ "dn": "topology/pod-1/node-101/sys/fwstatuscont/running",
+ "peVer": "3.1(2u)",
+ "type": "switch",
+ "version": "n9000-13.1(2u)"
+ }
+ }
+ },
+ {
+ "firmwareRunning": {
+ "attributes": {
+ "dn": "topology/pod-1/node-102/sys/fwstatuscont/running",
+ "peVer": "3.2(7f)",
+ "type": "switch",
+ "version": "n9000-13.2(7f)"
+ }
+ }
+ },
+ {
+ "firmwareRunning": {
+ "attributes": {
+ "dn": "topology/pod-1/node-1001/sys/fwstatuscont/running",
+ "peVer": "3.2(7f)",
+ "type": "switch",
+ "version": "n9000-13.2(7f)"
+ }
+ }
+ },
+ {
+ "firmwareRunning": {
+ "attributes": {
+ "dn": "topology/pod-2/node-201/sys/fwstatuscont/running",
+ "peVer": "3.2(7f)",
+ "type": "switch",
+ "version": "n9000-13.2(7f)"
+ }
+ }
+ },
+ {
+ "firmwareRunning": {
+ "attributes": {
+ "dn": "topology/pod-2/node-2001/sys/fwstatuscont/running",
+ "peVer": "3.2(7f)",
+ "type": "switch",
+ "version": "n9000-13.2(7f)"
+ }
+ }
+ },
+
+ ],
+}
+
+
+@pytest.fixture(scope="function")
+def fake_args(request):
+ data = {
+ "api_only": False,
+ "cversion": None,
+ "tversion": None,
+ }
+ # update data contents when parametrize provides non-falsy values
+ for key in data:
+ if request.param.get(key, "non_falsy_default") != "non_falsy_default":
+ data[key] = request.param[key]
+ return data
+
+
+# ------------------------------
+# Tests
+# ------------------------------
+
+
+@pytest.mark.parametrize(
+ "icurl_outputs, fake_args, expected_common_data",
+ [
+ # Default, no argparse arguments
+ pytest.param(
+ _icurl_outputs,
+ {},
+ {
+ "cversion": AciVersion("6.1(1a)"),
+ "sw_cversion": AciVersion("6.0(9d)"),
+ "tversion": AciVersion("6.2(1a)"),
+ "fabric_nodes": _icurl_outputs["fabricNode.json"],
+ "vpc_node_ids": ["101", "102"],
+ },
+ id="default_no_args",
+ ),
+ # Default, no argparse arguments, old ACI version
+ pytest.param(
+ _icurl_outputs_old,
+ {},
+ {
+ "cversion": AciVersion("3.2(7f)"),
+ "sw_cversion": AciVersion("3.1(2u)"),
+ "tversion": AciVersion("6.2(1a)"),
+ "fabric_nodes": _icurl_outputs_old["fabricNode.json"],
+ "vpc_node_ids": ["101", "102"],
+ },
+ id="default_no_args_old_aci",
+ ),
+ # `api_only` is True.
+ # No `get_credentials()`, no username nor password
+ pytest.param(
+ _icurl_outputs,
+ {
+ "api_only": True,
+ },
+ {
+ "username": None,
+ "password": None,
+ "cversion": AciVersion("6.1(1a)"),
+ "sw_cversion": AciVersion("6.0(9d)"),
+ "tversion": AciVersion("6.2(1a)"),
+ "fabric_nodes": _icurl_outputs["fabricNode.json"],
+ "vpc_node_ids": ["101", "102"],
+ },
+ id="api_only",
+ ),
+ # `arg_tversion` is provided (i.e. -t 6.1(4a))
+ pytest.param(
+ _icurl_outputs,
+ {
+ "tversion": "6.1(4a)",
+ },
+ {
+ "cversion": AciVersion("6.1(1a)"),
+ "sw_cversion": AciVersion("6.0(9d)"),
+ "tversion": AciVersion("6.1(4a)"),
+ "fabric_nodes": _icurl_outputs["fabricNode.json"],
+ "vpc_node_ids": ["101", "102"],
+ },
+ id="tversion",
+ ),
+ # `arg_tversion` and `arg_cversion` are both provided (i.e. -t 6.1(4a))
+ pytest.param(
+ _icurl_outputs,
+ {
+ "cversion": "6.0(8d)",
+ "tversion": "6.1(4a)",
+ },
+ {
+ "cversion": AciVersion("6.0(8d)"),
+ "sw_cversion": AciVersion("6.0(8d)"),
+ "tversion": AciVersion("6.1(4a)"),
+ "fabric_nodes": _icurl_outputs["fabricNode.json"],
+ "vpc_node_ids": ["101", "102"],
+ },
+ id="cversion_tversion",
+ ),
+ # versions are switch syntax
+ pytest.param(
+ _icurl_outputs,
+ {
+ "cversion": "16.0(4d)",
+ "tversion": "16.1(4a)",
+ },
+ {
+ "cversion": AciVersion("6.0(4d)"),
+ "sw_cversion": AciVersion("6.0(4d)"),
+ "tversion": AciVersion("6.1(4a)"),
+ "fabric_nodes": _icurl_outputs["fabricNode.json"],
+ "vpc_node_ids": ["101", "102"],
+ },
+ id="cversion_tversion_with_switch_version_syntax",
+ ),
+ # versions are APIC image name syntax
+ pytest.param(
+ _icurl_outputs,
+ {
+ "cversion": "aci-apic-dk9.6.0.1a.bin",
+ "tversion": "aci-apic-dk9.6.2.1a.bin",
+ },
+ {
+ "cversion": AciVersion("6.0(1a)"),
+ "sw_cversion": AciVersion("6.0(1a)"),
+ "tversion": AciVersion("6.2(1a)"),
+ "fabric_nodes": _icurl_outputs["fabricNode.json"],
+ "vpc_node_ids": ["101", "102"],
+ },
+ id="cversion_tversion_with_apic_image_name_syntax",
+ ),
+ # versions are Switch image name syntax
+ pytest.param(
+ _icurl_outputs,
+ {
+ "cversion": "n9000-16.0(1a).bin",
+ "tversion": "n9000-16.2(1a).bin",
+ },
+ {
+ "cversion": AciVersion("6.0(1a)"),
+ "sw_cversion": AciVersion("6.0(1a)"),
+ "tversion": AciVersion("6.2(1a)"),
+ "fabric_nodes": _icurl_outputs["fabricNode.json"],
+ "vpc_node_ids": ["101", "102"],
+ },
+ id="cversion_tversion_with_switch_image_name_syntax",
+ ),
+ ],
+ indirect=["fake_args", "expected_common_data"],
+)
+def test_common_data(mock_icurl, fake_args, expected_common_data):
+ """test query_common_data and write_script_metadata"""
+ # --- test for `query_common_data()`
+ common_data = script.query_common_data(
+ api_only=fake_args["api_only"], arg_cversion=fake_args["cversion"], arg_tversion=fake_args["tversion"]
+ )
+ for key in common_data:
+ if isinstance(common_data[key], AciVersion):
+ assert str(common_data[key]) == str(expected_common_data[key])
+ else:
+ assert common_data[key] == expected_common_data[key]
+
+ # --- test for `write_script_metadata()`
+ script.write_script_metadata(
+ api_only=fake_args["api_only"], timeout=1200, total_checks=100, common_data=expected_common_data
+ )
+ with open(script.META_FILE, "r") as f:
+ meta = json.load(f)
+ assert meta["name"] == "PreupgradeCheck"
+ assert meta["method"] == "standalone script"
+ assert meta["datetime"] == script.ts + script.tz
+ assert meta["script_version"] == script.SCRIPT_VERSION
+ assert meta["cversion"] == str(expected_common_data["cversion"])
+ assert meta["tversion"] == str(expected_common_data["tversion"])
+ assert meta["sw_cversion"] == str(expected_common_data["sw_cversion"])
+ assert meta["api_only"] == fake_args["api_only"]
+ assert meta["timeout"] == 1200
+ assert meta["total_checks"] == 100
+
+
+@pytest.mark.parametrize("icurl_outputs", [_icurl_outputs])
+def test_tversion_invald(capsys, mock_icurl):
+ with pytest.raises(SystemExit):
+ script.query_common_data(arg_cversion="6.0(1a)", arg_tversion="invalid_version")
+
+ captured = capsys.readouterr()
+ expected_output = """\
+Gathering Node Information...
+
+Current version is overridden to 6.0(1a)
+
+Target APIC version is overridden to invalid_version
+
+Parsing failure of ACI version `invalid_version`
+"""
+ assert captured.out.endswith(expected_output), "captured.out is:\n{}".format(captured.out)
+
+
+@pytest.mark.parametrize("icurl_outputs", [_icurl_outputs])
+def test_cversion_invald(capsys, mock_icurl):
+ with pytest.raises(SystemExit):
+ script.query_common_data(arg_cversion="invalid_version", arg_tversion="6.0(1a)")
+
+ captured = capsys.readouterr()
+ expected_output = """\
+Gathering Node Information...
+
+Current version is overridden to invalid_version
+
+Parsing failure of ACI version `invalid_version`
+"""
+ assert captured.out.endswith(expected_output), "captured.out is:\n{}".format(captured.out)
+
+
+@pytest.mark.parametrize(
+ "icurl_outputs, print_output",
+ [
+ # `get_fabric_nodes()` failure
+ (
+ {
+ "fabricNode.json": [{"error": {"attributes": {"code": "400", "text": "Request failed, unresolved class for dummyClass"}}}],
+ "fabricNodePEp.json": _icurl_outputs["fabricNodePEp.json"],
+ },
+ "Gathering Node Information...\n\n",
+ ),
+ # `get_vpc_nodes()` failure
+ (
+ {
+ "fabricNode.json": _icurl_outputs["fabricNode.json"],
+ "fabricNodePEp.json": [{"error": {"attributes": {"code": "400", "text": "Request failed, unresolved class for dummyClass"}}}],
+ },
+ "Collecting VPC Node IDs...",
+ ),
+ ],
+)
+def test_icurl_failure_in_query_common_data(capsys, caplog, mock_icurl, print_output):
+ caplog.set_level(logging.CRITICAL)
+ with pytest.raises(SystemExit):
+ script.query_common_data()
+ captured = capsys.readouterr()
+ expected_output = (
+ print_output
+ + """
+
+Error: Your current ACI version does not have requested class
+Initial query failed. Ensure APICs are healthy. Ending script run.
+"""
+ )
+ assert captured.out.endswith(expected_output), "captured.out is:\n{}".format(captured.out)
diff --git a/tests/test_icurl.py b/tests/test_icurl.py
index 68417a18..351e3cf5 100644
--- a/tests/test_icurl.py
+++ b/tests/test_icurl.py
@@ -3,6 +3,14 @@
script = importlib.import_module("aci-preupgrade-validation-script")
+
+# TimeoutError is only from py3.3
+try:
+ TimeoutError
+except NameError:
+ TimeoutError = script.TimeoutError
+
+
# icurl queries
fabricNodePEps = "fabricNodePEp.json"
@@ -134,6 +142,34 @@ def test_icurl(mock_icurl, apitype, query, expected_result):
],
script.OldVerClassNotFound,
),
+ # Query timeout (90 sec) - pre-4.1
+ (
+ [
+ {
+ "error": {
+ "attributes": {
+ "code": "503",
+ "text": "Unable to deliver the message, Resolve timeout from (type/num/svc/shard) = apic:1:7:1, apic:1:7:32, apic:1:7:31, apic:1:7:30, apic:1:7:13, apic:1:7:12, apic:1:7:11, apic:1:7:10, apic:1:7:9, apic:1:7:8, apic:1:7:7, apic:1:7:3, apic:1:7:14, apic:1:7:15, apic:1:7:16, apic:1:7:17, apic:1:7:18, apic:1:7:19, apic:1:7:20, apic:1:7:21, apic:1:7:22, apic:1:7:23, apic:1:7:24, apic:1:7:25, apic:1:7:26, apic:1:7:27, apic:1:7:28, apic:1:7:29",
+ }
+ }
+ }
+ ],
+ TimeoutError,
+ ),
+ # Query timeout (90 sec) - from-4.1
+ (
+ [
+ {
+ "error": {
+ "attributes": {
+ "code": "503",
+ "text": "Unable to deliver the message, Resolve timeout",
+ }
+ }
+ }
+ ],
+ TimeoutError,
+ ),
],
)
def test_icurl_error_handler(imdata, expected_exception):
diff --git a/tests/test_main.py b/tests/test_main.py
index 41c4543f..00f5413b 100644
--- a/tests/test_main.py
+++ b/tests/test_main.py
@@ -1,22 +1,158 @@
import pytest
import importlib
+import os
+import json
script = importlib.import_module("aci-preupgrade-validation-script")
-AciVersion = script.AciVersion
+AciResult = script.AciResult
+CheckManager = script.CheckManager
+# ----------------------------
+# Fixtures
+# ----------------------------
+@pytest.fixture
+def mock_query_common_data(monkeypatch, expected_common_data):
+ def _mock_query_common_data(api_only, args_cversion, args_tversion):
+ return expected_common_data
+
+ monkeypatch.setattr(script, "query_common_data", _mock_query_common_data)
+
+
+@pytest.fixture
+def expected_result_objects(result_objects_factory):
+ return (
+ result_objects_factory("pass")
+ + result_objects_factory("fail_full", script.FAIL_O)
+ + result_objects_factory("fail_simple", script.FAIL_UF)
+ + result_objects_factory("only_msg")
+ + result_objects_factory("pass")
+ + result_objects_factory("only_long_msg")
+ )
+
+
+@pytest.fixture
+def mock_CheckManager_get_check_funcs(monkeypatch, check_funcs_factory, expected_result_objects):
+ check_funcs = check_funcs_factory(expected_result_objects)
+
+ def _mock_CheckManager_get_check_funcs(self):
+ return check_funcs
+
+ monkeypatch.setattr(script.CheckManager, "get_check_funcs", _mock_CheckManager_get_check_funcs)
+
+
+# ----------------------------
+# Tests
+# ----------------------------
def test_args_version(capsys):
script.main(["--version"])
captured = capsys.readouterr()
- print(captured.out)
- assert "{}\n".format(script.SCRIPT_VERSION) == captured.out
+ assert "{}\n".format(script.SCRIPT_VERSION) == captured.out, "captured.out is =\n{}".format(captured.out)
@pytest.mark.parametrize("api_only", [False, True])
def test_args_total_checks(capsys, api_only):
args = ["--total-checks", "--api-only"] if api_only else ["--total-checks"]
- checks = script.get_checks(api_only=api_only, debug_function=None)
+
+ cm = CheckManager(api_only)
+ expected_output = "Total Number of Checks: {}\n".format(cm.total_checks)
+
script.main(args)
captured = capsys.readouterr()
- print(captured.out)
- assert "Total Number of Checks: {}\n".format(len(checks)) == captured.out
+ assert captured.out == expected_output, "captured.out is =\n{}".format(captured.out)
+
+
+def test_main(capsys, mock_query_common_data, mock_CheckManager_get_check_funcs, expected_result_objects):
+ script.main(["--no-cleanup"])
+
+ for idx, result_obj in enumerate(expected_result_objects):
+ check_id = "fake_{}_check".format(idx)
+ check_title = "Fake Check {}".format(idx)
+ expected_aci_result_obj = AciResult(check_id, check_title, result_obj)
+ expected_aci_result = expected_aci_result_obj.as_dict()
+ # Err msg from try/except in `check_wrapper()`
+ if result_obj.result == script.ERROR:
+ expected_aci_result["reason"] = "Unexpected Error: {}".format(expected_aci_result["reason"])
+
+ with open(os.path.join(script.JSON_DIR, check_id + ".json")) as f:
+ aci_result = json.load(f)
+ assert aci_result == expected_aci_result
+
+ captured = capsys.readouterr()
+ assert captured.out.startswith(
+ """\
+ ==== {ts}{tz}, Script Version {version} ====
+
+!!!! Check https://github.com/datacenter/ACI-Pre-Upgrade-Validation-Script for Latest Release !!!!
+
+Progress:""".format(
+ ts=script.ts,
+ tz=script.tz,
+ version=script.SCRIPT_VERSION,
+ )
+ ), "captured.out is =\n{}".format(captured.out)
+
+ assert captured.out.endswith(
+ """\
+11/11 checks completed\r
+
+
+=== Check Result (failed only) ===
+
+[Check 2/11] Fake Check 1... test msg FAIL - OUTAGE WARNING!!
+ H1 H2 H3
+ -- -- --
+ Data1 Data2 Data3
+ Data4 Data5 Data6
+ Loooooong Data7 Data8 Data9
+
+ Unformatted_H1
+ --------------
+ Data1
+ Data2
+
+ Recommended Action: This is your recommendation to remediate the issue
+ Reference Document: https://fake_doc_url.local/path1/#section1
+
+
+[Check 3/11] Fake Check 2... FAIL - UPGRADE FAILURE!!
+ H1 H2 H3
+ -- -- --
+ Data1 Data2 Data3
+ Data4 Data5 Data6
+ Loooooong Data7 Data8 Data9
+
+ Recommended Action: This is your recommendation to remediate the issue
+ Reference Document: https://fake_doc_url.local/path1/#section1
+
+
+[Check 6/11] Fake Check 5... test msg MANUAL CHECK REQUIRED
+[Check 7/11] Fake Check 6... test msg POST UPGRADE CHECK REQUIRED
+[Check 8/11] Fake Check 7... Unexpected Error: test msg ERROR !!
+[Check 11/11] Fake Check 10... Unexpected Error: long test msg xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ERROR !!
+
+=== Summary Result ===
+
+PASS : 3
+FAIL - OUTAGE WARNING!! : 1
+FAIL - UPGRADE FAILURE!! : 1
+MANUAL CHECK REQUIRED : 1
+POST UPGRADE CHECK REQUIRED : 1
+N/A : 2
+ERROR !! : 2
+TOTAL : 11
+
+ Pre-Upgrade Check Complete.
+ Next Steps: Address all checks flagged as FAIL, ERROR or MANUAL CHECK REQUIRED
+
+ Result output and debug info saved to below bundle for later reference.
+ Attach this bundle to Cisco TAC SRs opened to address the flagged checks.
+
+ Result Bundle: {bundle_loc}
+
+==== Script Version {version} FIN ====
+""".format(
+ version=script.SCRIPT_VERSION,
+ bundle_loc="/".join([os.getcwd(), script.BUNDLE_NAME]),
+ )
+ ), "captured.out is =\n{}".format(captured.out)
diff --git a/tests/test_prepare.py b/tests/test_prepare.py
deleted file mode 100644
index 5906bb4c..00000000
--- a/tests/test_prepare.py
+++ /dev/null
@@ -1,327 +0,0 @@
-import pytest
-import importlib
-import logging
-import json
-import os
-
-script = importlib.import_module("aci-preupgrade-validation-script")
-AciVersion = script.AciVersion
-AciResult = script.AciResult
-
-
-@pytest.fixture(autouse=True)
-def mock_get_credentials(monkeypatch):
- """Mock the get_credentials function to return a fixed username and password."""
-
- def _mock_get_credentials():
- return ("admin", "mypassword")
-
- monkeypatch.setattr(script, "get_credentials", _mock_get_credentials)
-
-
-@pytest.fixture(autouse=True)
-def mock_get_target_version(monkeypatch):
- """
- Mock `get_target_version()` to return a fixed target version.
- Used when the script is run without the `-t` option which is simulated by
- `arg_tversion`.
- Not using `mock_icurl` because this function involves a user interaction to
- select a version.
- """
-
- def _mock_get_target_version(arg_tversion):
- if arg_tversion:
- try:
- return AciVersion(arg_tversion)
- except ValueError as e:
- script.prints(e)
- raise SystemExit(1)
- return AciVersion("6.2(1a)")
-
- monkeypatch.setattr(script, "get_target_version", _mock_get_target_version)
-
-
-outputs = {
- "cversion": [
- {
- "firmwareCtrlrRunning": {
- "attributes": {
- "dn": "topology/pod-1/node-1/sys/ctrlrfwstatuscont/ctrlrrunning",
- "version": "6.1(1a)",
- }
- }
- }
- ],
- "switch_version": [
- {"firmwareRunning": {"attributes": {"peVer": "6.1(1a)", "version": "n9000-16.1(1a)"}}},
- {"firmwareRunning": {"attributes": {"peVer": "6.0(9d)", "version": "n9000-16.0(9d)"}}},
- ],
- "vpc_nodes": [
- {"fabricNodePEp": {"attributes": {"dn": "uni/fabric/protpol/expgep-101-102/nodepep-101", "id": "101"}}},
- {"fabricNodePEp": {"attributes": {"dn": "uni/fabric/protpol/expgep-101-102/nodepep-102", "id": "102"}}},
- ],
-}
-
-
-@pytest.mark.parametrize(
- "icurl_outputs, api_only, arg_tversion, arg_cversion, debug_function, expected_result",
- [
- # Default, no argparse arguments
- (
- {
- "firmwareCtrlrRunning.json": outputs["cversion"],
- "firmwareRunning.json": outputs["switch_version"],
- "fabricNodePEp.json": outputs["vpc_nodes"],
- },
- False,
- None,
- None,
- None,
- {"username": "admin", "password": "mypassword", "cversion": AciVersion("6.1(1a)"), "tversion": AciVersion("6.2(1a)"), "sw_cversion": AciVersion("6.0(9d)"), "vpc_node_ids": ["101", "102"]},
- ),
- # `api_only` is True (i.e. --puv)
- # No `get_credentials()`, no username nor password
- (
- {
- "firmwareCtrlrRunning.json": outputs["cversion"],
- "firmwareRunning.json": outputs["switch_version"],
- "fabricNodePEp.json": outputs["vpc_nodes"],
- },
- True,
- None,
- None,
- None,
- {"username": None, "password": None, "cversion": AciVersion("6.1(1a)"), "tversion": AciVersion("6.2(1a)"), "sw_cversion": AciVersion("6.0(9d)"), "vpc_node_ids": ["101", "102"]},
- ),
- # `arg_tversion` is provided (i.e. -t 6.1(4a))
- # The version `get_target_version()` is ignored.
- (
- {
- "firmwareCtrlrRunning.json": outputs["cversion"],
- "firmwareRunning.json": outputs["switch_version"],
- "fabricNodePEp.json": outputs["vpc_nodes"],
- },
- False,
- "6.1(4a)",
- None,
- None,
- {"username": "admin", "password": "mypassword", "cversion": AciVersion("6.1(1a)"), "tversion": AciVersion("6.1(4a)"), "sw_cversion": AciVersion("6.0(9d)"), "vpc_node_ids": ["101", "102"]},
- ),
- # `arg_tversion` and `arg_cversion` are both provided (i.e. -t 6.1(4a))
- # The version `get_target_version()` is ignored.
- (
- {
- "firmwareCtrlrRunning.json": outputs["cversion"],
- "firmwareRunning.json": outputs["switch_version"],
- "fabricNodePEp.json": outputs["vpc_nodes"],
- },
- False,
- "6.1(4a)",
- "6.0(8d)",
- None,
- {"username": "admin", "password": "mypassword", "cversion": AciVersion("6.0(8d)"), "tversion": AciVersion("6.1(4a)"), "sw_cversion": AciVersion("6.0(9d)"), "vpc_node_ids": ["101", "102"]},
- ),
- # `arg_tversion`, `arg_cversion` and 'debug_function' are all provided
- # The version `get_target_version()` is ignored.
- (
- {
- "firmwareCtrlrRunning.json": outputs["cversion"],
- "firmwareRunning.json": outputs["switch_version"],
- "fabricNodePEp.json": outputs["vpc_nodes"],
- },
- False,
- "6.1(4a)",
- "6.0(4d)",
- "ave_eol_check",
- {"username": "admin", "password": "mypassword", "cversion": AciVersion("6.0(4d)"), "tversion": AciVersion("6.1(4a)"), "sw_cversion": AciVersion("6.0(9d)"), "vpc_node_ids": ["101", "102"]},
- ),
- # versions are switch syntax
- # The version `get_target_version()` is ignored.
- (
- {
- "firmwareCtrlrRunning.json": outputs["cversion"],
- "firmwareRunning.json": outputs["switch_version"],
- "fabricNodePEp.json": outputs["vpc_nodes"],
- },
- False,
- "16.1(4a)",
- "16.0(4d)",
- "ave_eol_check",
- {"username": "admin", "password": "mypassword", "cversion": AciVersion("6.0(4d)"), "tversion": AciVersion("6.1(4a)"), "sw_cversion": AciVersion("6.0(9d)"), "vpc_node_ids": ["101", "102"]},
- ),
- # versions are switch or APIC syntax
- # The version `get_target_version()` is ignored.
- (
- {
- "firmwareCtrlrRunning.json": outputs["cversion"],
- "firmwareRunning.json": outputs["switch_version"],
- "fabricNodePEp.json": outputs["vpc_nodes"],
- },
- False,
- "n9000-16.2(1a).bin",
- "aci-apic-dk9.6.0.1a.bin",
- "ave_eol_check",
- {"username": "admin", "password": "mypassword", "cversion": AciVersion("6.0(1a)"), "tversion": AciVersion("6.2(1a)"), "sw_cversion": AciVersion("6.0(9d)"), "vpc_node_ids": ["101", "102"]},
- ),
- ],
-)
-def test_prepare(mock_icurl, api_only, arg_tversion, arg_cversion, debug_function, expected_result):
- script.initialize()
- checks = script.get_checks(api_only, debug_function)
- inputs = script.prepare(api_only, arg_tversion, arg_cversion, checks)
- for key, value in expected_result.items():
- if "version" in key: # cversion or tversion
- assert isinstance(inputs[key], AciVersion)
- assert str(inputs[key]) == str(value)
- else:
- assert inputs[key] == value
-
- result_files = os.listdir(script.JSON_DIR)
- # Result files should be created for all checks
- assert len(result_files) == len(checks)
- for check in checks:
- # Rule name is known only through the wrapper `check_wrapper`.
- # Rule name content should be checked via another unit test.
- # Use AciResult class here just to get the filename from `check.__name__`.
- ar = AciResult(check.__name__, "unknown_name", "")
- file_path = os.path.join(script.JSON_DIR, ar.filename)
- assert os.path.exists(file_path), "Missing result file: {}".format(file_path)
- with open(file_path, "r") as f:
- result = json.load(f)
- assert result["ruleId"] == check.__name__
- assert result["ruleStatus"] == AciResult.IN_PROGRESS
-
- with open(script.META_FILE, "r") as f:
- meta = json.load(f)
- assert meta["name"] == "PreupgradeCheck"
- assert meta["method"] == "standalone script"
- assert meta.get("datetime") is not None
- assert meta["script_version"] == script.SCRIPT_VERSION
- assert meta["cversion"] == str(expected_result["cversion"])
- assert meta["tversion"] == str(expected_result["tversion"])
- assert meta["sw_cversion"] == str(expected_result["sw_cversion"])
- assert meta["api_only"] == api_only
- assert meta["total_checks"] == len(checks)
- if debug_function:
- assert meta["total_checks"] == 1
-
-
-def test_tversion_invald():
- with pytest.raises(SystemExit):
- with pytest.raises(ValueError):
- script.prepare(False, "invalid_version", "6.0(1a)", [])
-
-
-def test_cversion_invald():
- with pytest.raises(SystemExit):
- with pytest.raises(ValueError):
- script.prepare(False, "6.0(1a)", "invalid_version", [])
-
-
-@pytest.mark.parametrize(
- "icurl_outputs, api_only, arg_tversion, arg_cversion, debug_function, expected_result",
- [
- # `get_cversion()` failure
- (
- {
- "firmwareCtrlrRunning.json": [{"error": {"attributes": {"code": "400", "text": "Request failed, unresolved class for firmwareCtrlrRunning_fake"}}}],
- "firmwareRunning.json": outputs["switch_version"],
- "fabricNodePEp.json": outputs["vpc_nodes"],
- },
- False,
- None,
- None,
- None,
- """\
-Checking current APIC version...
-
-Error: Your current ACI version does not have requested class
-Initial query failed. Ensure APICs are healthy. Ending script run.
-""",
- ),
- # `get_switch_version()` failure
- (
- {
- "firmwareCtrlrRunning.json": outputs["cversion"],
- "firmwareRunning.json": [{"error": {"attributes": {"code": "400", "text": "Request failed, unresolved class for firmwareRunning_fake"}}}],
- "fabricNodePEp.json": outputs["vpc_nodes"],
- },
- False,
- None,
- None,
- None,
- """\
-Gathering Lowest Switch Version from Firmware Repository...
-
-Error: Your current ACI version does not have requested class
-Initial query failed. Ensure APICs are healthy. Ending script run.
-""",
- ),
- # `get_vpc_nodes()` failure
- (
- {
- "firmwareCtrlrRunning.json": outputs["cversion"],
- "firmwareRunning.json": outputs["switch_version"],
- "fabricNodePEp.json": [{"error": {"attributes": {"code": "400", "text": "Request failed, unresolved class for fabricNodePEp_fake"}}}],
- },
- False,
- None,
- None,
- None,
- """\
-Collecting VPC Node IDs...
-
-Error: Your current ACI version does not have requested class
-Initial query failed. Ensure APICs are healthy. Ending script run.
-""",
- ),
- ],
-)
-def test_prepare_exception(capsys, caplog, mock_icurl, api_only, arg_tversion, arg_cversion, debug_function, expected_result):
- caplog.set_level(logging.CRITICAL)
- with pytest.raises(SystemExit):
- with pytest.raises(Exception):
- checks = script.get_checks(api_only, debug_function)
- script.prepare(api_only, arg_tversion, arg_cversion, checks)
- captured = capsys.readouterr()
- print(captured.out)
- assert captured.out.endswith(expected_result)
-
-
-# Unit test focusing only on the result file creation
-def test_prepare_initial_result_files(mock_icurl, icurl_outputs):
- # Provide required API outputs used inside prepare()
- icurl_outputs.update({
- "firmwareCtrlrRunning.json": outputs["cversion"],
- "firmwareRunning.json": outputs["switch_version"],
- "fabricNodePEp.json": outputs["vpc_nodes"],
- })
-
- # Create two simple checks with known titles
- @script.check_wrapper(check_title="Prepare Check A")
- def prep_check_a(**kwargs):
- return script.Result(result=script.PASS)
-
- @script.check_wrapper(check_title="Prepare Check B")
- def prep_check_b(**kwargs):
- return script.Result(result=script.PASS)
-
- checks = [prep_check_a, prep_check_b]
-
- # Run prepare which should only initialize result files
- script.prepare(api_only=False, arg_tversion=None, arg_cversion=None, checks=checks)
-
- # Verify result files and contents
- expected = {
- "prep_check_a": "Prepare Check A",
- "prep_check_b": "Prepare Check B",
- }
- for func_name, title in expected.items():
- ar = AciResult(func_name, title, "")
- file_path = os.path.join(script.JSON_DIR, ar.filename)
- assert os.path.exists(file_path), "Missing result file: {}".format(file_path)
- with open(file_path, "r") as f:
- data = json.load(f)
- assert data["ruleId"] == func_name
- assert data["name"] == title
- assert data["ruleStatus"] == AciResult.IN_PROGRESS
diff --git a/tests/test_run_checks.py b/tests/test_run_checks.py
deleted file mode 100644
index c8a440e2..00000000
--- a/tests/test_run_checks.py
+++ /dev/null
@@ -1,188 +0,0 @@
-import importlib
-import logging
-import json
-import os
-
-script = importlib.import_module("aci-preupgrade-validation-script")
-AciVersion = script.AciVersion
-JSON_DIR = script.JSON_DIR
-AciResult = script.AciResult
-Result = script.Result
-check_wrapper = script.check_wrapper
-
-
-# 120 = length of ` + --padding-- ` in `[Check XX/YY] ... --padding-- `
-ERROR_REASON = "This is a test exception to result in `script.ERROR`."
-ERROR_REASON_LONG = "This is a looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong test exception to result in `script.ERROR`." # > 120 char
-
-
-def check_builder(func_name, title, result, err_msg, others):
- @check_wrapper(check_title=title)
- def _check(**kwargs):
- _check.__name__ = func_name # Set the function name for the check
- if result == script.ERROR:
- raise Exception(err_msg)
- else:
- return Result(result=result, **others)
- return _check
-
-
-fake_data_full = {
- "msg": "test msg",
- "headers": ["H1", "H2", "H3"],
- "data": [["Data1", "Data2", "Data3"], ["Data4", "Data5", "Data6"], ["Loooooong Data7", "Data8", "Data9"]],
- "unformatted_headers": ["Unformatted_H1"],
- "unformatted_data": [["Data1"], ["Data2"]],
- "recommended_action": "This is your recommendation to remediate the issue",
- "doc_url": "https://fake_doc_url.local/path1/#section1",
-}
-
-fake_data_no_msg_no_unform = {
- "headers": ["H1", "H2", "H3"],
- "data": [["Data1", "Data2", "Data3"], ["Data4", "Data5", "Data6"], ["Loooooong Data7", "Data8", "Data9"]],
- "recommended_action": "This is your recommendation to remediate the issue",
- "doc_url": "https://fake_doc_url.local/path1/#section1",
-}
-
-fake_data_error = {
- "msg": "Error msg. This should not be printed",
-}
-
-fake_data_only_msg = {
- "msg": "test msg",
-}
-
-fake_data_only_long_msg = {
- "msg": "looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong test msg", # > 120 char
-}
-
-fake_checks_meta = [
- ("fake_check1", "Test Check 1", script.PASS, "", {}),
- ("fake_check2", "Test Check 2", script.FAIL_O, "", fake_data_full),
- ("fake_check3", "Test Check 3", script.FAIL_UF, "", fake_data_no_msg_no_unform),
- ("fake_check4", "Test Check 4", script.MANUAL, "", fake_data_only_msg),
- ("fake_check5", "Test Check 5", script.POST, "", fake_data_only_msg),
- ("fake_check6", "Test Check 6", script.NA, "", fake_data_only_msg),
- ("fake_check7", "Test Check 7", script.ERROR, ERROR_REASON, fake_data_error),
- ("fake_check8", "Test Check 8", script.PASS, "", fake_data_only_msg),
- ("fake_check9", "Test Check 9", script.ERROR, ERROR_REASON_LONG, fake_data_error),
- ("fake_check10", "Test Check 10", script.NA, "", fake_data_only_long_msg),
-]
-
-fake_checks = [
- check_builder(func_name, title, result, err_msg, others)
- for func_name, title, result, err_msg, others in fake_checks_meta
-]
-
-fake_result_filenames = [
- "{}.json".format(func_name) for func_name, _, _, _, _ in fake_checks_meta
-]
-
-fake_inputs = {
- "username": "admin",
- "password": "mypassword",
- "cversion": AciVersion("6.1(1a)"),
- "tversion": AciVersion("6.2(1a)"),
- "sw_cversion": AciVersion("6.1(1a)"),
- "vpc_node_ids": ["101", "102"],
-}
-
-
-def test_run_checks(capsys, caplog):
- caplog.set_level(logging.CRITICAL) # Skip logging.exceptions in pytest output as it is expected.
- script.run_checks(fake_checks, fake_inputs)
- captured = capsys.readouterr()
- print(captured.out)
- assert (
- captured.out
- == """\
-[Check 1/10] Test Check 1... PASS
-[Check 2/10] Test Check 2... test msg FAIL - OUTAGE WARNING!!
- H1 H2 H3
- -- -- --
- Data1 Data2 Data3
- Data4 Data5 Data6
- Loooooong Data7 Data8 Data9
-
- Unformatted_H1
- --------------
- Data1
- Data2
-
- Recommended Action: This is your recommendation to remediate the issue
- Reference Document: https://fake_doc_url.local/path1/#section1
-
-
-[Check 3/10] Test Check 3... FAIL - UPGRADE FAILURE!!
- H1 H2 H3
- -- -- --
- Data1 Data2 Data3
- Data4 Data5 Data6
- Loooooong Data7 Data8 Data9
-
- Recommended Action: This is your recommendation to remediate the issue
- Reference Document: https://fake_doc_url.local/path1/#section1
-
-
-[Check 4/10] Test Check 4... test msg MANUAL CHECK REQUIRED
-[Check 5/10] Test Check 5... test msg POST UPGRADE CHECK REQUIRED
-[Check 6/10] Test Check 6... test msg N/A
-[Check 7/10] Test Check 7... Unexpected Error: This is a test exception to result in `script.ERROR`. ERROR !!
-[Check 8/10] Test Check 8... test msg PASS
-[Check 9/10] Test Check 9... Unexpected Error: This is a looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong test exception to result in `script.ERROR`. ERROR !!
-[Check 10/10] Test Check 10... looooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong test msg N/A
-
-=== Summary Result ===
-
-PASS : 2
-FAIL - OUTAGE WARNING!! : 1
-FAIL - UPGRADE FAILURE!! : 1
-MANUAL CHECK REQUIRED : 1
-POST UPGRADE CHECK REQUIRED : 1
-N/A : 2
-ERROR !! : 2
-TOTAL : 10
-""" # noqa: W291
- )
-
- json_files = [f for f in os.listdir(JSON_DIR) if f in fake_result_filenames]
- assert json_files, "Result JSON file not created"
-
- for json_file in json_files:
- with open(os.path.join(JSON_DIR, json_file)) as f:
- data = json.load(f)
-
- for func_name, title, result, err_msg, others, in fake_checks_meta:
- if data["ruleId"] == func_name:
- assert data["name"] == title
- # reason
- if result == script.ERROR:
- assert data["reason"].endswith(err_msg)
- elif result not in [script.PASS, script.NA]:
- msg = others.get("msg", "See Failure Details")
- if others.get("unformatted_data"):
- msg += (
- "\n"
- "Parse failure occurred, the provided data may not be complete. "
- "Please contact Cisco TAC to identify the missing data."
- )
- assert data["reason"] == msg
- else:
- assert data["reason"] == others.get("msg", "")
- # failureDetails.failType
- if result not in [script.PASS, script.NA]:
- assert data["failureDetails"]["failType"] == result
- else:
- assert data["failureDetails"]["failType"] == ""
- # failureDetails.data
- assert data["failureDetails"]["data"] == AciResult.craftData(
- others.get("headers", []), others.get("data", [])
- )
- assert data["failureDetails"]["unformatted_data"] == AciResult.craftData(
- others.get("unformatted_headers", []), others.get("unformatted_data", [])
- )
- # other fields
- assert data["recommended_action"] == others.get("recommended_action", "")
- assert data["docUrl"] == others.get("doc_url", "")
- assert data["description"] == ""
- assert data["sub_reason"] == ""
diff --git a/tests/vpc_paired_switches_check/test_vpc_paired_switches_check.py b/tests/vpc_paired_switches_check/test_vpc_paired_switches_check.py
deleted file mode 100644
index 36191372..00000000
--- a/tests/vpc_paired_switches_check/test_vpc_paired_switches_check.py
+++ /dev/null
@@ -1,36 +0,0 @@
-import os
-import pytest
-import logging
-import importlib
-from helpers.utils import read_data
-
-script = importlib.import_module("aci-preupgrade-validation-script")
-
-log = logging.getLogger(__name__)
-dir = os.path.dirname(os.path.abspath(__file__))
-
-
-# icurl queries
-topSystems = "topSystem.json"
-
-
-@pytest.mark.parametrize(
- "icurl_outputs, vpc_node_ids, expected_result",
- [
- # all leaf switches are in vPC
- (
- {topSystems: read_data(dir, "topSystem.json")},
- ["101", "102", "103", "204", "206"],
- script.PASS,
- ),
- # not all leaf switches are in vPC
- (
- {topSystems: read_data(dir, "topSystem.json")},
- ["101", "103", "204", "206"],
- script.MANUAL,
- ),
- ],
-)
-def test_logic(mock_icurl, vpc_node_ids, expected_result):
- result = script.vpc_paired_switches_check(1, 1, vpc_node_ids=vpc_node_ids)
- assert result == expected_result
diff --git a/tests/vpc_paired_switches_check/topSystem.json b/tests/vpc_paired_switches_check/topSystem.json
deleted file mode 100644
index 39faf83a..00000000
--- a/tests/vpc_paired_switches_check/topSystem.json
+++ /dev/null
@@ -1,134 +0,0 @@
-[
- {
- "topSystem": {
- "attributes": {
- "dn": "topology/pod-1/node-1/sys",
- "id": "1",
- "name": "apic1",
- "podId": "1",
- "role": "controller"
- }
- }
- },
- {
- "topSystem": {
- "attributes": {
- "dn": "topology/pod-2/node-3/sys",
- "id": "3",
- "name": "apic3",
- "podId": "2",
- "role": "controller"
- }
- }
- },
- {
- "topSystem": {
- "attributes": {
- "dn": "topology/pod-1/node-2/sys",
- "id": "2",
- "name": "apic2",
- "podId": "1",
- "role": "controller"
- }
- }
- },
- {
- "topSystem": {
- "attributes": {
- "dn": "topology/pod-1/node-101/sys",
- "id": "101",
- "name": "leaf101",
- "podId": "1",
- "role": "leaf"
- }
- }
- },
- {
- "topSystem": {
- "attributes": {
- "dn": "topology/pod-1/node-102/sys",
- "id": "102",
- "name": "leaf102",
- "podId": "1",
- "role": "leaf"
- }
- }
- },
- {
- "topSystem": {
- "attributes": {
- "dn": "topology/pod-1/node-103/sys",
- "id": "103",
- "name": "leaf103",
- "podId": "1",
- "role": "leaf"
- }
- }
- },
- {
- "topSystem": {
- "attributes": {
- "dn": "topology/pod-2/node-204/sys",
- "id": "204",
- "name": "leaf204",
- "podId": "2",
- "role": "leaf"
- }
- }
- },
- {
- "topSystem": {
- "attributes": {
- "dn": "topology/pod-2/node-206/sys",
- "id": "206",
- "name": "leaf206",
- "podId": "2",
- "role": "leaf"
- }
- }
- },
- {
- "topSystem": {
- "attributes": {
- "dn": "topology/pod-2/node-2002/sys",
- "id": "2002",
- "name": "spine2002",
- "podId": "2",
- "role": "spine"
- }
- }
- },
- {
- "topSystem": {
- "attributes": {
- "dn": "topology/pod-2/node-2001/sys",
- "id": "2001",
- "name": "spine2001",
- "podId": "2",
- "role": "spine"
- }
- }
- },
- {
- "topSystem": {
- "attributes": {
- "dn": "topology/pod-1/node-1002/sys",
- "id": "1002",
- "name": "spine1002",
- "podId": "1",
- "role": "spine"
- }
- }
- },
- {
- "topSystem": {
- "attributes": {
- "dn": "topology/pod-1/node-1001/sys",
- "id": "1001",
- "name": "spine1001",
- "podId": "1",
- "role": "spine"
- }
- }
- }
-]