Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: jobs check enhancement #162

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 10 additions & 6 deletions docs/panos-upgrade-assurance/api/check_firewall.md
Original file line number Diff line number Diff line change
Expand Up @@ -529,23 +529,27 @@ __Returns__
* [`CheckStatus.ERROR`](/panos/docs/panos-upgrade-assurance/api/utils#class-checkstatus) when the `test_window` parameter
does not meet criteria.

### `CheckFirewall.check_non_finished_jobs`
### `CheckFirewall.check_non_matching_jobs`

```python
def check_non_finished_jobs() -> CheckResult
def check_non_matching_jobs(job_type: str = None,
job_status: str = "FIN",
job_result: str = None) -> CheckResult
```

Check for any job with status different than FIN.
Check for any job that does not match with the type, status and result set in the parameters (by default looks for
jobs with status different than FIN).

__Returns__


`CheckResult`: Object of [`CheckResult`](/panos/docs/panos-upgrade-assurance/api/utils#class-checkresult) class taking value of:

* [`CheckStatus.SUCCESS`](/panos/docs/panos-upgrade-assurance/api/utils#class-checkstatus) when all jobs are in FIN state.
* [`CheckStatus.SUCCESS`](/panos/docs/panos-upgrade-assurance/api/utils#class-checkstatus) when all jobs match the type,
status and result set in the parameters (by default, when all jobs are in FIN state).
* [`CheckStatus.FAIL`](/panos/docs/panos-upgrade-assurance/api/utils#class-checkstatus) otherwise, `CheckResult.reason`
field contains information about the 1<sup>st</sup> job found with status different than FIN (job ID and the actual
status).
field contains information about the 1<sup>st</sup> job found with type, status or result different than desired (job
ID and the actual value).
* [`CheckStatus.SKIPPED`](/panos/docs/panos-upgrade-assurance/api/utils#class-checkstatus) when there are no jobs on a
device.

Expand Down
6 changes: 3 additions & 3 deletions docs/panos-upgrade-assurance/configuration_details.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -762,12 +762,12 @@ checks_configuration:

### `jobs`

Verify if there are any running/pending jobs in the job queue. Any job with a status different than `FIN` will cause
the check to fail.
Verify if there are any running/pending jobs in the job queue. Any job with a type, status or result different than set in the
parameters (by default checks only whether the job status equals to `FIN`), will cause the check to fail.

Does not require configuration.

**Method**: [`CheckFirewall.check_non_finished_jobs()`](/panos/docs/panos-upgrade-assurance/api/check_firewall#checkfirewallcheck_non_finished_jobs)
**Method**: [`CheckFirewall.check_non_matching_jobs()`](/panos/docs/panos-upgrade-assurance/api/check_firewall#checkfirewallcheck_non_matching_jobs)

### `ntp_sync`

Expand Down
35 changes: 27 additions & 8 deletions panos_upgrade_assurance/check_firewall.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ def __init__(self, node: FirewallProxy, skip_force_locale: Optional[bool] = Fals
CheckType.MP_DP_CLOCK_SYNC: self.check_mp_dp_sync,
CheckType.CERTS: self.check_ssl_cert_requirements,
CheckType.UPDATES: self.check_scheduled_updates,
CheckType.JOBS: self.check_non_finished_jobs,
CheckType.JOBS: self.check_non_matching_jobs,
}

self._health_check_method_mapping = {
Expand Down Expand Up @@ -1048,30 +1048,49 @@ def check_scheduled_updates(self, test_window: int = 60) -> CheckResult:

return result

def check_non_finished_jobs(self) -> CheckResult:
"""Check for any job with status different than FIN.
def check_non_matching_jobs(self, job_type: str = None, job_status: str = "FIN", job_result: str = None) -> CheckResult:
"""Check for any job that does not match with the type, status and result set in the parameters (by default looks for
jobs with status different than FIN).

# Returns

CheckResult: Object of [`CheckResult`](/panos/docs/panos-upgrade-assurance/api/utils#class-checkresult) class taking \
value of:

* [`CheckStatus.SUCCESS`](/panos/docs/panos-upgrade-assurance/api/utils#class-checkstatus) when all jobs are in FIN state.
* [`CheckStatus.SUCCESS`](/panos/docs/panos-upgrade-assurance/api/utils#class-checkstatus) when all jobs match the type,
status and result set in the parameters (by default, when all jobs are in FIN state).
* [`CheckStatus.FAIL`](/panos/docs/panos-upgrade-assurance/api/utils#class-checkstatus) otherwise, `CheckResult.reason`
field contains information about the 1<sup>st</sup> job found with status different than FIN (job ID and the actual
status).
field contains information about the 1<sup>st</sup> job found with type, status or result different than desired (job
ID and the actual value).
* [`CheckStatus.SKIPPED`](/panos/docs/panos-upgrade-assurance/api/utils#class-checkstatus) when there are no jobs on a
device.

"""
result = CheckResult()

if job_type is None and job_status is None and job_result is None:
result.status = CheckStatus.SKIPPED
result.reason = "Neither 'job_type', 'job_status' nor 'job_result' parameters were set."
return result

all_jobs = self._node.get_jobs()

if all_jobs:
for jid, job in all_jobs.items():
if job["status"] != "FIN":
result.reason = f"At least one job (ID={jid}) is not in finished state (state={job['status']})."
if job_type and (job["type"] != job_type):
result.reason = (
f"At least one job (ID={jid}) does not have a desired type of {job_type} (type={job['type']})."
)
return result
elif job_status and (job["status"] != job_status):
result.reason = (
f"At least one job (ID={jid}) does not have a desired status of {job_status} (status={job['status']})."
)
return result
elif job_result and (job["result"] != job_result):
result.reason = (
f"At least one job (ID={jid}) does not have a desired result of {job_result} (result={job['result']})."
)
return result
result.status = CheckStatus.SUCCESS
return result
Expand Down
51 changes: 47 additions & 4 deletions tests/test_check_firewall.py
Original file line number Diff line number Diff line change
Expand Up @@ -1061,7 +1061,7 @@ def test_check_jobs_success(self, check_firewall_mock):

check_firewall_mock._node.get_jobs = lambda: jobs

assert check_firewall_mock.check_non_finished_jobs() == CheckResult(status=CheckStatus.SUCCESS)
assert check_firewall_mock.check_non_matching_jobs() == CheckResult(status=CheckStatus.SUCCESS)

def test_check_jobs_failure(self, check_firewall_mock):
jobs = {
Expand Down Expand Up @@ -1099,13 +1099,56 @@ def test_check_jobs_failure(self, check_firewall_mock):
},
}
check_firewall_mock._node.get_jobs = lambda: jobs
result = CheckResult(status=CheckStatus.FAIL, reason="At least one job (ID=4) is not in finished state (state=ACC).")
assert check_firewall_mock.check_non_finished_jobs() == result
result = CheckResult(
status=CheckStatus.FAIL, reason="At least one job (ID=4) does not have a desired status of FIN (status=ACC)."
)
assert check_firewall_mock.check_non_matching_jobs() == result

def test_check_jobs_custom(self, check_firewall_mock):
jobs = {
"4": {
"tenq": "2023/08/07 04:00:40",
"tdeq": "04:00:40",
"user": "Auto update agent",
"type": "WildFire",
"status": "ACC",
"queued": "NO",
"stoppable": "no",
"result": "OK",
"tfin": "2023/08/07 04:00:45",
"description": None,
"positionInQ": "0",
"progress": "2023/08/07 04:00:45",
"details": {"line": ["Configuration committed successfully", "Successfully committed last configuration"]},
"warnings": None,
},
"1": {
"tenq": "2023/08/07 03:59:57",
"tdeq": "03:59:57",
"user": None,
"type": "AutoCom",
"status": "FIN",
"queued": "NO",
"stoppable": "no",
"result": "OK",
"tfin": "2023/08/07 04:00:28",
"description": None,
"positionInQ": "0",
"progress": "100",
"details": {"line": ["Configuration committed successfully", "Successfully committed last configuration"]},
"warnings": None,
},
}
check_firewall_mock._node.get_jobs = lambda: jobs
result = CheckResult(
status=CheckStatus.FAIL, reason="At least one job (ID=4) does not have a desired type of AutoCom (type=WildFire)."
)
assert check_firewall_mock.check_non_matching_jobs(job_type="AutoCom", job_status="ACC") == result

def test_check_jobs_no_jobs(self, check_firewall_mock):
check_firewall_mock._node.get_jobs = lambda: {}
result = CheckResult(status=CheckStatus.SKIPPED, reason="No jobs found on device. This is unusual, please investigate.")
assert check_firewall_mock.check_non_finished_jobs() == result
assert check_firewall_mock.check_non_matching_jobs() == result

def test_run_readiness_checks(self, check_firewall_mock):
check_firewall_mock._check_method_mapping = {
Expand Down
Loading