2020-10-28 20:39:20 +00:00
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
# Copyright 2020 Red Hat
|
|
|
|
# GNU General Public License v3.0+
|
|
|
|
# (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
|
|
|
|
|
|
|
|
from __future__ import absolute_import, division, print_function
|
|
|
|
|
2022-05-26 17:18:57 +00:00
|
|
|
|
2020-10-28 20:39:20 +00:00
|
|
|
__metaclass__ = type
|
|
|
|
|
|
|
|
import unittest
|
2022-05-26 17:18:57 +00:00
|
|
|
|
|
|
|
from ansible.errors import AnsibleActionFail
|
2020-10-28 20:39:20 +00:00
|
|
|
from ansible.playbook.task import Task
|
|
|
|
from ansible.template import Templar
|
|
|
|
|
2022-05-26 17:18:57 +00:00
|
|
|
from ansible_collections.ansible.utils.plugins.action.validate import ActionModule
|
|
|
|
|
2020-10-28 20:39:20 +00:00
|
|
|
|
2020-10-30 21:36:30 +00:00
|
|
|
try:
|
|
|
|
from unittest.mock import MagicMock # pylint:disable=syntax-error
|
|
|
|
except ImportError:
|
2022-04-13 13:01:59 +00:00
|
|
|
from mock import MagicMock # pyright: ignore[reportMissingModuleSource]
|
2020-10-30 21:36:30 +00:00
|
|
|
|
|
|
|
|
2020-10-28 20:39:20 +00:00
|
|
|
DATA = {
|
2020-10-30 21:36:30 +00:00
|
|
|
"GigabitEthernet0/0/0/0": {
|
|
|
|
"auto_negotiate": False,
|
|
|
|
"counters": {
|
|
|
|
"in_crc_errors": 0,
|
|
|
|
"in_errors": 0,
|
|
|
|
"rate": {"in_rate": 0, "out_rate": 0},
|
|
|
|
},
|
|
|
|
"description": "configured using Ansible",
|
|
|
|
"duplex_mode": "full",
|
|
|
|
"enabled": True,
|
|
|
|
"line_protocol": "up",
|
|
|
|
"mtu": 1514,
|
|
|
|
"oper_status": "down",
|
|
|
|
"type": "GigabitEthernet",
|
2020-10-28 20:39:20 +00:00
|
|
|
},
|
2020-10-30 21:36:30 +00:00
|
|
|
"GigabitEthernet0/0/0/1": {
|
|
|
|
"auto_negotiate": False,
|
|
|
|
"counters": {
|
|
|
|
"in_crc_errors": 10,
|
|
|
|
"in_errors": 0,
|
|
|
|
"rate": {"in_rate": 0, "out_rate": 0},
|
|
|
|
},
|
|
|
|
"description": "# interface is configures with Ansible",
|
|
|
|
"duplex_mode": "full",
|
|
|
|
"enabled": False,
|
|
|
|
"line_protocol": "up",
|
|
|
|
"mtu": 1514,
|
|
|
|
"oper_status": "up",
|
|
|
|
"type": "GigabitEthernet",
|
2020-10-28 20:39:20 +00:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
CRITERIA_CRC_ERROR_CHECK = {
|
2020-10-30 21:36:30 +00:00
|
|
|
"type": "object",
|
2020-10-28 20:39:20 +00:00
|
|
|
"patternProperties": {
|
|
|
|
"^.*": {
|
|
|
|
"type": "object",
|
|
|
|
"properties": {
|
2022-05-26 17:18:57 +00:00
|
|
|
"counters": {"properties": {"in_crc_errors": {"type": "number", "maximum": 0}}}
|
2020-10-30 21:36:30 +00:00
|
|
|
},
|
2020-10-28 20:39:20 +00:00
|
|
|
}
|
2020-10-30 21:36:30 +00:00
|
|
|
},
|
2020-10-28 20:39:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
CRITERIA_ENABLED_CHECK = {
|
2020-10-30 21:36:30 +00:00
|
|
|
"type": "object",
|
2022-05-26 17:18:57 +00:00
|
|
|
"patternProperties": {"^.*": {"type": "object", "properties": {"enabled": {"enum": [True]}}}},
|
2020-10-28 20:39:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
CRITERIA_OPER_STATUS_UP_CHECK = {
|
2020-10-30 21:36:30 +00:00
|
|
|
"type": "object",
|
2020-10-28 20:39:20 +00:00
|
|
|
"patternProperties": {
|
|
|
|
"^.*": {
|
|
|
|
"type": "object",
|
2020-10-30 21:36:30 +00:00
|
|
|
"properties": {"oper_status": {"type": "string", "pattern": "up"}},
|
2020-10-28 20:39:20 +00:00
|
|
|
}
|
2020-10-30 21:36:30 +00:00
|
|
|
},
|
2020-10-28 20:39:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
CRITERIA_IN_RATE_CHECK = {
|
|
|
|
"type": "object",
|
|
|
|
"patternProperties": {
|
|
|
|
"^.*": {
|
|
|
|
"type": "object",
|
|
|
|
"properties": {
|
|
|
|
"counters": {
|
|
|
|
"properties": {
|
2022-05-26 17:18:57 +00:00
|
|
|
"rate": {"properties": {"in_rate": {"type": "number", "maximum": 0}}}
|
2020-10-28 20:39:20 +00:00
|
|
|
}
|
|
|
|
}
|
2020-10-30 21:36:30 +00:00
|
|
|
},
|
2020-10-28 20:39:20 +00:00
|
|
|
}
|
2020-10-30 21:36:30 +00:00
|
|
|
},
|
2020-10-28 20:39:20 +00:00
|
|
|
}
|
|
|
|
|
2021-06-28 13:27:51 +00:00
|
|
|
VALID_DATA = {"name": "ansible", "email": "ansible@redhat.com"}
|
|
|
|
|
|
|
|
IN_VALID_DATA = {"name": "ansible", "email": "redhatcom"}
|
|
|
|
|
|
|
|
CRITERIA_FORMAT_SUPPORT_CHECK = {
|
|
|
|
"$schema": "https://json-schema.org/schema#",
|
|
|
|
"type": "object",
|
|
|
|
"properties": {"name": {"type": "string"}, "email": {"format": "email"}},
|
|
|
|
"required": ["email"],
|
|
|
|
}
|
|
|
|
|
2020-10-28 20:39:20 +00:00
|
|
|
|
|
|
|
class TestValidate(unittest.TestCase):
|
|
|
|
def setUp(self):
|
|
|
|
task = MagicMock(Task)
|
|
|
|
play_context = MagicMock()
|
|
|
|
play_context.check_mode = False
|
|
|
|
connection = MagicMock()
|
|
|
|
fake_loader = {}
|
|
|
|
templar = Templar(loader=fake_loader)
|
|
|
|
self._plugin = ActionModule(
|
|
|
|
task=task,
|
|
|
|
connection=connection,
|
|
|
|
play_context=play_context,
|
|
|
|
loader=fake_loader,
|
|
|
|
templar=templar,
|
|
|
|
shared_loader_obj=None,
|
|
|
|
)
|
|
|
|
self._plugin._task.action = "validate"
|
|
|
|
|
|
|
|
def test_invalid_argspec(self):
|
|
|
|
"""Check passing invalid argspec"""
|
|
|
|
|
|
|
|
# missing required arguments
|
|
|
|
self._plugin._task.args = {"engine": "ansible.utils.jsonschema"}
|
|
|
|
result = self._plugin.run(task_vars=None)
|
2021-08-03 08:04:33 +00:00
|
|
|
msg = "missing required arguments:"
|
|
|
|
if isinstance(result["errors"], list):
|
|
|
|
self.assertIn(msg, result["errors"][0])
|
|
|
|
else:
|
|
|
|
self.assertIn(msg, result["errors"])
|
2020-10-28 20:39:20 +00:00
|
|
|
|
|
|
|
# invalid engine option value
|
|
|
|
self._plugin._task.args = {
|
|
|
|
"engine": "ansible.utils.sample",
|
|
|
|
"data": DATA,
|
2020-10-30 21:36:30 +00:00
|
|
|
"criteria": CRITERIA_OPER_STATUS_UP_CHECK,
|
2020-10-28 20:39:20 +00:00
|
|
|
}
|
|
|
|
result = self._plugin.run(task_vars=None)
|
2020-10-30 21:36:30 +00:00
|
|
|
self.assertIn(
|
|
|
|
"For engine 'ansible.utils.sample' error loading the corresponding validate plugin",
|
|
|
|
result["msg"],
|
|
|
|
)
|
2020-10-28 20:39:20 +00:00
|
|
|
|
|
|
|
# invalid data option value
|
|
|
|
self._plugin._task.args = {
|
|
|
|
"engine": "ansible.utils.jsonschema",
|
|
|
|
"data": "invalid data",
|
2020-10-30 21:36:30 +00:00
|
|
|
"criteria": CRITERIA_OPER_STATUS_UP_CHECK,
|
2020-10-28 20:39:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
with self.assertRaises(AnsibleActionFail) as error:
|
|
|
|
self._plugin.run(task_vars=None)
|
2021-03-15 09:00:44 +00:00
|
|
|
self.assertIn("'data' option value is invalid", str(error.exception))
|
2020-10-28 20:39:20 +00:00
|
|
|
|
|
|
|
# invalid criteria option value
|
|
|
|
self._plugin._task.args = {
|
|
|
|
"engine": "ansible.utils.jsonschema",
|
|
|
|
"data": DATA,
|
2020-10-30 21:36:30 +00:00
|
|
|
"criteria": "invalid criteria",
|
2020-10-28 20:39:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
with self.assertRaises(AnsibleActionFail) as error:
|
|
|
|
self._plugin.run(task_vars=None)
|
2022-05-26 17:18:57 +00:00
|
|
|
self.assertIn("'criteria' option value is invalid", str(error.exception))
|
2020-10-28 20:39:20 +00:00
|
|
|
|
|
|
|
def test_invalid_validate_plugin_config_options(self):
|
|
|
|
"""Check passing invalid validate plugin options"""
|
|
|
|
|
|
|
|
self._plugin._task.args = {
|
|
|
|
"engine": "ansible.utils.jsonschema",
|
|
|
|
"data": DATA,
|
2020-10-30 21:36:30 +00:00
|
|
|
"criteria": CRITERIA_IN_RATE_CHECK,
|
2020-10-28 20:39:20 +00:00
|
|
|
}
|
|
|
|
|
2022-05-26 17:18:57 +00:00
|
|
|
result = self._plugin.run(task_vars={"ansible_validate_jsonschema_draft": "draft0"})
|
2020-10-30 21:36:30 +00:00
|
|
|
self.assertIn(
|
|
|
|
"value of draft must be one of: draft3, draft4, draft6, draft7, got: draft0",
|
|
|
|
result["msg"],
|
|
|
|
)
|
2020-10-28 20:39:20 +00:00
|
|
|
|
2021-06-28 13:27:51 +00:00
|
|
|
def test_validate_plugin_config_options_with_draft3(self):
|
|
|
|
"""Check passing invalid validate plugin options"""
|
|
|
|
|
|
|
|
self._plugin._task.args = {
|
|
|
|
"engine": "ansible.utils.jsonschema",
|
|
|
|
"data": DATA,
|
|
|
|
"criteria": CRITERIA_IN_RATE_CHECK,
|
|
|
|
}
|
|
|
|
|
2022-05-26 17:18:57 +00:00
|
|
|
result = self._plugin.run(task_vars={"ansible_validate_jsonschema_draft": "draft3"})
|
2022-01-28 00:02:36 +00:00
|
|
|
self.assertIn("All checks passed", result["msg"])
|
2021-06-28 13:27:51 +00:00
|
|
|
|
|
|
|
def test_validate_plugin_config_options_with_draft4(self):
|
|
|
|
"""Check passing invalid validate plugin options"""
|
|
|
|
|
|
|
|
self._plugin._task.args = {
|
|
|
|
"engine": "ansible.utils.jsonschema",
|
|
|
|
"data": DATA,
|
|
|
|
"criteria": CRITERIA_IN_RATE_CHECK,
|
|
|
|
}
|
|
|
|
|
2022-05-26 17:18:57 +00:00
|
|
|
result = self._plugin.run(task_vars={"ansible_validate_jsonschema_draft": "draft4"})
|
2022-01-28 00:02:36 +00:00
|
|
|
self.assertIn("All checks passed", result["msg"])
|
2021-06-28 13:27:51 +00:00
|
|
|
|
|
|
|
def test_validate_plugin_config_options_with_draft6(self):
|
|
|
|
"""Check passing invalid validate plugin options"""
|
|
|
|
|
|
|
|
self._plugin._task.args = {
|
|
|
|
"engine": "ansible.utils.jsonschema",
|
|
|
|
"data": DATA,
|
|
|
|
"criteria": CRITERIA_IN_RATE_CHECK,
|
|
|
|
}
|
|
|
|
|
2022-05-26 17:18:57 +00:00
|
|
|
result = self._plugin.run(task_vars={"ansible_validate_jsonschema_draft": "draft6"})
|
2022-01-28 00:02:36 +00:00
|
|
|
self.assertIn("All checks passed", result["msg"])
|
2021-06-28 13:27:51 +00:00
|
|
|
|
2020-10-28 20:39:20 +00:00
|
|
|
def test_invalid_data(self):
|
|
|
|
"""Check passing invalid data as per criteria"""
|
|
|
|
|
|
|
|
self._plugin._task.args = {
|
|
|
|
"engine": "ansible.utils.jsonschema",
|
|
|
|
"data": DATA,
|
2020-10-30 21:36:30 +00:00
|
|
|
"criteria": [
|
|
|
|
CRITERIA_CRC_ERROR_CHECK,
|
|
|
|
CRITERIA_ENABLED_CHECK,
|
|
|
|
CRITERIA_OPER_STATUS_UP_CHECK,
|
|
|
|
],
|
2020-10-28 20:39:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
result = self._plugin.run(task_vars=None)
|
2020-10-30 21:36:30 +00:00
|
|
|
self.assertIn(
|
|
|
|
"patternProperties.^.*.properties.counters.properties.in_crc_errors.maximum",
|
|
|
|
result["msg"],
|
|
|
|
)
|
2022-05-26 17:18:57 +00:00
|
|
|
self.assertIn("patternProperties.^.*.properties.enabled.enum", result["msg"])
|
2020-10-30 21:36:30 +00:00
|
|
|
self.assertIn(
|
|
|
|
"'patternProperties.^.*.properties.oper_status.pattern",
|
|
|
|
result["msg"],
|
|
|
|
)
|
2020-10-28 20:39:20 +00:00
|
|
|
|
|
|
|
def test_valid_data(self):
|
|
|
|
"""Check passing valid data as per criteria"""
|
|
|
|
|
|
|
|
self._plugin._task.args = {
|
|
|
|
"engine": "ansible.utils.jsonschema",
|
|
|
|
"data": DATA,
|
2020-10-30 21:36:30 +00:00
|
|
|
"criteria": CRITERIA_IN_RATE_CHECK,
|
2020-10-28 20:39:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
result = self._plugin.run(task_vars=None)
|
2022-01-28 00:02:36 +00:00
|
|
|
self.assertIn("All checks passed", result["msg"])
|
2021-06-28 13:27:51 +00:00
|
|
|
|
|
|
|
def test_support_for_format(self):
|
|
|
|
"""Check passing valid data as per criteria"""
|
|
|
|
|
|
|
|
self._plugin._task.args = {
|
|
|
|
"engine": "ansible.utils.jsonschema",
|
|
|
|
"data": VALID_DATA,
|
|
|
|
"criteria": CRITERIA_FORMAT_SUPPORT_CHECK,
|
|
|
|
}
|
|
|
|
|
|
|
|
result = self._plugin.run(task_vars=None)
|
2022-01-28 00:02:36 +00:00
|
|
|
self.assertIn("All checks passed", result["msg"])
|
2021-06-28 13:27:51 +00:00
|
|
|
|
|
|
|
def test_support_for_format_with_invalid_data(self):
|
|
|
|
"""Check passing valid data as per criteria"""
|
|
|
|
|
|
|
|
self._plugin._task.args = {
|
|
|
|
"engine": "ansible.utils.jsonschema",
|
|
|
|
"data": IN_VALID_DATA,
|
|
|
|
"criteria": CRITERIA_FORMAT_SUPPORT_CHECK,
|
|
|
|
}
|
|
|
|
|
|
|
|
result = self._plugin.run(task_vars=None)
|
|
|
|
self.assertIn("Validation errors were found", result["msg"])
|