Skip to content

Commit

Permalink
Merge pull request Checkmk#508 from Checkmk/feature/module-rules-rule_id
Browse files Browse the repository at this point in the history
The rule module has now a parameter rule_id
  • Loading branch information
robin-checkmk authored Feb 16, 2024
2 parents c89a83d + 2e43a1e commit 2557900
Show file tree
Hide file tree
Showing 3 changed files with 140 additions and 32 deletions.
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

0 comments on commit 2557900

Please sign in to comment.