diff --git a/plugins/modules/zabbix_service.py b/plugins/modules/zabbix_service.py index 8a7b9b605..db15b5e27 100644 --- a/plugins/modules/zabbix_service.py +++ b/plugins/modules/zabbix_service.py @@ -287,13 +287,10 @@ class Service(ZabbixBase): - def get_service_ids(self, service_name): - service_ids = [] - services = self._zapi.service.get({"filter": {"name": service_name}}) - for service in services: - service_ids.append(service["serviceid"]) - return service_ids - + def get_service(self, service_name): + services = self._zapi.service.get({"selectParents": "extend", "selectChildren": "extend", "selectTags": "extend", "selectProblemTags": "extend", "selectStatusRules": "extend", "filter": {"name": service_name}}) + return services + def delete_service(self, service_ids): if self._module.check_mode: self._module.exit_json(changed=True) @@ -306,167 +303,157 @@ def dump_services(self, service_ids): return services - def generate_service_config(self, name, sortorder, weight, - algorithm, description, tags, problem_tags, parents, children, propagation_rule, propagation_value, status_rules): - algorithms = {"status_to_ok": "0", "most_crit_if_all_children": "1", "most_crit_of_child_serv": "2"} - algorithm = algorithms[algorithm] - - request = { - "name": name, - "algorithm": algorithm, - "sortorder": sortorder, - "description": description, - "weight": weight - } - - if tags: - request["tags"] = tags - else: - request["tags"] = [] - - request["problem_tags"] = [] - if problem_tags: - p_operators = {"equals": "0", "like": "2"} - for p_tag in problem_tags: - pt = {"tag": p_tag["tag"], "operator": "0", "value": ""} - if "operator" in p_tag: - pt["operator"] = p_operators[p_tag["operator"]] - if "value" in p_tag: - pt["value"] = p_tag["value"] - request["problem_tags"].append(pt) - - if parents: - p_service_ids = [] - p_services = self._zapi.service.get({"filter": {"name": parents}}) - for p_service in p_services: - p_service_ids.append({"serviceid": p_service["serviceid"]}) - request["parents"] = p_service_ids - else: - request["parents"] = [] - - if children: - c_service_ids = [] - c_services = self._zapi.service.get({"filter": {"name": children}}) - for c_service in c_services: - c_service_ids.append({"serviceid": c_service["serviceid"]}) - request["children"] = c_service_ids - else: - request["children"] = [] - - request["status_rules"] = [] - if status_rules: - for s_rule in status_rules: - status_rule = {} - if "type" in s_rule: - sr_type_map = {"at_least_n_child_services_have_status_or_above": "0", - "at_least_npct_child_services_have_status_or_above": "1", - "less_than_n_child_services_have_status_or_below": "2", - "less_than_npct_child_services_have_status_or_below": "3", - "weight_child_services_with_status_or_above_at_least_w": "4", - "weight_child_services_with_status_or_above_at_least_npct": "5", - "weight_child_services_with_status_or_below_less_w": "6", - "weight_child_services_with_status_or_below_less_npct": "7"} - if s_rule["type"] not in sr_type_map: - self._module.fail_json(msg="Wrong value for 'type' parameter in status rule.") - status_rule["type"] = sr_type_map[s_rule["type"]] - else: - self._module.fail_json(msg="'type' is mandatory paremeter for status rule.") - - if "limit_value" in s_rule: - lv = s_rule["limit_value"] - if status_rule["type"] in ["0", "2", "4", "6"]: - if int(lv) < 1 or int(lv) > 100000: - self._module.fail_json(msg="'limit_value' for N and W must be between 1 and 100000 but provided %s" % lv) - else: - if int(lv) < 1 or int(lv) > 100: - self._module.fail_json(msg="'limit_value' for N%% must be between 1 and 100 but provided %s" % lv) - status_rule["limit_value"] = str(lv) - else: - self._module.fail_json(msg="'limit_value' is mandatory paremeter for status rule.") - - if "limit_status" in s_rule: - sr_ls_map = {"ok": "-1", "not_classified": "0", "information": "1", "warning": "2", - "average": "3", "high": "4", "disaster": 5} - if s_rule["limit_status"] not in sr_ls_map: - self._module.fail_json(msg="Wrong value for 'limit_status' parameter in status rule.") - status_rule["limit_status"] = sr_ls_map[s_rule["limit_status"]] - else: - self._module.fail_json(msg="'limit_status' is mandatory paremeter for status rule.") - - if "new_status" in s_rule: - sr_ns_map = {"not_classified": "0", "information": "1", "warning": "2", - "average": "3", "high": "4", "disaster": "5"} - if s_rule["new_status"] not in sr_ns_map: - self._module.fail_json(msg="Wrong value for 'new_status' parameter in status rule.") - status_rule["new_status"] = sr_ns_map[s_rule["new_status"]] - else: - self._module.fail_json(msg="'new_status' is mandatory paremeter for status rule.") - - request["status_rules"].append(status_rule) - - request["propagation_rule"] = "0" - if propagation_rule: - if propagation_value is None: - self._module.fail_json(msg="If 'propagation_rule' is provided then 'propagation_value' must be provided too.") - pr_map = {"as_is": "0", "increase": "1", "decrease": "2", "ignore": "3", "fixed": "4"} - if propagation_rule not in pr_map: - self._module.fail_json(msg="Wrong value for 'propagation_rule' parameter.") + def construct_problem_tags(self, problem_tags): + p_operators = {"equals": "0", "like": "2"} + for problem_tag in problem_tags: + if problem_tag["operator"] in list(p_operators.keys()): + problem_tag["operator"] = p_operators[problem_tag["operator"]] else: - request["propagation_rule"] = pr_map[propagation_rule] - - request["propagation_value"] = "0" - if propagation_value: - if propagation_rule is None: - self._module.fail_json(msg="If 'propagation_value' is provided then 'propagation_rule' must be provided too.") - pv_map = {"ok": "-1", "not_classified": "0", "information": "1", "warning": "2", - "average": "3", "high": "4", "disaster": "5"} - if propagation_value not in pv_map: - self._module.fail_json(msg="Wrong value for 'propagation_value' parameter.") + problem_tag["operator"] = int(problem_tag["operator"]) + if not problem_tag["tag"]: + self._module.fail_json(msg='"tag" required in "problem_tags"') + return problem_tags + + def construct_status_rules(self, status_rules): + sr_type_map = {"at_least_n_child_services_have_status_or_above": "0", + "at_least_npct_child_services_have_status_or_above": "1", + "less_than_n_child_services_have_status_or_below": "2", + "less_than_npct_child_services_have_status_or_below": "3", + "weight_child_services_with_status_or_above_at_least_w": "4", + "weight_child_services_with_status_or_above_at_least_npct": "5", + "weight_child_services_with_status_or_below_less_w": "6", + "weight_child_services_with_status_or_below_less_npct": "7"} + for s_rule in status_rules: + if "type" in s_rule: + if s_rule["type"] not in sr_type_map: + self._module.fail_json(msg="Wrong value for 'type' parameter in status rule.") + s_rule["type"] = sr_type_map[s_rule["type"]] else: - request["propagation_value"] = pv_map[propagation_value] + self._module.fail_json(msg="'type' is mandatory paremeter for status rule.") - return request - - def create_service(self, name, sortorder, weight, algorithm, - description, tags, problem_tags, parents, children, propagation_rule, propagation_value, status_rules): - if self._module.check_mode: - self._module.exit_json(changed=True) + if "limit_value" in s_rule: + lv = s_rule["limit_value"] + if s_rule["type"] in ["0", "2", "4", "6"]: + if int(lv) < 1 or int(lv) > 100000: + self._module.fail_json(msg="'limit_value' for N and W must be between 1 and 100000 but provided %s" % lv) + else: + if int(lv) < 1 or int(lv) > 100: + self._module.fail_json(msg="'limit_value' for N%% must be between 1 and 100 but provided %s" % lv) + s_rule["limit_value"] = str(lv) + else: + self._module.fail_json(msg="'limit_value' is mandatory paremeter for status rule.") + + if "limit_status" in s_rule: + sr_ls_map = {"ok": "-1", "not_classified": "0", "information": "1", "warning": "2", + "average": "3", "high": "4", "disaster": 5} + if s_rule["limit_status"] not in sr_ls_map: + self._module.fail_json(msg="Wrong value for 'limit_status' parameter in status rule.") + s_rule["limit_status"] = sr_ls_map[s_rule["limit_status"]] + else: + self._module.fail_json(msg="'limit_status' is mandatory paremeter for status rule.") + + if "new_status" in s_rule: + sr_ns_map = {"not_classified": "0", "information": "1", "warning": "2", + "average": "3", "high": "4", "disaster": "5"} + if s_rule["new_status"] not in sr_ns_map: + self._module.fail_json(msg="Wrong value for 'new_status' parameter in status rule.") + s_rule["new_status"] = sr_ns_map[s_rule["new_status"]] + else: + self._module.fail_json(msg="'new_status' is mandatory paremeter for status rule.") - self._zapi.service.create(self.generate_service_config(name, sortorder, weight, - algorithm, description, tags, problem_tags, parents, children, propagation_rule, propagation_value, status_rules)) + return status_rules - def update_service(self, service_id, name, sortorder, weight, algorithm, + def create_service(self, service_name, sortorder, weight, algorithm, description, tags, problem_tags, parents, children, propagation_rule, propagation_value, status_rules): - generated_config = self.generate_service_config(name, sortorder, weight, algorithm, - description, tags, problem_tags, parents, children, propagation_rule, propagation_value, status_rules) - live_config = self.dump_services(service_id)[0] - - if len(live_config["parents"]) > 0: - # Need to rewrite parents list to only service ids - new_parents = [] - for parent in live_config["parents"]: - new_parents.append({"serviceid": parent["serviceid"]}) - live_config["parents"] = new_parents - - if len(live_config["children"]) > 0: - # Need to rewrite children list to only service ids - new_children = [] - for child in live_config["children"]: - new_children.append({"serviceid": child["serviceid"]}) - live_config["children"] = new_children - - change_parameters = {} - difference = zabbix_utils.helper_cleanup_data(zabbix_utils.helper_compare_dictionaries(generated_config, live_config, change_parameters)) - - if difference == {}: - self._module.exit_json(changed=False, msg="Service %s up to date" % name) - if self._module.check_mode: self._module.exit_json(changed=True) - generated_config["serviceid"] = service_id - self._zapi.service.update(generated_config) - self._module.exit_json(changed=True, msg="Service %s updated" % name) - + else: + try: + parameters = {"name": service_name} + if sortorder is not None: + parameters["sortorder"] = sortorder + if weight is not None: + parameters["weight"] = weight + if algorithm is not None: + parameters["algorithm"] = algorithm + if description is not None: + parameters["description"] = description + if tags is not None: + parameters["tags"] = tags + if problem_tags is not None: + parameters["problem_tags"] = problem_tags + if parents is not None: + parameters["parents"] = parents + if children is not None: + parameters["children"] = children + if propagation_rule is not None: + parameters["propagation_rule"] = propagation_rule + if propagation_value is not None: + parameters["propagation_value"] = propagation_value + if status_rules is not None: + parameters["status_rules"] = status_rules + + self._zapi.service.create(parameters) + self._module.exit_json(changed=True, msg="Service %s created" % service_name) + except Exception as e: + self._module.fail_json(msg="Failed creating service %s: %s" % (service_name, e)) + + def check_all_properties(self, name, sortorder, weight, algorithm, description, tags, problem_tags, parents, children, propagation_rule, propagation_value, status_rules, service_exist): + # raise Exception("%s ------ %s" % (parents, service_exist)) + if sortorder and sortorder != service_exist['sortorder']: + return True + if weight and weight != service_exist['weight']: + return True + if algorithm and algorithm != service_exist['algorithm']: + return True + if description and description != service_exist['description']: + return True + if tags and tags != service_exist['tags']: + return True + if problem_tags and problem_tags != service_exist['problem_tags']: + return True + if parents and parents != service_exist['parents']: + return True + if children and children != service_exist['children']: + return True + if propagation_rule and propagation_rule != service_exist['propagation_rule']: + return True + if propagation_value and propagation_value != service_exist['propagation_value']: + return True + if status_rules and status_rules != service_exist['status_rules']: + return True + return False + + def update_service(self, service_id, name, sortorder, weight, algorithm, description, tags, problem_tags, parents, children, propagation_rule, propagation_value, status_rules, service_name): + try: + parameters = {"serviceid": service_id} + if sortorder is not None: + parameters["sortorder"] = sortorder + if weight is not None: + parameters["weight"] = weight + if algorithm is not None: + parameters["algorithm"] = algorithm + if description is not None: + parameters["description"] = description + if tags is not None: + parameters["tags"] = tags + if problem_tags is not None: + parameters["problem_tags"] = problem_tags + if parents is not None: + parameters["parents"] = parents + if children is not None: + parameters["children"] = children + if propagation_rule is not None: + parameters["propagation_rule"] = propagation_rule + if propagation_value is not None: + parameters["propagation_value"] = propagation_value + if status_rules is not None: + parameters["status_rules"] = status_rules + + self._zapi.service.update(parameters) + self._module.exit_json(changed=True, msg="Service %s updated" % name) + except Exception as e: + self._module.fail_json(msg="Failed updating service %s: %s" % (service_name, e)) def main(): argument_spec = zabbix_utils.zabbix_common_argument_spec() @@ -519,7 +506,7 @@ def main(): ), parents=dict(type="list", required=False, elements="str"), children=dict(type="list", required=False, elements="str"), - propagation_rule=dict(default="as_is", type="str", required=False), + propagation_rule=dict(type="str", required=False), propagation_value=dict(type="str", required=False), status_rules=dict( type="list", @@ -547,7 +534,10 @@ def main(): )) module = AnsibleModule( argument_spec=argument_spec, - supports_check_mode=True + supports_check_mode=True, + required_together=[ + ('propagation_rule', 'propagation_value') + ] ) name = module.params["name"] @@ -566,26 +556,79 @@ def main(): # Load service module service = Service(module) - service_ids = service.get_service_ids(name) + service_exist = service.get_service(name) + service_id = "" + if service_exist: + service_id = service_exist[0]["serviceid"] + + algorithms = {"status_to_ok": "0", "most_crit_if_all_children": "1", "most_crit_of_child_serv": "2"} + algorithm = algorithms[algorithm] + + + if problem_tags: + problem_tags = service.construct_problem_tags(problem_tags) + + if parents: + p_service_ids = [] + p_services = service._zapi.service.get({"filter": {"name": parents}}) + for p_service in p_services: + p_service_ids.append({"serviceid": p_service["serviceid"]}) + parents = p_service_ids + + if children: + c_service_ids = [] + c_services = service._zapi.service.get({"filter": {"name": children}}) + for c_service in c_services: + c_service_ids.append({"serviceid": c_service["serviceid"]}) + children = c_service_ids + + if status_rules: + status_rules = service.construct_status_rules(status_rules) + + if propagation_rule: + if propagation_value is None: + module.fail_json(msg="If 'propagation_rule' is provided then 'propagation_value' must be provided too.") + pr_map = {"as_is": "0", "increase": "1", "decrease": "2", "ignore": "3", "fixed": "4"} + if propagation_rule not in pr_map: + module.fail_json(msg="Wrong value for 'propagation_rule' parameter.") + else: + propagation_rule = pr_map[propagation_rule] + + if propagation_value: + if propagation_rule is None: + module.fail_json(msg="If 'propagation_value' is provided then 'propagation_rule' must be provided too.") + pv_map = {"ok": "-1", "not_classified": "0", "information": "1", "warning": "2", + "average": "3", "high": "4", "disaster": "5"} + if propagation_value not in pv_map: + module.fail_json(msg="Wrong value for 'propagation_value' parameter.") + else: + propagation_value = pv_map[propagation_value] + # Delete service if state == "absent": - if not service_ids: + if not service_id: module.exit_json(changed=False, msg="Service not found, no change: %s" % name) - service.delete_service(service_ids) + service.delete_service(service_id) module.exit_json(changed=True, result="Successfully deleted service(s) %s" % name) elif state == "present": # Does not exists going to create it - if not service_ids: + if not service_id: service.create_service(name, sortorder, weight, algorithm, description, tags, problem_tags, parents, children, propagation_rule, propagation_value, status_rules) module.exit_json(changed=True, msg="Service %s created" % name) - # Else we update it if needed + # Else we update it if exists else: - service.update_service(service_ids[0], name, sortorder, weight, - algorithm, description, tags, problem_tags, parents, children, propagation_rule, propagation_value, status_rules) - + # Check if parameters have changed + if service.check_all_properties(name, sortorder, weight, algorithm, description, + tags, problem_tags, parents, children, propagation_rule, propagation_value, status_rules, service_exist[0]): + # Update service if a parameter is different + service.update_service(service_id, name, sortorder, weight, + algorithm, description, tags, problem_tags, parents, children, propagation_rule, propagation_value, status_rules, name) + else: + # No parameters changed, no update required. + module.exit_json(changed=False) if __name__ == "__main__": - main() + main() \ No newline at end of file