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

The rule module has now a parameter rule_id #508

Merged
merged 27 commits into from
Feb 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
6f39205
The rule module has now a parameter rule_id that can be used to ident…
lgetwan Dec 20, 2023
134836a
Ongoing development.
lgetwan Jan 9, 2024
f230187
Now it's also possible to change existing rules based on a given rule…
lgetwan Jan 19, 2024
48f3429
The rule module has now a parameter rule_id that can be used to ident…
lgetwan Dec 20, 2023
059df3d
Ongoing development.
lgetwan Jan 9, 2024
f740143
Now it's also possible to change existing rules based on a given rule…
lgetwan Jan 19, 2024
496a3a0
Merge branch 'feature/module-rules-rule_id' of github.com:tribe29/ans…
lgetwan Jan 19, 2024
add9f81
Modifying existing rules is now idempotent, at least for some of the …
lgetwan Jan 19, 2024
1b51ae9
Typo in the rules lookup module integration test.
lgetwan Jan 19, 2024
d30d7a7
Added an integration test case for modifying existing rules.
lgetwan Jan 19, 2024
fb5d4ae
Sanity
lgetwan Jan 19, 2024
409ee8e
Typo in rule integration test.
lgetwan Jan 19, 2024
311885c
Copy & paste issue in rule integration test.
lgetwan Jan 19, 2024
24dc453
Debugged the integration test for the rules module.
lgetwan Jan 23, 2024
2c3a9c6
The rule module has now a parameter rule_id that can be used to ident…
lgetwan Dec 20, 2023
8f7d4a6
Ongoing development.
lgetwan Jan 9, 2024
b39fd6d
Now it's also possible to change existing rules based on a given rule…
lgetwan Jan 19, 2024
ed320f6
The rule module has now a parameter rule_id that can be used to ident…
lgetwan Dec 20, 2023
aad310f
Typo in the rules lookup module integration test.
lgetwan Jan 19, 2024
461e4b9
Added an integration test case for modifying existing rules.
lgetwan Jan 19, 2024
8407c41
Typo in rule integration test.
lgetwan Jan 19, 2024
15cfe63
Copy & paste issue in rule integration test.
lgetwan Jan 19, 2024
05b4ef8
Debugged the integration test for the rules module.
lgetwan Jan 23, 2024
30bf87a
Manually merge pull request #517 from meni2029/fix/module_rule_relati…
lgetwan Jan 23, 2024
7eea75b
Merging...
lgetwan Jan 23, 2024
7a060a4
Merge branch 'devel' of github.com:tribe29/ansible-collection-tribe29…
lgetwan Jan 24, 2024
2e43a1e
Merge branch 'devel' into feature/module-rules-rule_id
robin-checkmk Feb 5, 2024
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
126 changes: 95 additions & 31 deletions plugins/modules/rule.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,11 @@
properties:
description: Properties of the rule.
type: dict
rule_id:
description:
- If given, it will be C(the only condition) to identify the rule to work on.
- When there's no rule found with this id, the task will fail.
type: str
value_raw:
description: Rule values as exported from the web interface.
type: str
Expand Down Expand Up @@ -284,20 +289,20 @@ def get_rules_in_ruleset(module, base_url, headers, ruleset):
exit_failed(
module,
"Error calling API. HTTP code %d. Details: %s, "
% (info["status"], info["body"]),
% (info["status"], str(info)),
)

return json.loads(response.read().decode("utf-8"))
return json.loads(response.read().decode("utf-8")).get("value")


def get_rule_by_id(module, base_url, headers, rule_id):
api_endpoint = "/objects/rule/" + rule_id

url = base_url + api_endpoint
url = "%s%s" % (base_url, api_endpoint)

response, info = fetch_url(module, url, headers=headers, method="GET")

if info["status"] not in [200, 204]:
if info["status"] != 200:
exit_failed(
module,
"Error calling API. HTTP code %d. Details: %s, "
Expand All @@ -308,8 +313,15 @@ def get_rule_by_id(module, base_url, headers, rule_id):


def get_existing_rule(module, base_url, headers, ruleset, rule):
# Get rules in ruleset
rules = get_rules_in_ruleset(module, base_url, headers, ruleset)
if rule.get("rule_id"):
# We already know whih rule to get
if module.params.get("state") == "absent":
# When deleting and we already know the ID, don't compare
return rule.get("rule_id")
rules = [get_rule_by_id(module, base_url, headers, rule.get("rule_id"))]
else:
# Get rules in ruleset
rules = get_rules_in_ruleset(module, base_url, headers, ruleset)

(value_mod, exc) = safe_eval(rule["value_raw"], include_exceptions=True)
if exc is not None:
Expand All @@ -324,7 +336,7 @@ def get_existing_rule(module, base_url, headers, ruleset, rule):

if rules is not None:
# Loop through all rules
for r in rules.get("value"):
for r in rules:
(value_api, exc) = safe_eval(
r["extensions"]["value_raw"], include_exceptions=True
)
Expand All @@ -338,7 +350,7 @@ def get_existing_rule(module, base_url, headers, ruleset, rule):
and value_api == value_mod
):
# If they are the same, return the ID
return r
return r["id"]

return None

Expand All @@ -347,9 +359,9 @@ def create_rule(module, base_url, headers, ruleset, rule):
api_endpoint = "/domain-types/rule/collections/all"

changed = True
e = get_existing_rule(module, base_url, headers, ruleset, rule)
if e:
return (e["id"], not changed)
rule_id = get_existing_rule(module, base_url, headers, ruleset, rule)
if rule_id:
return (rule_id, not changed)

if module.check_mode:
return (None, changed)
Expand Down Expand Up @@ -380,12 +392,51 @@ def create_rule(module, base_url, headers, ruleset, rule):
return (r["id"], changed)


def modify_rule(module, base_url, headers, ruleset, rule):
changed = True
rule_id = rule.get("rule_id")

if not rule_id:
return not changed

if get_existing_rule(module, base_url, headers, ruleset, rule):
return not changed

if module.check_mode:
return (None, changed)

headers["If-Match"] = get_rule_etag(module, base_url, headers, rule_id)

params = {
"properties": rule["properties"],
"value_raw": rule["value_raw"],
"conditions": rule["conditions"],
}

api_endpoint = "/objects/rule/" + rule_id
url = base_url + api_endpoint

info = fetch_url(
module, url, module.jsonify(params), headers=headers, method="PUT"
)[1]

if info["status"] not in [200, 204]:
exit_failed(
module,
"Error calling API. HTTP code %d. Details: %s, "
% (info["status"], info["body"]),
)

return changed


def delete_rule(module, base_url, headers, ruleset, rule):
changed = True
e = get_existing_rule(module, base_url, headers, ruleset, rule)
if e:
rule_id = get_existing_rule(module, base_url, headers, ruleset, rule)

if rule_id:
if not module.check_mode:
delete_rule_by_id(module, base_url, headers, e["id"])
delete_rule_by_id(module, base_url, headers, rule_id)
return changed
return not changed

Expand Down Expand Up @@ -471,6 +522,7 @@ def run_module():
conditions=dict(type="dict"),
properties=dict(type="dict"),
value_raw=dict(type="str"),
rule_id=dict(type="str"),
location=dict(
type="dict",
options=dict(
Expand Down Expand Up @@ -519,23 +571,24 @@ def run_module():

# Get the variables
ruleset = module.params.get("ruleset", "")
rule = module.params.get("rule", "")
rule = module.params.get("rule", {})
location = rule.get("location")

# Check if required params to create a rule are given
if rule.get("folder") is None or rule.get("folder") == "":
if not rule.get("folder"):
rule["folder"] = location["folder"]
if rule.get("properties") is None or rule.get("properties") == "":
exit_failed(module, "Rule properties are required")
if rule.get("value_raw") is None or rule.get("value_raw") == "":
exit_failed(module, "Rule value_raw is required")
# Default to all hosts if conditions arent given
if rule.get("conditions") is None or rule.get("conditions") == "":
rule["conditions"] = {
"host_tags": [],
"host_labels": [],
"service_labels": [],
}
if not rule.get("rule_id"):
if not rule.get("properties"):
exit_failed(module, "Rule properties are required")
if not rule.get("value_raw"):
exit_failed(module, "Rule value_raw is required")
# Default to all hosts if conditions arent given
if rule.get("conditions"):
rule["conditions"] = {
"host_tags": [],
"host_labels": [],
"service_labels": [],
}
if module.params.get("state") == "absent":
if location.get("rule_id") is not None:
exit_failed(module, "rule_id in location is invalid with state=absent")
Expand All @@ -549,13 +602,24 @@ def run_module():
exit_ok(module, "Rule does not exist")
# If state is present, create the rule
elif module.params.get("state") == "present":
(rule_id, created) = create_rule(module, base_url, headers, ruleset, rule)
if created:
action = None
if rule.get("rule_id"):
# Modify an existing rule
rule_id = rule.get("rule_id")
if modify_rule(module, base_url, headers, ruleset, rule):
action = "changed"
else:
# If no rule_id is mentioned, we check if our rule exists. If not, then create it.
(rule_id, changed) = create_rule(module, base_url, headers, ruleset, rule)
if changed:
action = "created"

if action:
# Move rule to specified location, if it's not default
if location["position"] != "bottom" and not module.check_mode:
move_rule(module, base_url, headers, rule_id, location)
exit_changed(module, "Rule created", rule_id)
exit_ok(module, "Rule already exists", rule_id)
exit_changed(module, "Rule %s" % action, rule_id)
exit_ok(module, "Rule already exists with equal settings", rule_id)

# Fallback
exit_failed(module, "Unknown error")
Expand Down
2 changes: 1 addition & 1 deletion tests/integration/targets/lookup_rules/tasks/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@
vars:
rules: "{{ lookup('checkmk.general.rules',
ruleset=item,
commebt_regex='Ansible managed',
comment_regex='Ansible managed',
server_url=checkmk_var_server_url,
site=outer_item.site,
validate_certs=False,
Expand Down
44 changes: 44 additions & 0 deletions tests/integration/targets/rule/tasks/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,50 @@
delegate_to: localhost
run_once: true # noqa run-once[task]

- name: "{{ outer_item.version }} - {{ outer_item.edition | upper }} - Modify rules."
rule:
server_url: "{{ checkmk_var_server_url }}"
site: "{{ outer_item.site }}"
automation_user: "{{ checkmk_var_automation_user }}"
automation_secret: "{{ checkmk_var_automation_secret }}"
ruleset: "{{ item.ruleset }}"
rule:
rule_id: "{{ existing_rule[0].id }}"
properties: {
"comment": "{{ existing_rule[0].extensions.properties.comment }}",
"description": "Modified this intentionally.",
"disabled": "{{ existing_rule[0].extensions.properties.disabled }}"
}
conditions: "{{ existing_rule[0].extensions.conditions }}"
value_raw: "{{ existing_rule[0].extensions.value_raw | string }}"
state: "present"
when: "existing_rule|length>0"
vars:
existing_rule: "{{ lookup('checkmk.general.rules',
ruleset=item.ruleset,
comment_regex='Ansible managed',
server_url=checkmk_var_server_url,
site=outer_item.site,
validate_certs=False,
automation_user=checkmk_var_automation_user,
automation_secret=checkmk_var_automation_secret)
}}"
delegate_to: localhost
run_once: true # noqa run-once[task]
loop: "{{ checkmk_var_rules }}"

- name: "{{ outer_item.version }} - {{ outer_item.edition | upper }} - Activate."
activation:
server_url: "{{ checkmk_var_server_url }}"
site: "{{ outer_item.site }}"
automation_user: "{{ checkmk_var_automation_user }}"
automation_secret: "{{ checkmk_var_automation_secret }}"
force_foreign_changes: true
sites:
- "{{ outer_item.site }}"
delegate_to: localhost
run_once: true # noqa run-once[task]

- name: "{{ outer_item.version }} - {{ outer_item.edition | upper }} - Delete rules."
rule:
server_url: "{{ checkmk_var_server_url }}"
Expand Down
Loading