diff options
Diffstat (limited to 'ansible_collections/community/zabbix/plugins')
44 files changed, 6840 insertions, 5789 deletions
diff --git a/ansible_collections/community/zabbix/plugins/doc_fragments/connection_persistent.py b/ansible_collections/community/zabbix/plugins/doc_fragments/connection_persistent.py deleted file mode 100644 index 6d9a82a8e..000000000 --- a/ansible_collections/community/zabbix/plugins/doc_fragments/connection_persistent.py +++ /dev/null @@ -1,59 +0,0 @@ -# -*- coding: utf-8 -*- -# 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 - -__metaclass__ = type - - -class ModuleDocFragment(object): - - # Standard files documentation fragment - DOCUMENTATION = r""" -options: - persistent_connect_timeout: - type: int - description: - - Configures, in seconds, the amount of time to wait when trying to initially - establish a persistent connection. If this value expires before the connection - to the remote device is completed, the connection will fail. - default: 30 - ini: - - section: persistent_connection - key: connect_timeout - env: - - name: ANSIBLE_PERSISTENT_CONNECT_TIMEOUT - vars: - - name: ansible_connect_timeout - persistent_command_timeout: - type: int - description: - - Configures, in seconds, the amount of time to wait for a command to - return from the remote device. If this timer is exceeded before the - command returns, the connection plugin will raise an exception and - close. - default: 30 - ini: - - section: persistent_connection - key: command_timeout - env: - - name: ANSIBLE_PERSISTENT_COMMAND_TIMEOUT - vars: - - name: ansible_command_timeout - persistent_log_messages: - type: boolean - description: - - This flag will enable logging the command executed and response received from - target device in the ansible log file. For this option to work 'log_path' ansible - configuration option is required to be set to a file path with write access. - - Be sure to fully understand the security implications of enabling this - option as it could create a security vulnerability by logging sensitive information in log file. - default: False - ini: - - section: persistent_connection - key: log_messages - env: - - name: ANSIBLE_PERSISTENT_LOG_MESSAGES - vars: - - name: ansible_persistent_log_messages -""" diff --git a/ansible_collections/community/zabbix/plugins/doc_fragments/zabbix.py b/ansible_collections/community/zabbix/plugins/doc_fragments/zabbix.py index 2cd64b00f..5cb7fcd54 100644 --- a/ansible_collections/community/zabbix/plugins/doc_fragments/zabbix.py +++ b/ansible_collections/community/zabbix/plugins/doc_fragments/zabbix.py @@ -7,31 +7,8 @@ class ModuleDocFragment(object): # Standard documentation fragment - DOCUMENTATION = r''' + DOCUMENTATION = r""" options: - server_url: - description: - - URL of Zabbix server, with protocol (http or https). - C(url) is an alias for C(server_url). - - If not set the environment variable C(ZABBIX_SERVER) will be used. - - This option is deprecated with the move to httpapi connection and will be removed in the next release - required: false - type: str - aliases: [ url ] - login_user: - description: - - Zabbix user name. - - If not set the environment variable C(ZABBIX_USERNAME) will be used. - - This option is deprecated with the move to httpapi connection and will be removed in the next release - type: str - required: false - login_password: - description: - - Zabbix user password. - - If not set the environment variable C(ZABBIX_PASSWORD) will be used. - - This option is deprecated with the move to httpapi connection and will be removed in the next release - type: str - required: false http_login_user: description: - Basic Auth login @@ -42,20 +19,4 @@ options: - Basic Auth password type: str required: false - timeout: - description: - - The timeout of API request (seconds). - - This option is deprecated with the move to httpapi connection and will be removed in the next release - - The default value is C(10) - type: int - validate_certs: - description: - - If set to False, SSL certificates will not be validated. This should only be used on personally controlled sites using self-signed certificates. - - If not set the environment variable C(ZABBIX_VALIDATE_CERTS) will be used. - - This option is deprecated with the move to httpapi connection and will be removed in the next release - - The default value is C(true) - type: bool -notes: - - If you use I(login_password=zabbix), the word "zabbix" is replaced by "********" in all module output, because I(login_password) uses C(no_log). - See L(this FAQ,https://docs.ansible.com/ansible/latest/network/user_guide/faq.html#why-is-my-output-sometimes-replaced-with) for more information. -''' + """ diff --git a/ansible_collections/community/zabbix/plugins/httpapi/zabbix.py b/ansible_collections/community/zabbix/plugins/httpapi/zabbix.py index 3db65532c..56ba0356d 100644 --- a/ansible_collections/community/zabbix/plugins/httpapi/zabbix.py +++ b/ansible_collections/community/zabbix/plugins/httpapi/zabbix.py @@ -165,12 +165,16 @@ class HttpApi(HttpApiBase): try: json_data = json.loads(value) if value else {} - if "result" in json_data: - json_data = json_data["result"] # JSONDecodeError only available on Python 3.5+ except ValueError: raise ConnectionError("Invalid JSON response: %s" % value) + if "error" in json_data: + raise ConnectionError("REST API returned %s when sending %s" % (json_data["error"], data)) + + if "result" in json_data: + json_data = json_data["result"] + try: # Some methods return bool not a dict in "result" iter(json_data) @@ -178,9 +182,6 @@ class HttpApi(HttpApiBase): # Do not try to find "error" if it is not a dict return response.getcode(), json_data - if "error" in json_data: - raise ConnectionError("REST API returned %s when sending %s" % (json_data["error"], data)) - return response.getcode(), json_data except AnsibleConnectionFailure as e: self.connection.queue_message("vvv", "AnsibleConnectionFailure: %s" % e) diff --git a/ansible_collections/community/zabbix/plugins/inventory/zabbix_inventory.py b/ansible_collections/community/zabbix/plugins/inventory/zabbix_inventory.py index 437d34227..8e4be416b 100644 --- a/ansible_collections/community/zabbix/plugins/inventory/zabbix_inventory.py +++ b/ansible_collections/community/zabbix/plugins/inventory/zabbix_inventory.py @@ -1,8 +1,9 @@ # # Copyright: (c), Ansible Project # -# (c) 2013, Greg Buehler +# (c) 2023, Alexandre Georges # (c) 2018, Filippo Ferrazini +# (c) 2013, Greg Buehler # (c) 2021, Timothy Test # Modified from ServiceNow Inventory Plugin and Zabbix inventory Script # GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt) @@ -10,7 +11,7 @@ from __future__ import absolute_import, division, print_function __metaclass__ = type -DOCUMENTATION = r''' +DOCUMENTATION = r""" name: zabbix_inventory author: - Timothy Test (@ttestscripting) @@ -20,8 +21,7 @@ description: - Zabbix Inventory plugin - All vars from zabbix are prefixed with zbx_ requirements: - - "python >= 2.6" - - "zabbix-api >= 0.5.4" + - "python >= 3.9" options: server_url: description: @@ -35,7 +35,7 @@ options: proxy: description: Proxy server to use for reaching zabbix API type: string - default: '' + default: "" host_zapi_query: description: - API query for hosts - see zabbix documentation for more details U(https://www.zabbix.com/documentation/current/manual/api/reference/host/get) @@ -47,9 +47,9 @@ options: description: - query - Return an applications property with host applications. - - To return all values specify 'extend' - - Can be limited to different fields for example setting the vaule to ['name'] will only return the name - - Additional fields can be specified by comma seperated value ['name', 'field2'] + - To return all values specify "extend" + - Can be limited to different fields for example setting the vaule to ["name"] will only return the name + - Additional fields can be specified by comma seperated value ["name", "field2"] - Please see U(https://www.zabbix.com/documentation/current/manual/api/reference/application/object) for more details on field names selectDiscoveries: type: str @@ -200,6 +200,12 @@ options: required: true env: - name: ZABBIX_PASSWORD + auth_token: + description: + - Zabbix authentication token (see https://www.zabbix.com/documentation/current/en/manual/web_interface/frontend_sections/users/api_tokens) + - If provided then C(login_user) and C(login_password) are ignored + type: str + required: false http_login_user: description: - Basic Auth login @@ -228,9 +234,9 @@ options: extends_documentation_fragment: - constructed - inventory_cache -''' +""" -EXAMPLES = r''' +EXAMPLES = r""" # Simple Inventory Plugin example # This will create an inventory with details from zabbix such as applications name, applicaitonids, Parent Template Name, and group membership name #It will also create 2 ansible inventory groups for enabled and disabled hosts in zabbix based on the status field. @@ -286,27 +292,34 @@ validate_certs: false compose: zbx_testvar: zbx_status.replace("1", "Disabled") +#Using auth token instead of username/password +plugin: community.zabbix.zabbix_inventory +server_url: https://zabbix.com +auth_token: 3bc3dc85e13e2431812e7a32fa8341cbcf378e5101356c015fdf2e35fd511b06 +validate_certs: false -''' +""" from ansible.plugins.inventory import BaseInventoryPlugin, Constructable, Cacheable, to_safe_group_name import os import atexit -import traceback - -try: - from zabbix_api import ZabbixAPI - HAS_ZABBIX_API = True -except ImportError: - ZBX_IMP_ERR = traceback.format_exc() - HAS_ZABBIX_API = False +import json +from ansible.module_utils.urls import Request +from ansible.module_utils.six.moves.urllib.error import URLError, HTTPError +from ansible.module_utils.compat.version import LooseVersion +from ansible.errors import AnsibleParserError class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable): NAME = 'community.zabbix.zabbix_inventory' - def login_zabbix(self): + def __init__(self): + super().__init__() + self.auth = '' + self.zabbix_verion = '' + + def api_request(self, method, params=None): # set proxy information if required proxy = self.get_option('proxy') os.environ['http_proxy'] = proxy @@ -315,24 +328,74 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable): os.environ['HTTPS_PROXY'] = proxy server_url = self.get_option('server_url') - http_login_user = self.get_option('login_user') - http_login_password = self.get_option('login_password') validate_certs = self.get_option('validate_certs') timeout = self.get_option('timeout') - self._zapi = ZabbixAPI(server_url, timeout=timeout, user=http_login_user, passwd=http_login_password, validate_certs=validate_certs) - self.login() - self._zbx_api_version = self._zapi.api_version()[:5] - - def login(self): - # check if api already logged in - if not self._zapi.auth != '': - try: - login_user = self.get_option('login_user') - login_password = self.get_option('login_password') - self._zapi.login(login_user, login_password) - atexit.register(self._zapi.logout) - except Exception as e: - self.display.vvv(msg="Failed to connect to Zabbix server: %s" % e) + + headers = {'Content-Type': 'application/json-rpc'} + payload = { + 'jsonrpc': '2.0', + 'method': method, + 'id': '1' + } + if params is None: + payload['params'] = {} + else: + payload['params'] = params + + if self.auth != '': + if (LooseVersion(self.zabbix_version) >= LooseVersion('6.4')): + headers['Authorization'] = 'Bearer ' + self.auth + else: + payload['auth'] = self.auth + + api_url = server_url + '/api_jsonrpc.php' + req = Request( + headers=headers, + timeout=timeout, + validate_certs=validate_certs + ) + try: + self.display.vvv("Sending request to {0}".format(api_url)) + response = req.post(api_url, data=json.dumps(payload)) + except ValueError: + raise AnsibleParserError("something went wrong with JSON loading") + except (URLError, HTTPError) as error: + raise AnsibleParserError(error) + + return response + + def get_version(self): + response = self.api_request( + 'apiinfo.version' + ) + res = json.load(response) + self.zabbix_version = res['result'] + + def logout_zabbix(self): + self.api_request( + 'user.logout', + [] + ) + + def login_zabbix(self): + auth_token = self.get_option('auth_token') + if auth_token: + self.auth = auth_token + return + + atexit.register(self.logout_zabbix) + + login_user = self.get_option('login_user') + login_password = self.get_option('login_password') + response = self.api_request( + 'user.login', + { + "username": login_user, + "password": login_password + } + ) + res = json.load(response) + self.auth = res["result"] def verify_file(self, path): valid = False @@ -354,9 +417,15 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable): self.use_cache = self.get_option('cache') and cache self.update_cache = self.get_option('cache') and not cache + self.get_version() self.login_zabbix() zapi_query = self.get_option('host_zapi_query') - content = self._zapi.host.get(zapi_query) + response = self.api_request( + 'host.get', + zapi_query + ) + res = json.load(response) + content = res['result'] strict = self.get_option('strict') @@ -377,7 +446,16 @@ class InventoryModule(BaseInventoryPlugin, Constructable, Cacheable): # organize inventory by zabbix groups if self.get_option('add_zabbix_groups'): - content = self._zapi.host.get({'selectGroups': ['name']}) + + response = self.api_request( + 'host.get', + { + 'selectGroups': ['name'] + } + ) + res = json.load(response) + content = res['result'] + for record in content: host_name = record['host'] if len(record['groups']) >= 1: diff --git a/ansible_collections/community/zabbix/plugins/module_utils/api_request.py b/ansible_collections/community/zabbix/plugins/module_utils/api_request.py index a29f492de..18b588e17 100644 --- a/ansible_collections/community/zabbix/plugins/module_utils/api_request.py +++ b/ansible_collections/community/zabbix/plugins/module_utils/api_request.py @@ -12,7 +12,7 @@ __metaclass__ = type from uuid import uuid4 -from ansible.module_utils.urls import CertificateError +from ssl import CertificateError from ansible.module_utils.connection import ConnectionError from ansible.module_utils.connection import Connection from ansible.module_utils._text import to_text diff --git a/ansible_collections/community/zabbix/plugins/module_utils/base.py b/ansible_collections/community/zabbix/plugins/module_utils/base.py index 8858a02e1..a1c73291e 100644 --- a/ansible_collections/community/zabbix/plugins/module_utils/base.py +++ b/ansible_collections/community/zabbix/plugins/module_utils/base.py @@ -7,7 +7,6 @@ from __future__ import absolute_import, division, print_function __metaclass__ = type -from ansible_collections.community.zabbix.plugins.module_utils.wrappers import ZapiWrapper from ansible_collections.community.zabbix.plugins.module_utils.api_request import ZabbixApiRequest @@ -17,17 +16,5 @@ class ZabbixBase(object): """ def __init__(self, module, zbx=None, zapi_wrapper=None): self._module = module - - if module._socket_path is None: - # ansible_connection = local - if zapi_wrapper is None: - self._zapi_wrapper = ZapiWrapper(module, zbx) - else: - self._zapi_wrapper = zapi_wrapper - - self._zapi = self._zapi_wrapper._zapi - self._zbx_api_version = self._zapi_wrapper._zbx_api_version - else: - # ansible_connection = httpapi - self._zapi = ZabbixApiRequest(module) - self._zbx_api_version = self._zapi.api_version() + self._zapi = ZabbixApiRequest(module) + self._zbx_api_version = self._zapi.api_version() diff --git a/ansible_collections/community/zabbix/plugins/module_utils/helpers.py b/ansible_collections/community/zabbix/plugins/module_utils/helpers.py index 6c9c0fca5..87e0c0925 100644 --- a/ansible_collections/community/zabbix/plugins/module_utils/helpers.py +++ b/ansible_collections/community/zabbix/plugins/module_utils/helpers.py @@ -7,15 +7,6 @@ from __future__ import absolute_import, division, print_function __metaclass__ = type -from ansible.module_utils.basic import env_fallback - - -def require_creds_params(module): - if module._socket_path is None: - # ansible_connection = local - if ((not module.params.get('server_url', None)) or (not module.params.get('login_user', None)) or (not module.params.get('login_password', None))): - module.fail_json(msg="server_url, login_user, login_password are mandatory parameters when httpapi connection is not used") - def zabbix_common_argument_spec(): """ @@ -23,22 +14,6 @@ def zabbix_common_argument_spec(): The options are commonly used by most of Zabbix modules. """ return dict( - server_url=dict( - type='str', - required=False, - aliases=['url'], - fallback=(env_fallback, ['ZABBIX_SERVER']) - ), - login_user=dict( - type='str', required=False, - fallback=(env_fallback, ['ZABBIX_USERNAME']) - ), - login_password=dict( - type='str', - required=False, - no_log=True, - fallback=(env_fallback, ['ZABBIX_PASSWORD']) - ), http_login_user=dict( type='str', required=False, @@ -49,15 +24,7 @@ def zabbix_common_argument_spec(): required=False, default=None, no_log=True - ), - timeout=dict( - type='int' - ), - validate_certs=dict( - type='bool', - required=False, - fallback=(env_fallback, ['ZABBIX_VALIDATE_CERTS']) - ), + ) ) diff --git a/ansible_collections/community/zabbix/plugins/module_utils/wrappers.py b/ansible_collections/community/zabbix/plugins/module_utils/wrappers.py deleted file mode 100644 index a982108f8..000000000 --- a/ansible_collections/community/zabbix/plugins/module_utils/wrappers.py +++ /dev/null @@ -1,84 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -# 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 -__metaclass__ = type - -import atexit -import traceback - -from ansible.module_utils.basic import missing_required_lib - -try: - from zabbix_api import ZabbixAPI, Already_Exists, ZabbixAPIException - - HAS_ZABBIX_API = True - ZBX_IMP_ERR = Exception() -except ImportError: - ZBX_IMP_ERR = traceback.format_exc() - HAS_ZABBIX_API = False - - -class ZapiWrapper(object): - """ - A simple wrapper over the Zabbix API - """ - def __init__(self, module, zbx=None): - self._module = module - - if not HAS_ZABBIX_API: - module.fail_json(msg=missing_required_lib('zabbix-api', url='https://pypi.org/project/zabbix-api/'), exception=ZBX_IMP_ERR) - - # check if zbx is already instantiated or not - if zbx is not None and isinstance(zbx, ZabbixAPI): - self._zapi = zbx - else: - server_url = module.params['server_url'] - - if module.params['validate_certs'] is None: - validate_certs = True - else: - validate_certs = module.params['validate_certs'] - - if module.params['timeout'] is None: - timeout = 10 - else: - timeout = module.params['timeout'] - - self._zapi = ZabbixAPI(server_url, timeout=timeout, validate_certs=validate_certs) - - self.login() - - self._zbx_api_version = self._zapi.api_version() - - def login(self): - # check if api already logged in - if not self._zapi.auth != '': - try: - login_user = self._module.params['login_user'] - login_password = self._module.params['login_password'] - self._zapi.login(login_user, login_password) - atexit.register(self._zapi.logout) - except Exception as e: - self._module.fail_json(msg="Failed to connect to Zabbix server: %s" % e) - - -class ScreenItem(object): - @staticmethod - def create(zapi_wrapper, data, ignoreExists=False): - try: - zapi_wrapper._zapi.screenitem.create(data) - except Already_Exists as ex: - if not ignoreExists: - raise ex - - @staticmethod - def delete(zapi_wrapper, id_list=None): - try: - if id_list is None: - id_list = [] - zapi_wrapper._zapi.screenitem.delete(id_list) - except ZabbixAPIException as ex: - raise ex diff --git a/ansible_collections/community/zabbix/plugins/modules/zabbix_action.py b/ansible_collections/community/zabbix/plugins/modules/zabbix_action.py index 8e130de69..c0123ae1f 100644 --- a/ansible_collections/community/zabbix/plugins/modules/zabbix_action.py +++ b/ansible_collections/community/zabbix/plugins/modules/zabbix_action.py @@ -8,7 +8,7 @@ from __future__ import absolute_import, division, print_function __metaclass__ = type -DOCUMENTATION = ''' +DOCUMENTATION = """ --- module: zabbix_action @@ -23,41 +23,51 @@ author: - Ruben Harutyunov (@K-DOT) requirements: - - "python >= 2.6" + - "python >= 3.9" options: name: + type: str description: - Name of the action required: true event_source: + type: str description: - Type of events that the action will handle. - Required when C(state=present). required: false - choices: ['trigger', 'discovery', 'auto_registration', 'internal'] + choices: ["trigger", "discovery", "auto_registration", "internal"] state: + type: str description: - State of the action. - On C(present), it will create an action if it does not exist or update the action if the associated data is different. - On C(absent), it will remove the action if it exists. - choices: ['present', 'absent'] - default: 'present' + choices: ["present", "absent"] + default: "present" status: + type: str description: - Status of the action. - choices: ['enabled', 'disabled'] - default: 'enabled' + choices: ["enabled", "disabled"] + default: "enabled" pause_in_maintenance: description: - Whether to pause escalation during maintenance periods or not. - Can be used when I(event_source=trigger). - type: 'bool' + type: "bool" + default: true + notify_if_canceled: + description: + - Weather to notify when escalation is canceled. + - Can be used when I(event_source=trigger). + type: "bool" default: true esc_period: + type: str description: - Default operation step duration. Must be greater than 60 seconds. - - Accepts only seconds in int for <= Zabbix 3.2 - Accepts seconds, time unit with suffix and user macro since => Zabbix 3.4 - Required when C(state=present). required: false @@ -68,112 +78,123 @@ options: - List of conditions to use for filtering results. - For more information about suboptions of this option please check out Zabbix API documentation U(https://www.zabbix.com/documentation/5.0/manual/api/reference/action/object#action_filter_condition) + default: [] suboptions: type: + type: str description: - Type (label) of the condition. - - C(application) is available only with <= Zabbix 5.2. - - 'Possible values when I(event_source=trigger):' - - ' - C(host_group)' - - ' - C(host)' - - ' - C(trigger)' - - ' - C(trigger_name)' - - ' - C(trigger_severity)' - - ' - C(time_period)' - - ' - C(host_template)' - - ' - C(application)' - - ' - C(maintenance_status) known in Zabbix 4.0 and above as "Problem is suppressed"' - - ' - C(event_tag)' - - ' - C(event_tag_value)' - - 'Possible values when I(event_source=discovery):' - - ' - C(host_IP)' - - ' - C(discovered_service_type)' - - ' - C(discovered_service_port)' - - ' - C(discovery_status)' - - ' - C(uptime_or_downtime_duration)' - - ' - C(received_value)' - - ' - C(discovery_rule)' - - ' - C(discovery_check)' - - ' - C(proxy)' - - ' - C(discovery_object)' - - 'Possible values when I(event_source=auto_registration):' - - ' - C(proxy)' - - ' - C(host_name)' - - ' - C(host_metadata)' - - 'Possible values when I(event_source=internal):' - - ' - C(host_group)' - - ' - C(host)' - - ' - C(host_template)' - - ' - C(application)' - - ' - C(event_type)' + - "Possible values when I(event_source=trigger):" + - " - C(host_group)" + - " - C(host)" + - " - C(trigger)" + - " - C(trigger_name)" + - " - C(trigger_severity)" + - " - C(time_period)" + - " - C(host_template)" + - " - C(maintenance_status) known in Zabbix 4.0 and above as 'Problem is suppressed'" + - " - C(event_tag)" + - " - C(event_tag_value)" + - "Possible values when I(event_source=discovery):" + - " - C(host_IP)" + - " - C(discovered_service_type)" + - " - C(discovered_service_port)" + - " - C(discovery_status)" + - " - C(uptime_or_downtime_duration)" + - " - C(received_value)" + - " - C(discovery_rule)" + - " - C(discovery_check)" + - " - C(proxy)" + - " - C(discovery_object)" + - "Possible values when I(event_source=auto_registration):" + - " - C(proxy)" + - " - C(host_name)" + - " - C(host_metadata)" + - "Possible values when I(event_source=internal):" + - " - C(host_group)" + - " - C(host)" + - " - C(host_template)" + - " - C(event_type)" + required: true value: + type: str description: - Value to compare with. - - 'When I(type=discovery_status), the choices are:' - - ' - C(up)' - - ' - C(down)' - - ' - C(discovered)' - - ' - C(lost)' - - 'When I(type=discovery_object), the choices are:' - - ' - C(host)' - - ' - C(service)' - - 'When I(type=event_type), the choices are:' - - ' - C(item in not supported state)' - - ' - C(item in normal state)' - - ' - C(LLD rule in not supported state)' - - ' - C(LLD rule in normal state)' - - ' - C(trigger in unknown state)' - - ' - C(trigger in normal state)' - - 'When I(type=trigger_severity), the choices are (case-insensitive):' - - ' - C(not classified)' - - ' - C(information)' - - ' - C(warning)' - - ' - C(average)' - - ' - C(high)' - - ' - C(disaster)' + - "When I(type=discovery_status), the choices are:" + - " - C(up)" + - " - C(down)" + - " - C(discovered)" + - " - C(lost)" + - "When I(type=discovery_object), the choices are:" + - " - C(host)" + - " - C(service)" + - "When I(type=event_type), the choices are:" + - " - C(item in not supported state)" + - " - C(item in normal state)" + - " - C(LLD rule in not supported state)" + - " - C(LLD rule in normal state)" + - " - C(trigger in unknown state)" + - " - C(trigger in normal state)" + - "When I(type=trigger_severity), the choices are (case-insensitive):" + - " - C(not classified)" + - " - C(information)" + - " - C(warning)" + - " - C(average)" + - " - C(high)" + - " - C(disaster)" - Irrespective of user-visible names being changed in Zabbix. Defaults to C(not classified) if omitted. - Besides the above options, this is usually either the name of the object or a string to compare with. value2: + type: str description: - Secondary value to compare with. - Required for trigger actions when condition I(type=event_tag_value). operator: + type: str description: - Condition operator. - When I(type) is set to C(time_period), the choices are C(in), C(not in). - - C(matches), C(does not match), C(Yes) and C(No) condition operators work only with >= Zabbix 4.0 - - When I(type) is set to C(maintenance_status), the choices are C(Yes) and C(No) for Zabbix >= 6.0 choices: - - C(equals) or C(=) - - C(does not equal) or C(<>) - - C(contains) or C(like) - - C(does not contain) or C(not like) - - C(in) - - C(is greater than or equals) or C(>=) - - C(is less than or equals) or C(<=) - - C(not in) - - C(matches) - - C(does not match) - - C(Yes) - - C(No) + - "equals" + - "=" + - "does not equal" + - "<>" + - "contains" + - "like" + - "does not contain" + - "not like" + - "in" + - "is greater than or equals" + - ">=" + - "is less than or equals" + - "<=" + - "not in" + - "matches" + - "does not match" + - "Yes" + - "No" + required: true formulaid: + type: str description: - Arbitrary unique ID that is used to reference the condition from a custom expression. - Can only contain upper-case letters. - Required for custom expression filters and ignored otherwise. eval_type: + type: str description: - Filter condition evaluation method. - Defaults to C(andor) if conditions are less then 2 or if I(formula) is not specified. - Defaults to C(custom_expression) when formula is specified. choices: - - 'andor' - - 'and' - - 'or' - - 'custom_expression' + - "andor" + - "and" + - "or" + - "custom_expression" formula: + type: str description: - User-defined expression to be used for evaluating conditions with a custom expression. - The expression must contain IDs that reference each condition by its formulaid. @@ -182,48 +203,21 @@ options: - Required when I(eval_type=custom_expression). - Use sequential IDs that start at "A". If non-sequential IDs are used, Zabbix re-indexes them. This makes each module run notice the difference in IDs and update the action. - default_message: - description: - - Problem message default text. - - With >= Zabbix 5.0 this field is removed from the API and is dropped silently by module. - - Works only with < Zabbix 5.0 - default_subject: - description: - - Problem message default subject. - - With >= Zabbix 5.0 this field is removed from the API and is dropped silently by module. - - Works only with < Zabbix 5.0 - recovery_default_message: - description: - - Recovery message text. - - With >= Zabbix 5.0 this field is removed from the API and is dropped silently by module. - - Works only with >= Zabbix 3.2 and < Zabbix 5.0 - recovery_default_subject: - description: - - Recovery message subject. - - With >= Zabbix 5.0 this field is removed from the API and is dropped silently by module. - - Works only with >= Zabbix 3.2 and < Zabbix 5.0 - acknowledge_default_message: - description: - - Update operation (known as "Acknowledge operation" before Zabbix 4.0) message text. - - With >= Zabbix 5.0 this field is removed from the API and is dropped silently by module. - - Works only with >= Zabbix 3.4 and < Zabbix 5.0 - acknowledge_default_subject: - description: - - Update operation (known as "Acknowledge operation" before Zabbix 4.0) message subject. - - With >= Zabbix 5.0 this field is removed from the API and is dropped silently by module. - - Works only with >= Zabbix 3.4 and < Zabbix 5.0 operations: type: list + elements: dict description: - List of action operations + default: [] suboptions: type: + type: str description: - Type of operation. - - 'Valid choices when setting type for I(recovery_operations) and I(acknowledge_operations):' - - ' - C(send_message)' - - ' - C(remote_command)' - - ' - C(notify_all_involved)' + - "Valid choices when setting type for I(recovery_operations) and I(acknowledge_operations):" + - " - C(send_message)" + - " - C(remote_command)" + - " - C(notify_all_involved)" - Choice C(notify_all_involved) only supported in I(recovery_operations) and I(acknowledge_operations). choices: - send_message @@ -238,47 +232,55 @@ options: - disable_host - set_host_inventory_mode - notify_all_involved + required: true esc_period: + type: str description: - Duration of an escalation step in seconds. - Must be greater than 60 seconds. - - Accepts only seconds in int for <= Zabbix 3.2 - - Accepts seconds, time unit with suffix and user macro since => Zabbix 3.4 + - Accepts seconds, time unit with suffix and user macro. - If set to 0 or 0s, the default action escalation period will be used. default: 0s esc_step_from: + type: int description: - Step to start escalation from. default: 1 esc_step_to: + type: int description: - Step to end escalation at. - Specify 0 for infinitely. default: 1 send_to_groups: type: list + elements: str description: - User groups to send messages to. send_to_users: type: list + elements: str description: - Users (usernames or aliases) to send messages to. - message: + op_message: + type: str description: - Operation message text. - - Will check the 'default message' and use the text from I(default_message) if this and I(default_subject) are not specified + - If I(op_message) and I(subject) not defined then "default message" from media type will be used subject: + type: str description: - Operation message subject. - - Will check the 'default message' and use the text from I(default_subject) if this and I(default_subject) are not specified + - If I(op_message) and I(subject) not defined then "default message" from media type will be used media_type: + type: str description: - Media type that will be used to send the message. - Can be used with I(type=send_message) or I(type=notify_all_involved) inside I(acknowledge_operations). - Set to C(all) for all media types - default: 'all' + default: "all" operation_condition: - type: 'str' + type: "str" description: - The action operation condition object defines a condition that must be met to perform the current operation. choices: @@ -286,15 +288,18 @@ options: - not_acknowledged host_groups: type: list + elements: str description: - List of host groups host should be added to. - Required when I(type=add_to_host_group) or I(type=remove_from_host_group). templates: type: list + elements: str description: - List of templates host should be linked to. - Required when I(type=link_to_template) or I(type=unlink_from_template). inventory: + type: str description: - Host inventory mode. - Required when I(type=set_host_inventory_mode). @@ -302,6 +307,7 @@ options: - manual - automatic command_type: + type: str description: - Type of operation command. - Required when I(type=remote_command). @@ -312,10 +318,12 @@ options: - telnet - global_script command: + type: str description: - Command to run. - Required when I(type=remote_command) and I(command_type!=global_script). execute_on: + type: str description: - Target on which the custom script operation command will be executed. - Required when I(type=remote_command) and I(command_type=custom_script). @@ -324,15 +332,20 @@ options: - server - proxy run_on_groups: + type: list + elements: str description: - Host groups to run remote commands on. - Required when I(type=remote_command) and I(run_on_hosts) is not set. run_on_hosts: + type: list + elements: str description: - Hosts to run remote commands on. - Required when I(type=remote_command) and I(run_on_groups) is not set. - If set to 0 the command will be run on the current host. ssh_auth_type: + type: str description: - Authentication method used for SSH commands. - Required when I(type=remote_command) and I(command_type=ssh). @@ -340,48 +353,292 @@ options: - password - public_key ssh_privatekey_file: + type: str description: - Name of the private key file used for SSH commands with public key authentication. - Required when I(ssh_auth_type=public_key). - Can be used when I(type=remote_command). ssh_publickey_file: + type: str description: - Name of the public key file used for SSH commands with public key authentication. - Required when I(ssh_auth_type=public_key). - Can be used when I(type=remote_command). username: + type: str description: - User name used for authentication. - Required when I(ssh_auth_type in [public_key, password]) or I(command_type=telnet). - Can be used when I(type=remote_command). password: + type: str description: - Password used for authentication. - Required when I(ssh_auth_type=password) or I(command_type=telnet). - Can be used when I(type=remote_command). port: + type: int description: - Port number used for authentication. - Can be used when I(command_type in [ssh, telnet]) and I(type=remote_command). script_name: + type: str description: - The name of script used for global script commands. - Required when I(command_type=global_script). - Can be used when I(type=remote_command). recovery_operations: type: list + elements: dict description: - List of recovery operations. - C(Suboptions) are the same as for I(operations). - - Works only with >= Zabbix 3.2 + default: [] + suboptions: + type: + type: str + description: + - Type of operation. + choices: + - send_message + - remote_command + - notify_all_involved + required: true + command_type: + type: str + required: false + description: + - Type of operation command. + choices: + - custom_script + - ipmi + - ssh + - telnet + - global_script + command: + type: str + required: false + description: + - Command to run. + execute_on: + type: str + required: false + description: + - Target on which the custom script operation command will be executed. + choices: + - agent + - server + - proxy + ssh_auth_type: + type: str + description: + - Authentication method used for SSH commands. + - Required when I(type=remote_command) and I(command_type=ssh). + choices: + - password + - public_key + ssh_privatekey_file: + type: str + description: + - Name of the private key file used for SSH commands with public key authentication. + - Required when I(ssh_auth_type=public_key). + - Can be used when I(type=remote_command). + ssh_publickey_file: + type: str + description: + - Name of the public key file used for SSH commands with public key authentication. + - Required when I(ssh_auth_type=public_key). + - Can be used when I(type=remote_command). + run_on_groups: + type: list + elements: str + description: + - Host groups to run remote commands on. + - Required when I(type=remote_command) and I(run_on_hosts) is not set. + run_on_hosts: + type: list + elements: str + description: + - Hosts to run remote commands on. + - Required when I(type=remote_command) and I(run_on_groups) is not set. + - If set to 0 the command will be run on the current host. + send_to_groups: + type: list + elements: str + description: + - User groups to send messages to. + send_to_users: + type: list + elements: str + description: + - Users (usernames or aliases) to send messages to. + media_type: + type: str + description: + - Media type that will be used to send the message. + - Can be used with I(type=send_message) or I(type=notify_all_involved) inside I(acknowledge_operations). + - Set to C(all) for all media types + default: "all" + op_message: + type: str + description: + - Operation message text. + - If I(op_message) and I(subject) not defined then "default message" from media type will be used + subject: + type: str + description: + - Operation message subject. + - If I(op_message) and I(subject) not defined then "default message" from media type will be used + username: + type: str + description: + - User name used for authentication. + - Required when I(ssh_auth_type in [public_key, password]) or I(command_type=telnet). + - Can be used when I(type=remote_command). + password: + type: str + description: + - Password used for authentication. + - Required when I(ssh_auth_type=password) or I(command_type=telnet). + - Can be used when I(type=remote_command). + port: + type: int + description: + - Port number used for authentication. + - Can be used when I(command_type in [ssh, telnet]) and I(type=remote_command). + script_name: + type: str + description: + - The name of script used for global script commands. + - Required when I(command_type=global_script). + - Can be used when I(type=remote_command). acknowledge_operations: type: list + elements: dict description: - List of acknowledge operations. - Action acknowledge operations are known as update operations since Zabbix 4.0. - C(Suboptions) are the same as for I(operations). - - Works only with >= Zabbix 3.4 + suboptions: + type: + type: str + description: + - Type of operation. + choices: + - send_message + - remote_command + - notify_all_involved + required: true + command_type: + type: str + description: + - Type of operation command. + required: false + choices: + - custom_script + - ipmi + - ssh + - telnet + - global_script + execute_on: + type: str + required: false + description: + - Target on which the custom script operation command will be executed. + choices: + - agent + - server + - proxy + command: + type: str + required: false + description: + - Command to run. + ssh_auth_type: + type: str + description: + - Authentication method used for SSH commands. + - Required when I(type=remote_command) and I(command_type=ssh). + choices: + - password + - public_key + ssh_privatekey_file: + type: str + description: + - Name of the private key file used for SSH commands with public key authentication. + - Required when I(ssh_auth_type=public_key). + - Can be used when I(type=remote_command). + ssh_publickey_file: + type: str + description: + - Name of the public key file used for SSH commands with public key authentication. + - Required when I(ssh_auth_type=public_key). + - Can be used when I(type=remote_command). + run_on_groups: + type: list + elements: str + description: + - Host groups to run remote commands on. + - Required when I(type=remote_command) and I(run_on_hosts) is not set. + run_on_hosts: + type: list + elements: str + description: + - Hosts to run remote commands on. + - Required when I(type=remote_command) and I(run_on_groups) is not set. + - If set to 0 the command will be run on the current host. + send_to_groups: + type: list + elements: str + description: + - User groups to send messages to. + send_to_users: + type: list + elements: str + description: + - Users (usernames or aliases) to send messages to. + media_type: + type: str + description: + - Media type that will be used to send the message. + - Can be used with I(type=send_message) or I(type=notify_all_involved) inside I(acknowledge_operations). + - Set to C(all) for all media types + default: "all" + op_message: + type: str + description: + - Operation message text. + - If I(op_message) and I(subject) not defined then "default message" from media type will be used + subject: + type: str + description: + - Operation message subject. + - If I(op_message) and I(subject) not defined then "default message" from media type will be used + username: + type: str + description: + - User name used for authentication. + - Required when I(ssh_auth_type in [public_key, password]) or I(command_type=telnet). + - Can be used when I(type=remote_command). + password: + type: str + description: + - Password used for authentication. + - Required when I(ssh_auth_type=password) or I(command_type=telnet). + - Can be used when I(type=remote_command). + port: + type: int + description: + - Port number used for authentication. + - Can be used when I(command_type in [ssh, telnet]) and I(type=remote_command). + script_name: + type: str + description: + - The name of script used for global script commands. + - Required when I(command_type=global_script). + - Can be used when I(type=remote_command). aliases: [ update_operations ] + default: [] pause_symptoms: type: bool description: @@ -390,26 +647,23 @@ options: - Works only with >= Zabbix 6.4 default: true -notes: - - Only Zabbix >= 3.0 is supported. - extends_documentation_fragment: - community.zabbix.zabbix -''' +""" -EXAMPLES = ''' +EXAMPLES = """ # If you want to use Username and Password to be authenticated by Zabbix Server - name: Set credentials to access Zabbix Server API - set_fact: + ansible.builtin.set_fact: ansible_user: Admin ansible_httpapi_pass: zabbix # If you want to use API token to be authenticated by Zabbix Server # https://www.zabbix.com/documentation/current/en/manual/web_interface/frontend_sections/administration/general#api-tokens - name: Set API token - set_fact: + ansible.builtin.set_fact: ansible_zabbix_auth_key: 8ec0d52432c15c91fcafe9888500cf9a607f44091ab554dbee860f6b44fac895 # Trigger action with only one condition @@ -421,25 +675,25 @@ EXAMPLES = ''' ansible_httpapi_port: 443 ansible_httpapi_use_ssl: true ansible_httpapi_validate_certs: false - ansible_zabbix_url_path: 'zabbixeu' # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu + ansible_zabbix_url_path: "zabbixeu" # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu ansible_host: zabbix-example-fqdn.org community.zabbix.zabbix_action: name: "Send alerts to Admin" - event_source: 'trigger' + event_source: "trigger" state: present status: enabled esc_period: 60 conditions: - - type: 'trigger_severity' - operator: '>=' - value: 'Information' + - type: "trigger_severity" + operator: ">=" + value: "Information" operations: - type: send_message subject: "Something bad is happening" - message: "Come on, guys do something" - media_type: 'Email' + op_message: "Come on, guys do something" + media_type: "Email" send_to_users: - - 'Admin' + - "Admin" # Trigger action with multiple conditions and operations - name: Deploy trigger action @@ -450,31 +704,31 @@ EXAMPLES = ''' ansible_httpapi_port: 443 ansible_httpapi_use_ssl: true ansible_httpapi_validate_certs: false - ansible_zabbix_url_path: 'zabbixeu' # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu + ansible_zabbix_url_path: "zabbixeu" # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu ansible_host: zabbix-example-fqdn.org community.zabbix.zabbix_action: name: "Send alerts to Admin" - event_source: 'trigger' + event_source: "trigger" state: present status: enabled esc_period: 1m conditions: - - type: 'trigger_name' - operator: 'like' - value: 'Zabbix agent is unreachable' + - type: "trigger_name" + operator: "like" + value: "Zabbix agent is unreachable" formulaid: A - - type: 'trigger_severity' - operator: '>=' - value: 'disaster' + - type: "trigger_severity" + operator: ">=" + value: "disaster" formulaid: B formula: A or B operations: - type: send_message - media_type: 'Email' + media_type: "Email" send_to_users: - - 'Admin' + - "Admin" - type: remote_command - command: 'systemctl restart zabbix-agent' + command: "systemctl restart zabbix-agent" command_type: custom_script execute_on: server run_on_hosts: @@ -489,46 +743,46 @@ EXAMPLES = ''' ansible_httpapi_port: 443 ansible_httpapi_use_ssl: true ansible_httpapi_validate_certs: false - ansible_zabbix_url_path: 'zabbixeu' # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu + ansible_zabbix_url_path: "zabbixeu" # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu ansible_host: zabbix-example-fqdn.org community.zabbix.zabbix_action: name: "Send alerts to Admin" - event_source: 'trigger' + event_source: "trigger" state: present status: enabled esc_period: 1h conditions: - - type: 'trigger_severity' - operator: '>=' - value: 'Information' + - type: "trigger_severity" + operator: ">=" + value: "Information" operations: - type: send_message subject: "Something bad is happening" - message: "Come on, guys do something" - media_type: 'Email' + op_message: "Come on, guys do something" + media_type: "Email" send_to_users: - - 'Admin' + - "Admin" recovery_operations: - type: send_message subject: "Host is down" - message: "Come on, guys do something" - media_type: 'Email' + op_message: "Come on, guys do something" + media_type: "Email" send_to_users: - - 'Admin' + - "Admin" acknowledge_operations: - type: send_message - media_type: 'Email' + media_type: "Email" send_to_users: - - 'Admin' -''' + - "Admin" +""" -RETURN = ''' +RETURN = """ msg: description: The result of the operation returned: success type: str - sample: 'Action Deleted: Register webservers, ID: 0001' -''' + sample: "Action Deleted: Register webservers, ID: 0001" +""" from ansible.module_utils.basic import AnsibleModule @@ -558,16 +812,11 @@ class Zapi(ZabbixBase): _params = { "selectOperations": "extend", "selectRecoveryOperations": "extend", - "selectAcknowledgeOperations": "extend", + "selectUpdateOperations": "extend", "selectFilter": "extend", - 'filter': {'name': [name]} + "filter": {"name": [name]} } - if LooseVersion(self._zbx_api_version) >= LooseVersion('6.0'): - _params['selectUpdateOperations'] = _params.pop('selectAcknowledgeOperations', 'extend') _action = self._zapi.action.get(_params) - if len(_action) > 0 and LooseVersion(self._zbx_api_version) < LooseVersion('6.0'): - _action[0]['recovery_operations'] = _action[0].pop('recoveryOperations', []) - _action[0]['acknowledge_operations'] = _action[0].pop('acknowledgeOperations', []) return _action except Exception as e: self._module.fail_json(msg="Failed to check if action '%s' exists: %s" % (name, e)) @@ -584,8 +833,8 @@ class Zapi(ZabbixBase): """ try: action_list = self._zapi.action.get({ - 'output': 'extend', - 'filter': {'name': [name]} + "output": "extend", + "filter": {"name": [name]} }) if len(action_list) < 1: self._module.fail_json(msg="Action not found: %s" % name) @@ -606,9 +855,9 @@ class Zapi(ZabbixBase): """ try: host_list = self._zapi.host.get({ - 'output': 'extend', - 'selectInventory': 'extend', - 'filter': {'host': [host_name]} + "output": "extend", + "selectInventory": "extend", + "filter": {"host": [host_name]} }) if len(host_list) < 1: self._module.fail_json(msg="Host not found: %s" % host_name) @@ -629,8 +878,8 @@ class Zapi(ZabbixBase): """ try: hostgroup_list = self._zapi.hostgroup.get({ - 'output': 'extend', - 'filter': {'name': [hostgroup_name]} + "output": "extend", + "filter": {"name": [hostgroup_name]} }) if len(hostgroup_list) < 1: self._module.fail_json(msg="Host group not found: %s" % hostgroup_name) @@ -651,8 +900,8 @@ class Zapi(ZabbixBase): """ try: template_list = self._zapi.template.get({ - 'output': 'extend', - 'filter': {'host': [template_name]} + "output": "extend", + "filter": {"host": [template_name]} }) if len(template_list) < 1: self._module.fail_json(msg="Template not found: %s" % template_name) @@ -673,8 +922,8 @@ class Zapi(ZabbixBase): """ try: trigger_list = self._zapi.trigger.get({ - 'output': 'extend', - 'filter': {'description': [trigger_name]} + "output": "extend", + "filter": {"description": [trigger_name]} }) if len(trigger_list) < 1: self._module.fail_json(msg="Trigger not found: %s" % trigger_name) @@ -695,8 +944,8 @@ class Zapi(ZabbixBase): """ try: discovery_rule_list = self._zapi.drule.get({ - 'output': 'extend', - 'filter': {'name': [discovery_rule_name]} + "output": "extend", + "filter": {"name": [discovery_rule_name]} }) if len(discovery_rule_list) < 1: self._module.fail_json(msg="Discovery rule not found: %s" % discovery_rule_name) @@ -716,38 +965,38 @@ class Zapi(ZabbixBase): """ try: - discovery_rule_name, dcheck_type = discovery_check_name.split(': ') + discovery_rule_name, dcheck_type = discovery_check_name.split(": ") dcheck_type_to_number = { - 'SSH': '0', - 'LDAP': '1', - 'SMTP': '2', - 'FTP': '3', - 'HTTP': '4', - 'POP': '5', - 'NNTP': '6', - 'IMAP': '7', - 'TCP': '8', - 'Zabbix agent': '9', - 'SNMPv1 agent': '10', - 'SNMPv2 agent': '11', - 'ICMP ping': '12', - 'SNMPv3 agent': '13', - 'HTTPS': '14', - 'Telnet': '15' + "SSH": "0", + "LDAP": "1", + "SMTP": "2", + "FTP": "3", + "HTTP": "4", + "POP": "5", + "NNTP": "6", + "IMAP": "7", + "TCP": "8", + "Zabbix agent": "9", + "SNMPv1 agent": "10", + "SNMPv2 agent": "11", + "ICMP ping": "12", + "SNMPv3 agent": "13", + "HTTPS": "14", + "Telnet": "15" } if dcheck_type not in dcheck_type_to_number: self._module.fail_json(msg="Discovery check type: %s does not exist" % dcheck_type) discovery_rule_list = self._zapi.drule.get({ - 'output': ['dchecks'], - 'filter': {'name': [discovery_rule_name]}, - 'selectDChecks': 'extend' + "output": ["dchecks"], + "filter": {"name": [discovery_rule_name]}, + "selectDChecks": "extend" }) if len(discovery_rule_list) < 1: self._module.fail_json(msg="Discovery check not found: %s" % discovery_check_name) - for dcheck in discovery_rule_list[0]['dchecks']: - if dcheck_type_to_number[dcheck_type] == dcheck['type']: + for dcheck in discovery_rule_list[0]["dchecks"]: + if dcheck_type_to_number[dcheck_type] == dcheck["type"]: return dcheck self._module.fail_json(msg="Discovery check not found: %s" % discovery_check_name) except Exception as e: @@ -765,8 +1014,8 @@ class Zapi(ZabbixBase): """ try: proxy_list = self._zapi.proxy.get({ - 'output': 'extend', - 'filter': {'host': [proxy_name]} + "output": "extend", + "filter": {"host": [proxy_name]} }) if len(proxy_list) < 1: self._module.fail_json(msg="Proxy not found: %s" % proxy_name) @@ -785,22 +1034,19 @@ class Zapi(ZabbixBase): mediatype matching mediatype name """ - if LooseVersion(self._zbx_api_version) >= LooseVersion('4.4'): - filter = {'name': [mediatype_name]} - else: - filter = {'description': [mediatype_name]} + filter = {"name": [mediatype_name]} try: - if str(mediatype_name).lower() == 'all': - return '0' + if str(mediatype_name).lower() == "all": + return "0" mediatype_list = self._zapi.mediatype.get({ - 'output': 'extend', - 'filter': filter + "output": "extend", + "filter": filter }) if len(mediatype_list) < 1: self._module.fail_json(msg="Media type not found: %s" % mediatype_name) else: - return mediatype_list[0]['mediatypeid'] + return mediatype_list[0]["mediatypeid"] except Exception as e: self._module.fail_json(msg="Failed to get mediatype '%s': %s" % (mediatype_name, e)) @@ -815,13 +1061,10 @@ class Zapi(ZabbixBase): """ try: - if LooseVersion(self._zbx_api_version) >= LooseVersion('5.4'): - filter = {'username': [user_name]} - else: - filter = {'alias': [user_name]} + filter = {"username": [user_name]} user_list = self._zapi.user.get({ - 'output': 'extend', - 'filter': filter, + "output": "extend", + "filter": filter, }) if len(user_list) < 1: self._module.fail_json(msg="User not found: %s" % user_name) @@ -842,8 +1085,8 @@ class Zapi(ZabbixBase): """ try: usergroup_list = self._zapi.usergroup.get({ - 'output': 'extend', - 'filter': {'name': [usergroup_name]} + "output": "extend", + "filter": {"name": [usergroup_name]} }) if len(usergroup_list) < 1: self._module.fail_json(msg="User group not found: %s" % usergroup_name) @@ -867,8 +1110,8 @@ class Zapi(ZabbixBase): if script_name is None: return {} script_list = self._zapi.script.get({ - 'output': 'extend', - 'filter': {'name': [script_name]} + "output": "extend", + "filter": {"name": [script_name]} }) if len(script_list) < 1: self._module.fail_json(msg="Script not found: %s" % script_name) @@ -894,72 +1137,50 @@ class Action(Zapi): """ _params = { - 'name': kwargs['name'], - 'eventsource': zabbix_utils.helper_to_numeric_value([ - 'trigger', - 'discovery', - 'auto_registration', - 'internal'], kwargs['event_source']), - 'esc_period': kwargs.get('esc_period'), - 'filter': kwargs['conditions'], - 'def_longdata': kwargs['default_message'], - 'def_shortdata': kwargs['default_subject'], - 'r_longdata': kwargs['recovery_default_message'], - 'r_shortdata': kwargs['recovery_default_subject'], - 'ack_longdata': kwargs['acknowledge_default_message'], - 'ack_shortdata': kwargs['acknowledge_default_subject'], - 'operations': kwargs['operations'], - 'recovery_operations': kwargs.get('recovery_operations'), - 'acknowledge_operations': kwargs.get('acknowledge_operations'), - 'status': zabbix_utils.helper_to_numeric_value([ - 'enabled', - 'disabled'], kwargs['status']) + "name": kwargs["name"], + "eventsource": zabbix_utils.helper_to_numeric_value([ + "trigger", + "discovery", + "auto_registration", + "internal"], kwargs["event_source"]), + "esc_period": kwargs.get("esc_period"), + "filter": kwargs["conditions"], + "operations": kwargs["operations"], + "recovery_operations": kwargs.get("recovery_operations"), + "acknowledge_operations": kwargs.get("acknowledge_operations"), + "status": zabbix_utils.helper_to_numeric_value([ + "enabled", + "disabled"], kwargs["status"]) } - if kwargs['event_source'] == 'trigger': - if LooseVersion(self._zbx_api_version) >= LooseVersion('4.0'): - _params['pause_suppressed'] = '1' if kwargs['pause_in_maintenance'] else '0' - else: - _params['maintenance_mode'] = '1' if kwargs['pause_in_maintenance'] else '0' - if LooseVersion(self._zbx_api_version) >= LooseVersion('6.4'): - _params['pause_symptoms'] = '1' if kwargs['pause_symptoms'] else '0' - - if LooseVersion(self._zbx_api_version) >= LooseVersion('5.0'): - # remove some fields regarding - # https://www.zabbix.com/documentation/5.0/manual/api/reference/action/object - _params.pop('def_longdata', None) - _params.pop('def_shortdata', None) - _params.pop('r_longdata', None) - _params.pop('r_shortdata', None) - - if (LooseVersion(self._zbx_api_version) < LooseVersion('3.4') - or LooseVersion(self._zbx_api_version) >= LooseVersion('5.0')): - _params.pop('ack_longdata', None) - _params.pop('ack_shortdata', None) - - if LooseVersion(self._zbx_api_version) >= LooseVersion('6.0'): - _params['update_operations'] = kwargs.get('update_operations') - if 'update_operations' in _params and not isinstance(_params.get('update_operations', None), type(None)): - _params.pop('acknowledge_operations', None) - elif isinstance(_params.get('acknowledge_operations', None), list): - _params['update_operations'] = _params.pop('acknowledge_operations', []) - else: - _params['update_operations'] = [] - _params.pop('acknowledge_operations', None) + if kwargs["event_source"] == "trigger": + _params["pause_suppressed"] = "1" if kwargs["pause_in_maintenance"] else "0" + if LooseVersion(self._zbx_api_version) >= LooseVersion("6.4"): + _params["pause_symptoms"] = "1" if kwargs["pause_symptoms"] else "0" + _params["notify_if_canceled"] = "1" if kwargs["notify_if_canceled"] else "0" + + _params["update_operations"] = kwargs.get("update_operations") + if "update_operations" in _params and not isinstance(_params.get("update_operations", None), type(None)): + _params.pop("acknowledge_operations", None) + elif isinstance(_params.get("acknowledge_operations", None), list): + _params["update_operations"] = _params.pop("acknowledge_operations", []) + else: + _params["update_operations"] = [] + _params.pop("acknowledge_operations", None) - if 'esc_period' in _params and isinstance(_params.get('esc_period', None), type(None)): - _params.pop('esc_period') + if "esc_period" in _params and isinstance(_params.get("esc_period", None), type(None)): + _params.pop("esc_period") - if 'recovery_operations' in _params: - if isinstance(_params.get('recovery_operations', None), type(None)) or len(_params.get('recovery_operations', [])) == 0: - _params.pop('recovery_operations') + if "recovery_operations" in _params: + if isinstance(_params.get("recovery_operations", None), type(None)) or len(_params.get("recovery_operations", [])) == 0: + _params.pop("recovery_operations") - if 'update_operations' in _params: - if isinstance(_params.get('update_operations', None), type(None)) or len(_params.get('update_operations', [])) == 0: - _params.pop('update_operations') + if "update_operations" in _params: + if isinstance(_params.get("update_operations", None), type(None)) or len(_params.get("update_operations", [])) == 0: + _params.pop("update_operations") - if _params['eventsource'] not in [0, 3]: - _params.pop('esc_period') + if _params["eventsource"] not in [0, 3]: + _params.pop("esc_period") return _params @@ -972,7 +1193,7 @@ class Action(Zapi): Returns: dict: dictionary of differences """ - existing_action = zabbix_utils.helper_convert_unicode_to_str(self.check_if_action_exists(kwargs['name'])[0]) + existing_action = zabbix_utils.helper_convert_unicode_to_str(self.check_if_action_exists(kwargs["name"])[0]) parameters = zabbix_utils.helper_convert_unicode_to_str(self._construct_parameters(**kwargs)) change_parameters = {} _diff = zabbix_utils.helper_cleanup_data(zabbix_utils.helper_compare_dictionaries(parameters, existing_action, change_parameters)) @@ -990,10 +1211,10 @@ class Action(Zapi): try: if self._module.check_mode: self._module.exit_json(msg="Action would be updated if check mode was not specified: %s" % kwargs, changed=True) - kwargs['actionid'] = kwargs.pop('action_id') + kwargs["actionid"] = kwargs.pop("action_id") return self._zapi.action.update(kwargs) except Exception as e: - self._module.fail_json(msg="Failed to update action '%s': %s" % (kwargs['actionid'], e)) + self._module.fail_json(msg="Failed to update action '%s': %s" % (kwargs["actionid"], e)) def add_action(self, **kwargs): """Add action. @@ -1009,9 +1230,9 @@ class Action(Zapi): self._module.exit_json(msg="Action would be added if check mode was not specified", changed=True) parameters = self._construct_parameters(**kwargs) action_list = self._zapi.action.create(parameters) - return action_list['actionids'][0] + return action_list["actionids"][0] except Exception as e: - self._module.fail_json(msg="Failed to create action '%s': %s" % (kwargs['name'], e)) + self._module.fail_json(msg="Failed to create action '%s': %s" % (kwargs["name"], e)) def delete_action(self, action_id): """Delete action. @@ -1052,10 +1273,11 @@ class Operations(Zapi): "unlink_from_template", "enable_host", "disable_host", - "set_host_inventory_mode"], operation['type'] + "set_host_inventory_mode"], + operation["type"] ) except Exception: - self._module.fail_json(msg="Unsupported value '%s' for operation type." % operation['type']) + self._module.fail_json(msg="Unsupported value '%s' for operation type." % operation["type"]) def _construct_opmessage(self, operation): """Construct operation message. @@ -1068,12 +1290,12 @@ class Operations(Zapi): """ try: return { - 'default_msg': '0' if operation.get('message') is not None or operation.get('subject') is not None else '1', - 'mediatypeid': self._zapi_wrapper.get_mediatype_by_mediatype_name( - operation.get('media_type') - ) if operation.get('media_type') is not None else '0', - 'message': operation.get('message'), - 'subject': operation.get('subject'), + "default_msg": "0" if operation.get("op_message") is not None or operation.get("subject") is not None else "1", + "mediatypeid": self._zapi_wrapper.get_mediatype_by_mediatype_name( + operation.get("media_type") + ) if operation.get("media_type") is not None else "0", + "message": operation.get("op_message"), + "subject": operation.get("subject"), } except Exception as e: self._module.fail_json(msg="Failed to construct operation message. The error was: %s" % e) @@ -1087,11 +1309,11 @@ class Operations(Zapi): Returns: list: constructed operation message user or None if operation not found """ - if operation.get('send_to_users') is None: + if operation.get("send_to_users") is None: return None return [{ - 'userid': self._zapi_wrapper.get_user_by_user_name(_user)['userid'] - } for _user in operation.get('send_to_users')] + "userid": self._zapi_wrapper.get_user_by_user_name(_user)["userid"] + } for _user in operation.get("send_to_users")] def _construct_opmessage_grp(self, operation): """Construct operation message group. @@ -1102,11 +1324,11 @@ class Operations(Zapi): Returns: list: constructed operation message group or None if operation not found """ - if operation.get('send_to_groups') is None: + if operation.get("send_to_groups") is None: return None return [{ - 'usrgrpid': self._zapi_wrapper.get_usergroup_by_usergroup_name(_group)['usrgrpid'] - } for _group in operation.get('send_to_groups')] + "usrgrpid": self._zapi_wrapper.get_usergroup_by_usergroup_name(_group)["usrgrpid"] + } for _group in operation.get("send_to_groups")] def _construct_opcommand(self, operation): """Construct operation command. @@ -1118,39 +1340,11 @@ class Operations(Zapi): list: constructed operation command """ try: - if LooseVersion(self._zbx_api_version) < LooseVersion('6.0'): - opcommand = { - 'type': zabbix_utils.helper_to_numeric_value([ - 'custom_script', - 'ipmi', - 'ssh', - 'telnet', - 'global_script'], operation.get('command_type', 'custom_script')), - 'command': operation.get('command'), - 'execute_on': zabbix_utils.helper_to_numeric_value([ - 'agent', - 'server', - 'proxy'], operation.get('execute_on', 'server')), - 'scriptid': self._zapi_wrapper.get_script_by_script_name( - operation.get('script_name') - ).get('scriptid'), - 'authtype': zabbix_utils.helper_to_numeric_value([ - 'password', - 'public_key' - ], operation.get('ssh_auth_type')), - 'privatekey': operation.get('ssh_privatekey_file'), - 'publickey': operation.get('ssh_publickey_file'), - 'username': operation.get('username'), - 'password': operation.get('password'), - 'port': operation.get('port') - } - else: - # In 6.0 opcommand is an opbject with just one key 'scriptid' - opcommand = { - 'scriptid': self._zapi_wrapper.get_script_by_script_name( - operation.get('script_name') - ).get('scriptid') - } + opcommand = { + "scriptid": self._zapi_wrapper.get_script_by_script_name( + operation.get("script_name") + ).get("scriptid") + } return opcommand @@ -1166,11 +1360,11 @@ class Operations(Zapi): Returns: list: constructed operation command host """ - if operation.get('run_on_hosts') is None: + if operation.get("run_on_hosts") is None: return None return [{ - 'hostid': self._zapi_wrapper.get_host_by_host_name(_host)['hostid'] - } if str(_host) != '0' else {'hostid': '0'} for _host in operation.get('run_on_hosts')] + "hostid": self._zapi_wrapper.get_host_by_host_name(_host)["hostid"] + } if str(_host) != "0" else {"hostid": "0"} for _host in operation.get("run_on_hosts")] def _construct_opcommand_grp(self, operation): """Construct operation command group. @@ -1181,11 +1375,11 @@ class Operations(Zapi): Returns: list: constructed operation command group """ - if operation.get('run_on_groups') is None: + if operation.get("run_on_groups") is None: return None return [{ - 'groupid': self._zapi_wrapper.get_hostgroup_by_hostgroup_name(_group)['groupid'] - } for _group in operation.get('run_on_groups')] + "groupid": self._zapi_wrapper.get_hostgroup_by_hostgroup_name(_group)["groupid"] + } for _group in operation.get("run_on_groups")] def _construct_opgroup(self, operation): """Construct operation group. @@ -1197,8 +1391,8 @@ class Operations(Zapi): list: constructed operation group """ return [{ - 'groupid': self._zapi_wrapper.get_hostgroup_by_hostgroup_name(_group)['groupid'] - } for _group in operation.get('host_groups', [])] + "groupid": self._zapi_wrapper.get_hostgroup_by_hostgroup_name(_group)["groupid"] + } for _group in operation.get("host_groups", [])] def _construct_optemplate(self, operation): """Construct operation template. @@ -1210,8 +1404,8 @@ class Operations(Zapi): list: constructed operation template """ return [{ - 'templateid': self._zapi_wrapper.get_template_by_template_name(_template)['templateid'] - } for _template in operation.get('templates', [])] + "templateid": self._zapi_wrapper.get_template_by_template_name(_template)["templateid"] + } for _template in operation.get("templates", [])] def _construct_opinventory(self, operation): """Construct operation inventory. @@ -1223,10 +1417,10 @@ class Operations(Zapi): dict: constructed operation inventory """ return { - 'inventory_mode': zabbix_utils.helper_to_numeric_value([ - 'manual', - 'automatic' - ], operation.get('inventory')) + "inventory_mode": zabbix_utils.helper_to_numeric_value([ + "manual", + "automatic" + ], operation.get("inventory")) } def _construct_opconditions(self, operation): @@ -1238,16 +1432,16 @@ class Operations(Zapi): Returns: list: constructed operation conditions """ - _opcond = operation.get('operation_condition') + _opcond = operation.get("operation_condition") if _opcond is not None: - if _opcond == 'acknowledged': - _value = '1' - elif _opcond == 'not_acknowledged': - _value = '0' + if _opcond == "acknowledged": + _value = "1" + elif _opcond == "not_acknowledged": + _value = "0" return [{ - 'conditiontype': '14', - 'operator': '0', - 'value': _value + "conditiontype": "14", + "operator": "0", + "value": _value }] return [] @@ -1264,49 +1458,49 @@ class Operations(Zapi): for op in operations: operation_type = self._construct_operationtype(op) constructed_operation = { - 'operationtype': operation_type, - 'esc_period': op.get('esc_period'), - 'esc_step_from': op.get('esc_step_from'), - 'esc_step_to': op.get('esc_step_to') + "operationtype": operation_type, + "esc_period": op.get("esc_period"), + "esc_step_from": op.get("esc_step_from"), + "esc_step_to": op.get("esc_step_to") } # Send Message type - if constructed_operation['operationtype'] == 0: - constructed_operation['opmessage'] = self._construct_opmessage(op) - constructed_operation['opmessage_usr'] = self._construct_opmessage_usr(op) - constructed_operation['opmessage_grp'] = self._construct_opmessage_grp(op) - if event_source == 'trigger': - # opconditions valid only for 'trigger' action - constructed_operation['opconditions'] = self._construct_opconditions(op) + if constructed_operation["operationtype"] == 0: + constructed_operation["opmessage"] = self._construct_opmessage(op) + constructed_operation["opmessage_usr"] = self._construct_opmessage_usr(op) + constructed_operation["opmessage_grp"] = self._construct_opmessage_grp(op) + if event_source == "trigger": + # opconditions valid only for "trigger" action + constructed_operation["opconditions"] = self._construct_opconditions(op) # Send Command type - if constructed_operation['operationtype'] == 1: - constructed_operation['opcommand'] = self._construct_opcommand(op) - constructed_operation['opcommand_hst'] = self._construct_opcommand_hst(op) - constructed_operation['opcommand_grp'] = self._construct_opcommand_grp(op) - if event_source == 'trigger': - # opconditions valid only for 'trigger' action - constructed_operation['opconditions'] = self._construct_opconditions(op) + if constructed_operation["operationtype"] == 1: + constructed_operation["opcommand"] = self._construct_opcommand(op) + constructed_operation["opcommand_hst"] = self._construct_opcommand_hst(op) + constructed_operation["opcommand_grp"] = self._construct_opcommand_grp(op) + if event_source == "trigger": + # opconditions valid only for "trigger" action + constructed_operation["opconditions"] = self._construct_opconditions(op) # Add to/Remove from host group - if constructed_operation['operationtype'] in (4, 5): - constructed_operation['opgroup'] = self._construct_opgroup(op) + if constructed_operation["operationtype"] in (4, 5): + constructed_operation["opgroup"] = self._construct_opgroup(op) # Link/Unlink template - if constructed_operation['operationtype'] in (6, 7): - constructed_operation['optemplate'] = self._construct_optemplate(op) + if constructed_operation["operationtype"] in (6, 7): + constructed_operation["optemplate"] = self._construct_optemplate(op) # Set inventory mode - if constructed_operation['operationtype'] == 10: - constructed_operation['opinventory'] = self._construct_opinventory(op) + if constructed_operation["operationtype"] == 10: + constructed_operation["opinventory"] = self._construct_opinventory(op) # Remove escalation params when for event sources where they are not applicable - if event_source in ['trigger', 'internal']: - if isinstance(constructed_operation.get('esc_period'), type(None)): - constructed_operation['esc_period'] = 0 + if event_source in ["trigger", "internal"]: + if isinstance(constructed_operation.get("esc_period"), type(None)): + constructed_operation["esc_period"] = 0 else: - constructed_operation.pop('esc_period') - constructed_operation.pop('esc_step_from') - constructed_operation.pop('esc_step_to') + constructed_operation.pop("esc_period") + constructed_operation.pop("esc_step_from") + constructed_operation.pop("esc_step_to") constructed_data.append(constructed_operation) @@ -1317,6 +1511,7 @@ class RecoveryOperations(Operations): """ Restructures the user defined recovery operations data to fit the Zabbix API requirements """ + def _construct_operationtype(self, operation): """Construct operation type. @@ -1339,10 +1534,10 @@ class RecoveryOperations(Operations): None, None, None, - "notify_all_involved"], operation['type'] + "notify_all_involved"], operation["type"] ) except Exception: - self._module.fail_json(msg="Unsupported value '%s' for recovery operation type." % operation['type']) + self._module.fail_json(msg="Unsupported value '%s' for recovery operation type." % operation["type"]) def construct_the_data(self, operations): """Construct the recovery operations data using helper methods. @@ -1357,25 +1552,24 @@ class RecoveryOperations(Operations): for op in operations: operation_type = self._construct_operationtype(op) constructed_operation = { - 'operationtype': operation_type, + "operationtype": operation_type, } # Send Message type - if constructed_operation['operationtype'] == 0: - constructed_operation['opmessage'] = self._construct_opmessage(op) - constructed_operation['opmessage_usr'] = self._construct_opmessage_usr(op) - constructed_operation['opmessage_grp'] = self._construct_opmessage_grp(op) + if constructed_operation["operationtype"] == 0: + constructed_operation["opmessage"] = self._construct_opmessage(op) + constructed_operation["opmessage_usr"] = self._construct_opmessage_usr(op) + constructed_operation["opmessage_grp"] = self._construct_opmessage_grp(op) - if constructed_operation['operationtype'] == 11: - constructed_operation['opmessage'] = self._construct_opmessage(op) - if LooseVersion(self._zbx_api_version) >= LooseVersion('6.0'): - constructed_operation['opmessage'].pop('mediatypeid') + if constructed_operation["operationtype"] == 11: + constructed_operation["opmessage"] = self._construct_opmessage(op) + constructed_operation["opmessage"].pop("mediatypeid") # Send Command type - if constructed_operation['operationtype'] == 1: - constructed_operation['opcommand'] = self._construct_opcommand(op) - constructed_operation['opcommand_hst'] = self._construct_opcommand_hst(op) - constructed_operation['opcommand_grp'] = self._construct_opcommand_grp(op) + if constructed_operation["operationtype"] == 1: + constructed_operation["opcommand"] = self._construct_opcommand(op) + constructed_operation["opcommand_hst"] = self._construct_opcommand_hst(op) + constructed_operation["opcommand_grp"] = self._construct_opcommand_grp(op) constructed_data.append(constructed_operation) @@ -1386,6 +1580,7 @@ class AcknowledgeOperations(Operations): """ Restructures the user defined acknowledge operations data to fit the Zabbix API requirements """ + def _construct_operationtype(self, operation): """Construct operation type. @@ -1409,10 +1604,10 @@ class AcknowledgeOperations(Operations): None, None, None, - "notify_all_involved"], operation['type'] + "notify_all_involved"], operation["type"] ) except Exception: - self._module.fail_json(msg="Unsupported value '%s' for acknowledge operation type." % operation['type']) + self._module.fail_json(msg="Unsupported value '%s' for acknowledge operation type." % operation["type"]) def construct_the_data(self, operations): """Construct the acknowledge operations data using helper methods. @@ -1427,25 +1622,24 @@ class AcknowledgeOperations(Operations): for op in operations: operation_type = self._construct_operationtype(op) constructed_operation = { - 'operationtype': operation_type, + "operationtype": operation_type, } # Send Message type - if constructed_operation['operationtype'] == 0: - constructed_operation['opmessage'] = self._construct_opmessage(op) - constructed_operation['opmessage_usr'] = self._construct_opmessage_usr(op) - constructed_operation['opmessage_grp'] = self._construct_opmessage_grp(op) + if constructed_operation["operationtype"] == 0: + constructed_operation["opmessage"] = self._construct_opmessage(op) + constructed_operation["opmessage_usr"] = self._construct_opmessage_usr(op) + constructed_operation["opmessage_grp"] = self._construct_opmessage_grp(op) - if constructed_operation['operationtype'] == 12: - constructed_operation['opmessage'] = self._construct_opmessage(op) - if LooseVersion(self._zbx_api_version) >= LooseVersion('6.0'): - constructed_operation['opmessage'].pop('mediatypeid') + if constructed_operation["operationtype"] == 12: + constructed_operation["opmessage"] = self._construct_opmessage(op) + constructed_operation["opmessage"].pop("mediatypeid") # Send Command type - if constructed_operation['operationtype'] == 1: - constructed_operation['opcommand'] = self._construct_opcommand(op) - constructed_operation['opcommand_hst'] = self._construct_opcommand_hst(op) - constructed_operation['opcommand_grp'] = self._construct_opcommand_grp(op) + if constructed_operation["operationtype"] == 1: + constructed_operation["opcommand"] = self._construct_opcommand(op) + constructed_operation["opcommand_hst"] = self._construct_opcommand_hst(op) + constructed_operation["opcommand_grp"] = self._construct_opcommand_grp(op) constructed_data.append(constructed_operation) @@ -1465,40 +1659,40 @@ class Filter(Zapi): """ if len(_conditions) <= 1: return { - 'evaltype': '0', - 'formula': None + "evaltype": "0", + "formula": None } - if _eval_type == 'andor': + if _eval_type == "andor": return { - 'evaltype': '0', - 'formula': None + "evaltype": "0", + "formula": None } - if _eval_type == 'and': + if _eval_type == "and": return { - 'evaltype': '1', - 'formula': None + "evaltype": "1", + "formula": None } - if _eval_type == 'or': + if _eval_type == "or": return { - 'evaltype': '2', - 'formula': None + "evaltype": "2", + "formula": None } - if _eval_type == 'custom_expression': + if _eval_type == "custom_expression": if _formula is not None: return { - 'evaltype': '3', - 'formula': _formula + "evaltype": "3", + "formula": _formula } else: self._module.fail_json(msg="'formula' is required when 'eval_type' is set to 'custom_expression'") if _formula is not None: return { - 'evaltype': '3', - 'formula': _formula + "evaltype": "3", + "formula": _formula } return { - 'evaltype': '0', - 'formula': None + "evaltype": "0", + "formula": None } def _construct_conditiontype(self, _condition): @@ -1510,11 +1704,6 @@ class Filter(Zapi): Returns: str: constructed condition type data """ - # application is disabled is disabled for condition type since 5.4 version. - if (LooseVersion(self._zbx_api_version) >= LooseVersion('5.4') - and _condition['type'] == 'application'): - self._module.fail_json(msg="'%s' is disabled for condition type since 5.4 version." % _condition['type']) - try: return zabbix_utils.helper_to_numeric_value([ "host_group", @@ -1532,7 +1721,7 @@ class Filter(Zapi): "received_value", "host_template", None, - "application", + None, "maintenance_status", None, "discovery_rule", @@ -1543,10 +1732,10 @@ class Filter(Zapi): "event_type", "host_metadata", "event_tag", - "event_tag_value"], _condition['type'] + "event_tag_value"], _condition["type"] ) except Exception: - self._module.fail_json(msg="Unsupported value '%s' for condition type." % _condition['type']) + self._module.fail_json(msg="Unsupported value '%s' for condition type." % _condition["type"]) def _construct_operator(self, _condition): """Construct operator @@ -1570,10 +1759,10 @@ class Filter(Zapi): "matches", "does not match", "Yes", - "No"], _condition['operator'] + "No"], _condition["operator"] ) except Exception: - self._module.fail_json(msg="Unsupported value '%s' for operator." % _condition['operator']) + self._module.fail_json(msg="Unsupported value '%s' for operator." % _condition["operator"]) def _construct_value(self, conditiontype, value): """Construct operator @@ -1588,13 +1777,13 @@ class Filter(Zapi): try: # Host group if conditiontype == 0: - return self._zapi_wrapper.get_hostgroup_by_hostgroup_name(value)['groupid'] + return self._zapi_wrapper.get_hostgroup_by_hostgroup_name(value)["groupid"] # Host if conditiontype == 1: - return self._zapi_wrapper.get_host_by_host_name(value)['hostid'] + return self._zapi_wrapper.get_host_by_host_name(value)["hostid"] # Trigger if conditiontype == 2: - return self._zapi_wrapper.get_trigger_by_trigger_name(value)['triggerid'] + return self._zapi_wrapper.get_trigger_by_trigger_name(value)["triggerid"] # Trigger name: return as is # Trigger severity if conditiontype == 4: @@ -1645,20 +1834,19 @@ class Filter(Zapi): "lost"], value ) if conditiontype == 13: - return self._zapi_wrapper.get_template_by_template_name(value)['templateid'] - if LooseVersion(self._zapi_wrapper._zbx_api_version) >= LooseVersion('6.0'): - # maintenance_status - if conditiontype == 16: - return zabbix_utils.helper_to_numeric_value([ - "Yes", - "No"], value - ) + return self._zapi_wrapper.get_template_by_template_name(value)["templateid"] + # maintenance_status + if conditiontype == 16: + return zabbix_utils.helper_to_numeric_value([ + "Yes", + "No"], value + ) if conditiontype == 18: - return self._zapi_wrapper.get_discovery_rule_by_discovery_rule_name(value)['druleid'] + return self._zapi_wrapper.get_discovery_rule_by_discovery_rule_name(value)["druleid"] if conditiontype == 19: - return self._zapi_wrapper.get_discovery_check_by_discovery_check_name(value)['dcheckid'] + return self._zapi_wrapper.get_discovery_check_by_discovery_check_name(value)["dcheckid"] if conditiontype == 20: - return self._zapi_wrapper.get_proxy_by_proxy_name(value)['proxyid'] + return self._zapi_wrapper.get_proxy_by_proxy_name(value)["proxyid"] if conditiontype == 21: return zabbix_utils.helper_to_numeric_value([ "pchldrfor0", @@ -1697,10 +1885,10 @@ class Filter(Zapi): if _conditions is None: return None constructed_data = {} - constructed_data['conditions'] = [] + constructed_data["conditions"] = [] for cond in _conditions: condition_type = self._construct_conditiontype(cond) - constructed_data['conditions'].append({ + constructed_data["conditions"].append({ "conditiontype": condition_type, "value": self._construct_value(condition_type, cond.get("value")), "value2": cond.get("value2"), @@ -1710,10 +1898,10 @@ class Filter(Zapi): _constructed_evaltype = self._construct_evaltype( _eval_type, _formula, - constructed_data['conditions'] + constructed_data["conditions"] ) - constructed_data['evaltype'] = _constructed_evaltype['evaltype'] - constructed_data['formula'] = _constructed_evaltype['formula'] + constructed_data["evaltype"] = _constructed_evaltype["evaltype"] + constructed_data["formula"] = _constructed_evaltype["formula"] return zabbix_utils.helper_cleanup_data(constructed_data) @@ -1723,300 +1911,308 @@ def main(): argument_spec = zabbix_utils.zabbix_common_argument_spec() argument_spec.update(dict( - esc_period=dict(type='str', required=False), - name=dict(type='str', required=True), - event_source=dict(type='str', required=False, choices=['trigger', 'discovery', 'auto_registration', 'internal']), - state=dict(type='str', required=False, default='present', choices=['present', 'absent']), - status=dict(type='str', required=False, default='enabled', choices=['enabled', 'disabled']), - pause_in_maintenance=dict(type='bool', required=False, default=True), - default_message=dict(type='str', required=False, default=''), - default_subject=dict(type='str', required=False, default=''), - recovery_default_message=dict(type='str', required=False, default=''), - recovery_default_subject=dict(type='str', required=False, default=''), - acknowledge_default_message=dict(type='str', required=False, default=''), - acknowledge_default_subject=dict(type='str', required=False, default=''), + esc_period=dict(type="str", required=False), + name=dict(type="str", required=True), + event_source=dict(type="str", required=False, choices=["trigger", "discovery", "auto_registration", "internal"]), + state=dict(type="str", required=False, default="present", choices=["present", "absent"]), + status=dict(type="str", required=False, default="enabled", choices=["enabled", "disabled"]), + pause_in_maintenance=dict(type="bool", required=False, default=True), conditions=dict( - type='list', + type="list", required=False, default=[], - elements='dict', + elements="dict", options=dict( - formulaid=dict(type='str', required=False), - operator=dict(type='str', required=True), - type=dict(type='str', required=True), - value=dict(type='str', required=False), - value2=dict(type='str', required=False) + formulaid=dict(type="str", required=False), + operator=dict( + type="str", + required=True, + choices=[ + "equals", + "=", + "does not equal", + "<>", + "contains", + "like", + "does not contain", + "not like", + "in", + "is greater than or equals", + ">=", + "is less than or equals", + "<=", + "not in", + "matches", + "does not match", + "Yes", + "No" + ] + ), + type=dict(type="str", required=True), + value=dict(type="str", required=False), + value2=dict(type="str", required=False) ), required_if=[ - ['type', 'event_tag_value', ['value2']], + ["type", "event_tag_value", ["value2"]], ] ), - formula=dict(type='str', required=False, default=None), - eval_type=dict(type='str', required=False, default=None, choices=['andor', 'and', 'or', 'custom_expression']), + formula=dict(type="str", required=False, default=None), + eval_type=dict(type="str", required=False, default=None, choices=["andor", "and", "or", "custom_expression"]), operations=dict( - type='list', + type="list", required=False, default=[], - elements='dict', + elements="dict", options=dict( type=dict( - type='str', + type="str", required=True, choices=[ - 'send_message', - 'remote_command', - 'add_host', - 'remove_host', - 'add_to_host_group', - 'remove_from_host_group', - 'link_to_template', - 'unlink_from_template', - 'enable_host', - 'disable_host', - 'set_host_inventory_mode', + "send_message", + "remote_command", + "add_host", + "remove_host", + "add_to_host_group", + "remove_from_host_group", + "link_to_template", + "unlink_from_template", + "enable_host", + "disable_host", + "set_host_inventory_mode", + "notify_all_involved" ] ), - esc_period=dict(type='str', required=False), - esc_step_from=dict(type='int', required=False, default=1), - esc_step_to=dict(type='int', required=False, default=1), + esc_period=dict(type="str", required=False, default="0s"), + esc_step_from=dict(type="int", required=False, default=1), + esc_step_to=dict(type="int", required=False, default=1), operation_condition=dict( - type='str', + type="str", required=False, default=None, - choices=['acknowledged', 'not_acknowledged'] + choices=["acknowledged", "not_acknowledged"] ), # when type is remote_command command_type=dict( - type='str', + type="str", required=False, choices=[ - 'custom_script', - 'ipmi', - 'ssh', - 'telnet', - 'global_script' + "custom_script", + "ipmi", + "ssh", + "telnet", + "global_script" ] ), - command=dict(type='str', required=False), + command=dict(type="str", required=False), execute_on=dict( - type='str', + type="str", required=False, - choices=['agent', 'server', 'proxy'] + choices=["agent", "server", "proxy"] ), - password=dict(type='str', required=False, no_log=True), - port=dict(type='int', required=False), - run_on_groups=dict(type='list', required=False), - run_on_hosts=dict(type='list', required=False), - script_name=dict(type='str', required=False), - ssh_auth_type=dict(type='str', required=False, choices=['password', 'public_key']), - ssh_privatekey_file=dict(type='str', required=False), - ssh_publickey_file=dict(type='str', required=False), - username=dict(type='str', required=False), + password=dict(type="str", required=False, no_log=True), + port=dict(type="int", required=False), + run_on_groups=dict(type="list", required=False, elements="str"), + run_on_hosts=dict(type="list", required=False, elements="str"), + script_name=dict(type="str", required=False), + ssh_auth_type=dict(type="str", required=False, choices=["password", "public_key"]), + ssh_privatekey_file=dict(type="str", required=False), + ssh_publickey_file=dict(type="str", required=False), + username=dict(type="str", required=False), # when type is send_message - media_type=dict(type='str', required=False), - subject=dict(type='str', required=False), - message=dict(type='str', required=False), - send_to_groups=dict(type='list', required=False), - send_to_users=dict(type='list', required=False), + media_type=dict(type="str", required=False, default="all"), + subject=dict(type="str", required=False), + op_message=dict(type="str", required=False), + send_to_groups=dict(type="list", required=False, elements="str"), + send_to_users=dict(type="list", required=False, elements="str"), # when type is add_to_host_group or remove_from_host_group - host_groups=dict(type='list', required=False), + host_groups=dict(type="list", required=False, elements="str"), # when type is set_host_inventory_mode - inventory=dict(type='str', required=False, choices=['manual', 'automatic']), + inventory=dict(type="str", required=False, choices=["manual", "automatic"]), # when type is link_to_template or unlink_from_template - templates=dict(type='list', required=False) + templates=dict(type="list", required=False, elements="str") ), required_if=[ - ['type', 'remote_command', ['command_type']], - ['type', 'remote_command', ['run_on_groups', 'run_on_hosts'], True], - ['command_type', 'custom_script', ['command', 'execute_on']], - ['command_type', 'ipmi', ['command']], - ['command_type', 'ssh', ['command', 'ssh_auth_type']], - ['ssh_auth_type', 'password', ['username', 'password']], - ['ssh_auth_type', 'public_key', ['username', 'ssh_privatekey_file', 'ssh_publickey_file']], - ['command_type', 'telnet', ['command', 'username', 'password']], - ['command_type', 'global_script', ['script_name']], - ['type', 'add_to_host_group', ['host_groups']], - ['type', 'remove_from_host_group', ['host_groups']], - ['type', 'link_to_template', ['templates']], - ['type', 'unlink_from_template', ['templates']], - ['type', 'set_host_inventory_mode', ['inventory']], - ['type', 'send_message', ['send_to_users', 'send_to_groups'], True] + ["type", "remote_command", ["command_type"]], + ["type", "remote_command", ["run_on_groups", "run_on_hosts"], True], + ["command_type", "custom_script", ["command", "execute_on"]], + ["command_type", "ipmi", ["command"]], + ["command_type", "ssh", ["command", "ssh_auth_type"]], + ["ssh_auth_type", "password", ["username", "password"]], + ["ssh_auth_type", "public_key", ["username", "ssh_privatekey_file", "ssh_publickey_file"]], + ["command_type", "telnet", ["command", "username", "password"]], + ["command_type", "global_script", ["script_name"]], + ["type", "add_to_host_group", ["host_groups"]], + ["type", "remove_from_host_group", ["host_groups"]], + ["type", "link_to_template", ["templates"]], + ["type", "unlink_from_template", ["templates"]], + ["type", "set_host_inventory_mode", ["inventory"]], + ["type", "send_message", ["send_to_users", "send_to_groups"], True] ] ), recovery_operations=dict( - type='list', + type="list", required=False, default=[], - elements='dict', + elements="dict", options=dict( type=dict( - type='str', + type="str", required=True, choices=[ - 'send_message', - 'remote_command', - 'notify_all_involved' + "send_message", + "remote_command", + "notify_all_involved" ] ), # when type is remote_command command_type=dict( - type='str', + type="str", required=False, choices=[ - 'custom_script', - 'ipmi', - 'ssh', - 'telnet', - 'global_script' + "custom_script", + "ipmi", + "ssh", + "telnet", + "global_script" ] ), - command=dict(type='str', required=False), + command=dict(type="str", required=False), execute_on=dict( - type='str', + type="str", required=False, - choices=['agent', 'server', 'proxy'] + choices=["agent", "server", "proxy"] ), - password=dict(type='str', required=False, no_log=True), - port=dict(type='int', required=False), - run_on_groups=dict(type='list', required=False), - run_on_hosts=dict(type='list', required=False), - script_name=dict(type='str', required=False), - ssh_auth_type=dict(type='str', required=False, choices=['password', 'public_key']), - ssh_privatekey_file=dict(type='str', required=False), - ssh_publickey_file=dict(type='str', required=False), - username=dict(type='str', required=False), + password=dict(type="str", required=False, no_log=True), + port=dict(type="int", required=False), + run_on_groups=dict(type="list", required=False, elements="str"), + run_on_hosts=dict(type="list", required=False, elements="str"), + script_name=dict(type="str", required=False), + ssh_auth_type=dict(type="str", required=False, choices=["password", "public_key"]), + ssh_privatekey_file=dict(type="str", required=False), + ssh_publickey_file=dict(type="str", required=False), + username=dict(type="str", required=False), # when type is send_message - media_type=dict(type='str', required=False), - subject=dict(type='str', required=False), - message=dict(type='str', required=False), - send_to_groups=dict(type='list', required=False), - send_to_users=dict(type='list', required=False), + media_type=dict(type="str", required=False, default="all"), + subject=dict(type="str", required=False), + op_message=dict(type="str", required=False), + send_to_groups=dict(type="list", required=False, elements="str"), + send_to_users=dict(type="list", required=False, elements="str"), ), required_if=[ - ['type', 'remote_command', ['command_type']], - ['type', 'remote_command', [ - 'run_on_groups', - 'run_on_hosts' + ["type", "remote_command", ["command_type"]], + ["type", "remote_command", [ + "run_on_groups", + "run_on_hosts" ], True], - ['command_type', 'custom_script', [ - 'command', - 'execute_on' + ["command_type", "custom_script", [ + "command", + "execute_on" ]], - ['command_type', 'ipmi', ['command']], - ['command_type', 'ssh', ['command', 'ssh_auth_type']], - ['ssh_auth_type', 'password', ['username', 'password']], - ['ssh_auth_type', 'public_key', ['username', 'ssh_privatekey_file', 'ssh_publickey_file']], - ['command_type', 'telnet', ['command', 'username', 'password']], - ['command_type', 'global_script', ['script_name']], - ['type', 'send_message', ['send_to_users', 'send_to_groups'], True] + ["command_type", "ipmi", ["command"]], + ["command_type", "ssh", ["command", "ssh_auth_type"]], + ["ssh_auth_type", "password", ["username", "password"]], + ["ssh_auth_type", "public_key", ["username", "ssh_privatekey_file", "ssh_publickey_file"]], + ["command_type", "telnet", ["command", "username", "password"]], + ["command_type", "global_script", ["script_name"]], + ["type", "send_message", ["send_to_users", "send_to_groups"], True] ] ), acknowledge_operations=dict( - type='list', + type="list", required=False, default=[], - elements='dict', - aliases=['update_operations'], + elements="dict", + aliases=["update_operations"], options=dict( type=dict( - type='str', + type="str", required=True, choices=[ - 'send_message', - 'remote_command', - 'notify_all_involved' + "send_message", + "remote_command", + "notify_all_involved" ] ), # when type is remote_command command_type=dict( - type='str', + type="str", required=False, choices=[ - 'custom_script', - 'ipmi', - 'ssh', - 'telnet', - 'global_script' + "custom_script", + "ipmi", + "ssh", + "telnet", + "global_script" ] ), - command=dict(type='str', required=False), + command=dict(type="str", required=False), execute_on=dict( - type='str', + type="str", required=False, - choices=['agent', 'server', 'proxy'] + choices=["agent", "server", "proxy"] ), - password=dict(type='str', required=False, no_log=True), - port=dict(type='int', required=False), - run_on_groups=dict(type='list', required=False), - run_on_hosts=dict(type='list', required=False), - script_name=dict(type='str', required=False), - ssh_auth_type=dict(type='str', required=False, choices=['password', 'public_key']), - ssh_privatekey_file=dict(type='str', required=False), - ssh_publickey_file=dict(type='str', required=False), - username=dict(type='str', required=False), + password=dict(type="str", required=False, no_log=True), + port=dict(type="int", required=False), + run_on_groups=dict(type="list", required=False, elements="str"), + run_on_hosts=dict(type="list", required=False, elements="str"), + script_name=dict(type="str", required=False), + ssh_auth_type=dict(type="str", required=False, choices=["password", "public_key"]), + ssh_privatekey_file=dict(type="str", required=False), + ssh_publickey_file=dict(type="str", required=False), + username=dict(type="str", required=False), # when type is send_message - media_type=dict(type='str', required=False), - subject=dict(type='str', required=False), - message=dict(type='str', required=False), - send_to_groups=dict(type='list', required=False), - send_to_users=dict(type='list', required=False), + media_type=dict(type="str", required=False, default="all"), + subject=dict(type="str", required=False), + op_message=dict(type="str", required=False), + send_to_groups=dict(type="list", required=False, elements="str"), + send_to_users=dict(type="list", required=False, elements="str"), ), required_if=[ - ['type', 'remote_command', ['command_type']], - ['type', 'remote_command', [ - 'run_on_groups', - 'run_on_hosts' + ["type", "remote_command", ["command_type"]], + ["type", "remote_command", [ + "run_on_groups", + "run_on_hosts" ], True], - ['command_type', 'custom_script', [ - 'command', - 'execute_on' + ["command_type", "custom_script", [ + "command", + "execute_on" ]], - ['command_type', 'ipmi', ['command']], - ['command_type', 'ssh', ['command', 'ssh_auth_type']], - ['ssh_auth_type', 'password', ['username', 'password']], - ['ssh_auth_type', 'public_key', ['username', 'ssh_privatekey_file', 'ssh_publickey_file']], - ['command_type', 'telnet', ['command', 'username', 'password']], - ['command_type', 'global_script', ['script_name']], - ['type', 'send_message', ['send_to_users', 'send_to_groups'], True] + ["command_type", "ipmi", ["command"]], + ["command_type", "ssh", ["command", "ssh_auth_type"]], + ["ssh_auth_type", "password", ["username", "password"]], + ["ssh_auth_type", "public_key", ["username", "ssh_privatekey_file", "ssh_publickey_file"]], + ["command_type", "telnet", ["command", "username", "password"]], + ["command_type", "global_script", ["script_name"]], + ["type", "send_message", ["send_to_users", "send_to_groups"], True] ] ), - pause_symptoms=dict(type='bool', required=False, default=True) + pause_symptoms=dict(type="bool", required=False, default=True), + notify_if_canceled=dict(type="bool", required=False, default=True) )) module = AnsibleModule( argument_spec=argument_spec, required_if=[ - ['state', 'present', [ - 'event_source' + ["state", "present", [ + "event_source" ]] ], supports_check_mode=True ) - zabbix_utils.require_creds_params(module) - - for p in ['server_url', 'login_user', 'login_password', 'timeout', 'validate_certs']: - if p in module.params and not module.params[p] is None: - module.warn('Option "%s" is deprecated with the move to httpapi connection and will be removed in the next release' % p) - - name = module.params['name'] - esc_period = module.params['esc_period'] - event_source = module.params['event_source'] - state = module.params['state'] - status = module.params['status'] - pause_in_maintenance = module.params['pause_in_maintenance'] - default_message = module.params['default_message'] - default_subject = module.params['default_subject'] - recovery_default_message = module.params['recovery_default_message'] - recovery_default_subject = module.params['recovery_default_subject'] - acknowledge_default_message = module.params['acknowledge_default_message'] - acknowledge_default_subject = module.params['acknowledge_default_subject'] - conditions = module.params['conditions'] - formula = module.params['formula'] - eval_type = module.params['eval_type'] - operations = module.params['operations'] - recovery_operations = module.params['recovery_operations'] - acknowledge_operations = module.params['acknowledge_operations'] - pause_symptoms = module.params['pause_symptoms'] + name = module.params["name"] + esc_period = module.params["esc_period"] + event_source = module.params["event_source"] + state = module.params["state"] + status = module.params["status"] + pause_in_maintenance = module.params["pause_in_maintenance"] + conditions = module.params["conditions"] + formula = module.params["formula"] + eval_type = module.params["eval_type"] + operations = module.params["operations"] + recovery_operations = module.params["recovery_operations"] + acknowledge_operations = module.params["acknowledge_operations"] + pause_symptoms = module.params["pause_symptoms"] + notify_if_canceled = module.params["notify_if_canceled"] zapi_wrapper = Zapi(module) action = Action(module) @@ -2028,7 +2224,7 @@ def main(): fltr = Filter(module, zapi_wrapper) if action_exists: - action_id = zapi_wrapper.get_action_by_name(name)['actionid'] + action_id = zapi_wrapper.get_action_by_name(name)["actionid"] if state == "absent": result = action.delete_action(action_id) module.exit_json(changed=True, msg="Action Deleted: %s, ID: %s" % (name, result)) @@ -2040,24 +2236,16 @@ def main(): esc_period=esc_period, status=status, pause_in_maintenance=pause_in_maintenance, - default_message=default_message, - default_subject=default_subject, - recovery_default_message=recovery_default_message, - recovery_default_subject=recovery_default_subject, - acknowledge_default_message=acknowledge_default_message, - acknowledge_default_subject=acknowledge_default_subject, operations=ops.construct_the_data(operations, event_source), recovery_operations=recovery_ops.construct_the_data(recovery_operations), - conditions=fltr.construct_the_data(eval_type, formula, conditions) + conditions=fltr.construct_the_data(eval_type, formula, conditions), + notify_if_canceled=notify_if_canceled ) - if LooseVersion(zapi_wrapper._zbx_api_version) >= LooseVersion('6.4'): - kwargs['pause_symptoms'] = pause_symptoms + if LooseVersion(zapi_wrapper._zbx_api_version) >= LooseVersion("6.4"): + kwargs["pause_symptoms"] = pause_symptoms - if LooseVersion(zapi_wrapper._zbx_api_version) >= LooseVersion('6.0'): - kwargs[argument_spec['acknowledge_operations']['aliases'][0]] = acknowledge_ops.construct_the_data(acknowledge_operations) - else: - kwargs['acknowledge_operations'] = acknowledge_ops.construct_the_data(acknowledge_operations) + kwargs[argument_spec["acknowledge_operations"]["aliases"][0]] = acknowledge_ops.construct_the_data(acknowledge_operations) difference = action.check_difference(**kwargs) @@ -2079,28 +2267,20 @@ def main(): esc_period=esc_period, status=status, pause_in_maintenance=pause_in_maintenance, - default_message=default_message, - default_subject=default_subject, - recovery_default_message=recovery_default_message, - recovery_default_subject=recovery_default_subject, - acknowledge_default_message=acknowledge_default_message, - acknowledge_default_subject=acknowledge_default_subject, operations=ops.construct_the_data(operations, event_source), recovery_operations=recovery_ops.construct_the_data(recovery_operations), - conditions=fltr.construct_the_data(eval_type, formula, conditions) + conditions=fltr.construct_the_data(eval_type, formula, conditions), + notify_if_canceled=notify_if_canceled ) - if LooseVersion(zapi_wrapper._zbx_api_version) >= LooseVersion('6.0'): - kwargs[argument_spec['acknowledge_operations']['aliases'][0]] = acknowledge_ops.construct_the_data(acknowledge_operations) - else: - kwargs['acknowledge_operations'] = acknowledge_ops.construct_the_data(acknowledge_operations) + kwargs[argument_spec["acknowledge_operations"]["aliases"][0]] = acknowledge_ops.construct_the_data(acknowledge_operations) - if LooseVersion(zapi_wrapper._zbx_api_version) >= LooseVersion('6.4'): - kwargs['pause_symptoms'] = pause_symptoms + if LooseVersion(zapi_wrapper._zbx_api_version) >= LooseVersion("6.4"): + kwargs["pause_symptoms"] = pause_symptoms action_id = action.add_action(**kwargs) module.exit_json(changed=True, msg="Action created: %s, ID: %s" % (name, action_id)) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/ansible_collections/community/zabbix/plugins/modules/zabbix_api_info.py b/ansible_collections/community/zabbix/plugins/modules/zabbix_api_info.py new file mode 100644 index 000000000..5941408e9 --- /dev/null +++ b/ansible_collections/community/zabbix/plugins/modules/zabbix_api_info.py @@ -0,0 +1,108 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# +# Copyright: (c) 2022, ONODERA Masaru <masaru-onodera@ieee.org> +# 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 +__metaclass__ = type + + +DOCUMENTATION = ''' +--- +module: zabbix_api_info + +short_description: Retrieve Zabbix API info + +description: + - This module allows you to retrieve Zabbix api info. + +author: + - ONODERA Masaru(@masa-orca) + +requirements: + - "python >= 3.9" + +version_added: 2.1.0 + +extends_documentation_fragment: + - community.zabbix.zabbix +''' + +EXAMPLES = ''' +# If you want to use Username and Password to be authenticated by Zabbix Server +- name: Set credentials to access Zabbix Server API + ansible.builtin.set_fact: + ansible_user: Admin + ansible_httpapi_pass: zabbix + +# If you want to use API token to be authenticated by Zabbix Server +# https://www.zabbix.com/documentation/current/en/manual/web_interface/frontend_sections/administration/general#api-tokens +- name: Set API token + ansible.builtin.set_fact: + ansible_zabbix_auth_key: 8ec0d52432c15c91fcafe9888500cf9a607f44091ab554dbee860f6b44fac895 + +- name: Retrieve API information + # set task level variables as we change ansible_connection plugin here + vars: + ansible_network_os: community.zabbix.zabbix + ansible_connection: httpapi + ansible_httpapi_port: 443 + ansible_httpapi_use_ssl: true + ansible_httpapi_validate_certs: false + ansible_zabbix_url_path: 'zabbixeu' # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu + ansible_host: zabbix-example-fqdn.org + community.zabbix.zabbix_api_info: + register: zbx_api_info +''' + +RETURN = ''' +api: + description: Summaries of Zabbix API info + returned: success + type: dict + contains: + version: + description: API version + type: str + sample: 6.0.18 +''' + +from ansible.module_utils.basic import AnsibleModule +from ansible.module_utils.compat.version import LooseVersion + +from ansible_collections.community.zabbix.plugins.module_utils.base import ZabbixBase +import ansible_collections.community.zabbix.plugins.module_utils.helpers as zabbix_utils + + +class ApiInfo(ZabbixBase): + def __init__(self, module, zbx=None, zapi_wrapper=None): + super(ApiInfo, self).__init__(module, zbx, zapi_wrapper) + if LooseVersion(self._zbx_api_version) < LooseVersion('2.2.0'): + module.fail_json(msg="This module doesn't support Zabbix versions lower than 2.2.0") + + def get_api_info(self): + if self._module.check_mode: + self._module.exit_json(changed=False) + try: + api = { + 'version': self._zbx_api_version + } + self._module.exit_json(api=api) + except Exception as e: + self._module.fail_json(msg="Failed to retrieve API info: %s" % e) + + +def main(): + argument_spec = zabbix_utils.zabbix_common_argument_spec() + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True + ) + + api_info = ApiInfo(module) + api_info.get_api_info() + + +if __name__ == '__main__': + main() diff --git a/ansible_collections/community/zabbix/plugins/modules/zabbix_authentication.py b/ansible_collections/community/zabbix/plugins/modules/zabbix_authentication.py index 51fb3e5b9..f8eaf8e19 100644 --- a/ansible_collections/community/zabbix/plugins/modules/zabbix_authentication.py +++ b/ansible_collections/community/zabbix/plugins/modules/zabbix_authentication.py @@ -5,10 +5,11 @@ # 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 + __metaclass__ = type -DOCUMENTATION = ''' +DOCUMENTATION = """ --- module: zabbix_authentication @@ -21,7 +22,7 @@ author: - ONODERA Masaru(@masa-orca) requirements: - - "python >= 2.6" + - "python >= 3.9" version_added: 1.6.0 @@ -257,24 +258,21 @@ options: type: list elements: str -notes: - - Zabbix 5.4 version and higher are supported. - extends_documentation_fragment: - community.zabbix.zabbix -''' +""" -EXAMPLES = ''' +EXAMPLES = """ # If you want to use Username and Password to be authenticated by Zabbix Server - name: Set credentials to access Zabbix Server API - set_fact: + ansible.builtin.set_fact: ansible_user: Admin ansible_httpapi_pass: zabbix # If you want to use API token to be authenticated by Zabbix Server # https://www.zabbix.com/documentation/current/en/manual/web_interface/frontend_sections/administration/general#api-tokens - name: Set API token - set_fact: + ansible.builtin.set_fact: ansible_zabbix_auth_key: 8ec0d52432c15c91fcafe9888500cf9a607f44091ab554dbee860f6b44fac895 - name: Update all authentication setting (Zabbix <= 6.0) @@ -285,9 +283,9 @@ EXAMPLES = ''' ansible_httpapi_port: 443 ansible_httpapi_use_ssl: true ansible_httpapi_validate_certs: false - ansible_zabbix_url_path: 'zabbixeu' # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu + ansible_zabbix_url_path: "zabbixeu" # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu ansible_host: zabbix-example-fqdn.org - zabbix_authentication: + community.zabbix.zabbix_authentication: authentication_type: internal http_auth_enabled: true http_login_form: zabbix_login_form @@ -296,20 +294,20 @@ EXAMPLES = ''' - any http_case_sensitive: true ldap_configured: true - ldap_host: 'ldap://localhost' + ldap_host: "ldap://localhost" ldap_port: 389 - ldap_base_dn: 'ou=Users,ou=system' - ldap_search_attribute: 'uid' - ldap_bind_dn: 'uid=ldap_search,ou=system' + ldap_base_dn: "ou=Users,ou=system" + ldap_search_attribute: "uid" + ldap_bind_dn: "uid=ldap_search,ou=system" ldap_case_sensitive: true - ldap_bind_password: 'password' + ldap_bind_password: "password" saml_auth_enabled: true - saml_idp_entityid: '' - saml_sso_url: 'https://localhost/SAML2/SSO' - saml_slo_url: 'https://localhost/SAML2/SLO' - saml_username_attribute: 'uid' - saml_sp_entityid: 'https://localhost' - saml_nameid_format: 'urn:oasis:names:tc:SAML:2.0:nameid-format:entity' + saml_idp_entityid: "" + saml_sso_url: "https://localhost/SAML2/SSO" + saml_slo_url: "https://localhost/SAML2/SLO" + saml_username_attribute: "uid" + saml_sp_entityid: "https://localhost" + saml_nameid_format: "urn:oasis:names:tc:SAML:2.0:nameid-format:entity" saml_sign_messages: true saml_sign_assertions: true saml_sign_authn_requests: true @@ -333,9 +331,9 @@ EXAMPLES = ''' ansible_httpapi_port: 443 ansible_httpapi_use_ssl: true ansible_httpapi_validate_certs: false - ansible_zabbix_url_path: 'zabbixeu' # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu + ansible_zabbix_url_path: "zabbixeu" # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu ansible_host: zabbix-example-fqdn.org - zabbix_authentication: + community.zabbix.zabbix_authentication: authentication_type: internal http_auth_enabled: true http_login_form: zabbix_login_form @@ -346,12 +344,12 @@ EXAMPLES = ''' ldap_configured: true ldap_case_sensitive: true saml_auth_enabled: true - saml_idp_entityid: '' - saml_sso_url: 'https://localhost/SAML2/SSO' - saml_slo_url: 'https://localhost/SAML2/SLO' - saml_username_attribute: 'uid' - saml_sp_entityid: 'https://localhost' - saml_nameid_format: 'urn:oasis:names:tc:SAML:2.0:nameid-format:entity' + saml_idp_entityid: "" + saml_sso_url: "https://localhost/SAML2/SSO" + saml_slo_url: "https://localhost/SAML2/SLO" + saml_username_attribute: "uid" + saml_sp_entityid: "https://localhost" + saml_nameid_format: "urn:oasis:names:tc:SAML:2.0:nameid-format:entity" saml_sign_messages: true saml_sign_assertions: true saml_sign_authn_requests: true @@ -375,9 +373,9 @@ EXAMPLES = ''' ansible_httpapi_port: 443 ansible_httpapi_use_ssl: true ansible_httpapi_validate_certs: false - ansible_zabbix_url_path: 'zabbixeu' # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu + ansible_zabbix_url_path: "zabbixeu" # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu ansible_host: zabbix-example-fqdn.org - zabbix_authentication: + community.zabbix.zabbix_authentication: authentication_type: internal http_auth_enabled: true http_login_form: zabbix_login_form @@ -386,6 +384,7 @@ EXAMPLES = ''' - any http_case_sensitive: true ldap_auth_enabled: true + ldap_userdirectory: TestUserDirectory ldap_case_sensitive: true saml_auth_enabled: true saml_case_sensitive: true @@ -399,15 +398,15 @@ EXAMPLES = ''' - contain_digits - contain_special_characters - avoid_easy_to_guess -''' +""" -RETURN = ''' +RETURN = """ msg: description: The result of the operation returned: success type: str - sample: 'Successfully update authentication setting' -''' + sample: "Successfully update authentication setting" +""" from ansible.module_utils.basic import AnsibleModule @@ -417,251 +416,291 @@ import ansible_collections.community.zabbix.plugins.module_utils.helpers as zabb class Authentication(ZabbixBase): - def __init__(self, module, zbx=None, zapi_wrapper=None): - super(Authentication, self).__init__(module, zbx, zapi_wrapper) - if LooseVersion(self._zbx_api_version) < LooseVersion('5.4.0'): - module.fail_json(msg="This module doesn't support Zabbix versions lower than 5.4.0") # get authentication setting def get_authentication(self): try: - return self._zapi.authentication.get({'output': 'extend'}) + return self._zapi.authentication.get({"output": "extend"}) except Exception as e: self._module.fail_json(msg="Failed to get authentication setting: %s" % e) # update authentication setting def update_authentication( - self, - current_authentication, - authentication_type, - http_auth_enabled, - http_login_form, - http_strip_domains, - http_case_sensitive, - ldap_configured, - ldap_auth_enabled, - ldap_host, - ldap_port, - ldap_base_dn, - ldap_search_attribute, - ldap_bind_dn, - ldap_case_sensitive, - ldap_bind_password, - ldap_userdirectory, - saml_auth_enabled, - saml_idp_entityid, - saml_sso_url, - saml_slo_url, - saml_username_attribute, - saml_sp_entityid, - saml_nameid_format, - saml_sign_messages, - saml_sign_assertions, - saml_sign_authn_requests, - saml_sign_logout_requests, - saml_sign_logout_responses, - saml_encrypt_nameid, - saml_encrypt_assertions, - saml_case_sensitive, - passwd_min_length, - passwd_check_rules, - ldap_jit_status, - saml_jit_status, - jit_provision_interval, - disabled_usrgroup): + self, + current_authentication, + authentication_type, + http_auth_enabled, + http_login_form, + http_strip_domains, + http_case_sensitive, + ldap_configured, + ldap_auth_enabled, + ldap_host, + ldap_port, + ldap_base_dn, + ldap_search_attribute, + ldap_bind_dn, + ldap_case_sensitive, + ldap_bind_password, + ldap_userdirectory, + saml_auth_enabled, + saml_idp_entityid, + saml_sso_url, + saml_slo_url, + saml_username_attribute, + saml_sp_entityid, + saml_nameid_format, + saml_sign_messages, + saml_sign_assertions, + saml_sign_authn_requests, + saml_sign_logout_requests, + saml_sign_logout_responses, + saml_encrypt_nameid, + saml_encrypt_assertions, + saml_case_sensitive, + passwd_min_length, + passwd_check_rules, + ldap_jit_status, + saml_jit_status, + jit_provision_interval, + disabled_usrgroup, + ): try: params = {} if authentication_type: - params['authentication_type'] = str(zabbix_utils.helper_to_numeric_value( - ['internal', 'ldap'], - authentication_type - )) + params["authentication_type"] = str( + zabbix_utils.helper_to_numeric_value( + ["internal", "ldap"], authentication_type + ) + ) if isinstance(http_auth_enabled, bool): - params['http_auth_enabled'] = str(int(http_auth_enabled)) + params["http_auth_enabled"] = str(int(http_auth_enabled)) if http_login_form: - params['http_login_form'] = str(zabbix_utils.helper_to_numeric_value( - ['zabbix_login_form', 'http_login_form'], - http_login_form - )) + params["http_login_form"] = str( + zabbix_utils.helper_to_numeric_value( + ["zabbix_login_form", "http_login_form"], http_login_form + ) + ) if http_strip_domains: - params['http_strip_domains'] = ','.join(http_strip_domains) + params["http_strip_domains"] = ",".join(http_strip_domains) if isinstance(http_case_sensitive, bool): - params['http_case_sensitive'] = str(int(http_case_sensitive)) + params["http_case_sensitive"] = str(int(http_case_sensitive)) - if LooseVersion(self._zbx_api_version) < LooseVersion('6.4'): + if LooseVersion(self._zbx_api_version) < LooseVersion("6.4"): if isinstance(ldap_configured, bool): - params['ldap_configured'] = str(int(ldap_configured)) + params["ldap_configured"] = str(int(ldap_configured)) else: if isinstance(ldap_auth_enabled, bool): - params['ldap_auth_enabled'] = str(int(ldap_auth_enabled)) + params["ldap_auth_enabled"] = str(int(ldap_auth_enabled)) - if LooseVersion(self._zbx_api_version) < LooseVersion('6.2.0'): + if LooseVersion(self._zbx_api_version) < LooseVersion("6.2"): if ldap_host: - params['ldap_host'] = ldap_host + params["ldap_host"] = ldap_host if ldap_port: - params['ldap_port'] = str(ldap_port) + params["ldap_port"] = str(ldap_port) if ldap_base_dn: - params['ldap_base_dn'] = ldap_base_dn + params["ldap_base_dn"] = ldap_base_dn if ldap_search_attribute: - params['ldap_search_attribute'] = ldap_search_attribute + params["ldap_search_attribute"] = ldap_search_attribute if ldap_bind_dn: - params['ldap_bind_dn'] = ldap_bind_dn + params["ldap_bind_dn"] = ldap_bind_dn if ldap_bind_password: - params['ldap_bind_password'] = ldap_bind_password + params["ldap_bind_password"] = ldap_bind_password else: if ldap_userdirectory: - directory = self._zapi.userdirectory.get({'search': {'name': ldap_userdirectory}}) + directory = self._zapi.userdirectory.get( + {"search": {"name": ldap_userdirectory}} + ) if not directory: - self._module.fail_json(msg="Canot find user directory with name: %s" % ldap_userdirectory) - params['ldap_userdirectoryid'] = directory[0]['userdirectoryid'] + self._module.fail_json( + msg="Canot find user directory with name: %s" + % ldap_userdirectory + ) + params["ldap_userdirectoryid"] = directory[0]["userdirectoryid"] if isinstance(ldap_case_sensitive, bool): - params['ldap_case_sensitive'] = str(int(ldap_case_sensitive)) + params["ldap_case_sensitive"] = str(int(ldap_case_sensitive)) if isinstance(saml_auth_enabled, bool): - params['saml_auth_enabled'] = str(int(saml_auth_enabled)) + params["saml_auth_enabled"] = str(int(saml_auth_enabled)) - if LooseVersion(self._zbx_api_version) < LooseVersion('6.4'): + if LooseVersion(self._zbx_api_version) < LooseVersion("6.4"): if saml_idp_entityid: - params['saml_idp_entityid'] = saml_idp_entityid + params["saml_idp_entityid"] = saml_idp_entityid if saml_sso_url: - params['saml_sso_url'] = saml_sso_url + params["saml_sso_url"] = saml_sso_url if saml_slo_url: - params['saml_slo_url'] = saml_slo_url + params["saml_slo_url"] = saml_slo_url if saml_username_attribute: - params['saml_username_attribute'] = saml_username_attribute + params["saml_username_attribute"] = saml_username_attribute if saml_sp_entityid: - params['saml_sp_entityid'] = saml_sp_entityid + params["saml_sp_entityid"] = saml_sp_entityid if saml_nameid_format: - params['saml_nameid_format'] = saml_nameid_format + params["saml_nameid_format"] = saml_nameid_format if isinstance(saml_sign_messages, bool): - params['saml_sign_messages'] = str(int(saml_sign_messages)) + params["saml_sign_messages"] = str(int(saml_sign_messages)) if isinstance(saml_sign_assertions, bool): - params['saml_sign_assertions'] = str(int(saml_sign_assertions)) + params["saml_sign_assertions"] = str(int(saml_sign_assertions)) if isinstance(saml_sign_authn_requests, bool): - params['saml_sign_authn_requests'] = str(int(saml_sign_authn_requests)) + params["saml_sign_authn_requests"] = str( + int(saml_sign_authn_requests) + ) if isinstance(saml_sign_logout_requests, bool): - params['saml_sign_logout_requests'] = str(int(saml_sign_logout_requests)) + params["saml_sign_logout_requests"] = str( + int(saml_sign_logout_requests) + ) if isinstance(saml_sign_logout_responses, bool): - params['saml_sign_logout_responses'] = str(int(saml_sign_logout_responses)) + params["saml_sign_logout_responses"] = str( + int(saml_sign_logout_responses) + ) if isinstance(saml_encrypt_nameid, bool): - params['saml_encrypt_nameid'] = str(int(saml_encrypt_nameid)) + params["saml_encrypt_nameid"] = str(int(saml_encrypt_nameid)) if isinstance(saml_encrypt_assertions, bool): - params['saml_encrypt_assertions'] = str(int(saml_encrypt_assertions)) + params["saml_encrypt_assertions"] = str( + int(saml_encrypt_assertions) + ) if isinstance(saml_case_sensitive, bool): - params['saml_case_sensitive'] = str(int(saml_case_sensitive)) + params["saml_case_sensitive"] = str(int(saml_case_sensitive)) else: if isinstance(ldap_jit_status, bool): - params['ldap_jit_status'] = str(int(ldap_jit_status)) + params["ldap_jit_status"] = str(int(ldap_jit_status)) if isinstance(saml_jit_status, bool): - params['saml_jit_status'] = str(int(saml_jit_status)) + params["saml_jit_status"] = str(int(saml_jit_status)) if isinstance(jit_provision_interval, str): - params['jit_provision_interval'] = jit_provision_interval + params["jit_provision_interval"] = jit_provision_interval if isinstance(disabled_usrgroup, str): - usrgrpids = self._zapi.usergroup.get({'filter': {'name': disabled_usrgroup}}) + usrgrpids = self._zapi.usergroup.get( + {"filter": {"name": disabled_usrgroup}} + ) if not usrgrpids: - self._module.fail_json('User group "%s" cannot be found' % disabled_usrgroup) - params['disabled_usrgrpid'] = str(int(usrgrpids[0]['usrgrpid'])) + self._module.fail_json( + "User group '%s' cannot be found" % disabled_usrgroup + ) + params["disabled_usrgrpid"] = str(int(usrgrpids[0]["usrgrpid"])) if (ldap_jit_status or saml_jit_status) and not disabled_usrgroup: - self._module.fail_json('"disabled_usrgroup" must be provided if "ldap_jit_status" or "saml_jit_status" enabled') + self._module.fail_json( + "'disabled_usrgroup' must be provided if 'ldap_jit_status' or 'saml_jit_status' enabled" + ) if passwd_min_length: - if LooseVersion(self._zbx_api_version) < LooseVersion('6.0'): - self._module.warn('passwd_min_length is ignored with Zabbix 5.4.') - elif passwd_min_length < 1 or passwd_min_length > 70: + if passwd_min_length < 1 or passwd_min_length > 70: self._module.fail_json(msg="Please set 0-70 to passwd_min_length.") else: - params['passwd_min_length'] = str(passwd_min_length) + params["passwd_min_length"] = str(passwd_min_length) if passwd_check_rules: - if LooseVersion(self._zbx_api_version) < LooseVersion('6.0'): - self._module.warn('passwd_check_rules is ignored with Zabbix 5.4.') - else: - passwd_check_rules_values = [ - 'contain_uppercase_and_lowercase_letters', - 'contain_digits', - 'contain_special_characters', - 'avoid_easy_to_guess' - ] - params['passwd_check_rules'] = 0 - if isinstance(passwd_check_rules, str): - if passwd_check_rules not in passwd_check_rules_values: - self._module.fail_json(msg="%s is invalid value for passwd_check_rules." % passwd_check_rules) - params['passwd_check_rules'] += 2 ** zabbix_utils.helper_to_numeric_value( - passwd_check_rules_values, passwd_check_rules + passwd_check_rules_values = [ + "contain_uppercase_and_lowercase_letters", + "contain_digits", + "contain_special_characters", + "avoid_easy_to_guess", + ] + params["passwd_check_rules"] = 0 + if isinstance(passwd_check_rules, str): + if passwd_check_rules not in passwd_check_rules_values: + self._module.fail_json( + msg="%s is invalid value for passwd_check_rules." + % passwd_check_rules ) - elif isinstance(passwd_check_rules, list): - for _passwd_check_rules_value in passwd_check_rules: - if _passwd_check_rules_value not in passwd_check_rules_values: - self._module.fail_json(msg="%s is invalid value for passwd_check_rules." % _passwd_check_rules_value) - params['passwd_check_rules'] += 2 ** zabbix_utils.helper_to_numeric_value( + params[ + "passwd_check_rules" + ] += 2 ** zabbix_utils.helper_to_numeric_value( + passwd_check_rules_values, passwd_check_rules + ) + elif isinstance(passwd_check_rules, list): + for _passwd_check_rules_value in passwd_check_rules: + if ( + _passwd_check_rules_value + not in passwd_check_rules_values + ): + self._module.fail_json( + msg="%s is invalid value for passwd_check_rules." + % _passwd_check_rules_value + ) + params[ + "passwd_check_rules" + ] += 2 ** zabbix_utils.helper_to_numeric_value( passwd_check_rules_values, _passwd_check_rules_value ) - params['passwd_check_rules'] = str(params['passwd_check_rules']) + params["passwd_check_rules"] = str(params["passwd_check_rules"]) future_authentication = current_authentication.copy() future_authentication.update(params) - if LooseVersion(self._zbx_api_version) < LooseVersion('6.4'): - if (current_authentication['ldap_configured'] == '0' - and future_authentication['ldap_configured'] == '1'): - if LooseVersion(self._zbx_api_version) < LooseVersion('6.2.0'): - if (not ldap_host - or not ldap_port - or not ldap_search_attribute - or not ldap_base_dn): + if LooseVersion(self._zbx_api_version) < LooseVersion("6.4"): + if ( + current_authentication["ldap_configured"] == "0" + and future_authentication["ldap_configured"] == "1" + ): + if LooseVersion(self._zbx_api_version) < LooseVersion("6.2"): + if ( + not ldap_host + or not ldap_port + or not ldap_search_attribute + or not ldap_base_dn + ): self._module.fail_json( msg="Please set ldap_host, ldap_search_attribute and ldap_base_dn when you change a value of ldap_configured to true." ) else: if not ldap_userdirectory: - self._module.fail_json(msg="Please set ldap_userdirectory when you change a value of ldap_configured to true.") + self._module.fail_json( + msg="Please set ldap_userdirectory when you change a value of ldap_configured to true." + ) else: - if (current_authentication['ldap_auth_enabled'] == "0" - and future_authentication['ldap_auth_enabled'] == "1"): + if ( + current_authentication["ldap_auth_enabled"] == "0" + and future_authentication["ldap_auth_enabled"] == "1" + ): if not ldap_userdirectory: - self._module.fail_json(msg="Please set ldap_userdirectory when you change a value of ldap_auth_enabled to true.") - - if LooseVersion(self._zbx_api_version) < LooseVersion('6.4'): - if (current_authentication['saml_auth_enabled'] == '0' - and future_authentication['saml_auth_enabled'] == '1' - and not saml_idp_entityid - and not saml_sso_url - and not saml_username_attribute - and not saml_sp_entityid): + self._module.fail_json( + msg="Please set ldap_userdirectory when you change a value of ldap_auth_enabled to true." + ) + + if LooseVersion(self._zbx_api_version) < LooseVersion("6.4"): + if ( + current_authentication["saml_auth_enabled"] == "0" + and future_authentication["saml_auth_enabled"] == "1" + and not saml_idp_entityid + and not saml_sso_url + and not saml_username_attribute + and not saml_sp_entityid + ): self._module.fail_json( - msg=' '.join([ - "Please set saml_idp_entityid, saml_sso_url, saml_username_attribute and saml_sp_entityid", - "when you change a value of saml_auth_enabled to true." - ]) + msg=" ".join( + [ + "Please set saml_idp_entityid, saml_sso_url, saml_username_attribute and saml_sp_entityid", + "when you change a value of saml_auth_enabled to true.", + ] + ) ) if future_authentication != current_authentication: @@ -669,100 +708,101 @@ class Authentication(ZabbixBase): self._module.exit_json(changed=True) self._zapi.authentication.update(params) - self._module.exit_json(changed=True, result="Successfully update authentication setting") + self._module.exit_json( + changed=True, result="Successfully update authentication setting" + ) else: - self._module.exit_json(changed=False, result="Authentication setting is already up to date") + self._module.exit_json( + changed=False, result="Authentication setting is already up to date" + ) except Exception as e: - self._module.fail_json(msg="Failed to update authentication setting, Exception: %s" % e) + self._module.fail_json( + msg="Failed to update authentication setting, Exception: %s" % e + ) def main(): argument_spec = zabbix_utils.zabbix_common_argument_spec() - argument_spec.update(dict( - authentication_type=dict(type='str', choices=['internal', 'ldap']), - http_auth_enabled=dict(type='bool'), - http_login_form=dict(type='str', choices=['zabbix_login_form', 'http_login_form']), - http_strip_domains=dict(type='list', elements='str'), - http_case_sensitive=dict(type='bool'), - ldap_configured=dict(type='bool'), - ldap_auth_enabled=dict(type='bool'), - ldap_host=dict(type='str'), - ldap_port=dict(type='int'), - ldap_base_dn=dict(type='str'), - ldap_search_attribute=dict(type='str'), - ldap_bind_dn=dict(type='str'), - ldap_case_sensitive=dict(type='bool'), - ldap_bind_password=dict(type='str', no_log=True), - ldap_userdirectory=dict(type='str'), - ldap_jit_status=dict(type='bool'), - saml_auth_enabled=dict(type='bool'), - saml_idp_entityid=dict(type='str'), - saml_sso_url=dict(type='str'), - saml_slo_url=dict(type='str'), - saml_username_attribute=dict(type='str'), - saml_sp_entityid=dict(type='str'), - saml_nameid_format=dict(type='str'), - saml_sign_messages=dict(type='bool'), - saml_sign_assertions=dict(type='bool'), - saml_sign_authn_requests=dict(type='bool'), - saml_sign_logout_requests=dict(type='bool'), - saml_sign_logout_responses=dict(type='bool'), - saml_encrypt_nameid=dict(type='bool'), - saml_encrypt_assertions=dict(type='bool'), - saml_case_sensitive=dict(type='bool'), - saml_jit_status=dict(type='bool'), - jit_provision_interval=dict(type='str', default='1h'), - disabled_usrgroup=dict(type='str'), - passwd_min_length=dict(type='int', no_log=False), - passwd_check_rules=dict(type='list', elements='str', no_log=False) - )) - module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True + argument_spec.update( + dict( + authentication_type=dict(type="str", choices=["internal", "ldap"]), + http_auth_enabled=dict(type="bool"), + http_login_form=dict( + type="str", choices=["zabbix_login_form", "http_login_form"] + ), + http_strip_domains=dict(type="list", elements="str"), + http_case_sensitive=dict(type="bool"), + ldap_configured=dict(type="bool"), + ldap_auth_enabled=dict(type="bool"), + ldap_host=dict(type="str"), + ldap_port=dict(type="int"), + ldap_base_dn=dict(type="str"), + ldap_search_attribute=dict(type="str"), + ldap_bind_dn=dict(type="str"), + ldap_case_sensitive=dict(type="bool"), + ldap_bind_password=dict(type="str", no_log=True), + ldap_userdirectory=dict(type="str"), + ldap_jit_status=dict(type="bool"), + saml_auth_enabled=dict(type="bool"), + saml_idp_entityid=dict(type="str"), + saml_sso_url=dict(type="str"), + saml_slo_url=dict(type="str"), + saml_username_attribute=dict(type="str"), + saml_sp_entityid=dict(type="str"), + saml_nameid_format=dict(type="str"), + saml_sign_messages=dict(type="bool"), + saml_sign_assertions=dict(type="bool"), + saml_sign_authn_requests=dict(type="bool"), + saml_sign_logout_requests=dict(type="bool"), + saml_sign_logout_responses=dict(type="bool"), + saml_encrypt_nameid=dict(type="bool"), + saml_encrypt_assertions=dict(type="bool"), + saml_case_sensitive=dict(type="bool"), + saml_jit_status=dict(type="bool"), + jit_provision_interval=dict(type="str", default="1h"), + disabled_usrgroup=dict(type="str"), + passwd_min_length=dict(type="int", no_log=False), + passwd_check_rules=dict(type="list", elements="str", no_log=False), + ) ) - - zabbix_utils.require_creds_params(module) - - for p in ['server_url', 'login_user', 'login_password', 'timeout', 'validate_certs']: - if p in module.params and not module.params[p] is None: - module.warn('Option "%s" is deprecated with the move to httpapi connection and will be removed in the next release' % p) - - authentication_type = module.params['authentication_type'] - http_auth_enabled = module.params['http_auth_enabled'] - http_login_form = module.params['http_login_form'] - http_strip_domains = module.params['http_strip_domains'] - http_case_sensitive = module.params['http_case_sensitive'] - ldap_configured = module.params['ldap_configured'] - ldap_auth_enabled = module.params['ldap_auth_enabled'] - ldap_host = module.params['ldap_host'] - ldap_port = module.params['ldap_port'] - ldap_base_dn = module.params['ldap_base_dn'] - ldap_search_attribute = module.params['ldap_search_attribute'] - ldap_bind_dn = module.params['ldap_bind_dn'] - ldap_case_sensitive = module.params['ldap_case_sensitive'] - ldap_bind_password = module.params['ldap_bind_password'] - ldap_userdirectory = module.params['ldap_userdirectory'] - saml_auth_enabled = module.params['saml_auth_enabled'] - saml_idp_entityid = module.params['saml_idp_entityid'] - saml_sso_url = module.params['saml_sso_url'] - saml_slo_url = module.params['saml_slo_url'] - saml_username_attribute = module.params['saml_username_attribute'] - saml_sp_entityid = module.params['saml_sp_entityid'] - saml_nameid_format = module.params['saml_nameid_format'] - saml_sign_messages = module.params['saml_sign_messages'] - saml_sign_assertions = module.params['saml_sign_assertions'] - saml_sign_authn_requests = module.params['saml_sign_authn_requests'] - saml_sign_logout_requests = module.params['saml_sign_logout_requests'] - saml_sign_logout_responses = module.params['saml_sign_logout_responses'] - saml_encrypt_nameid = module.params['saml_encrypt_nameid'] - saml_encrypt_assertions = module.params['saml_encrypt_assertions'] - saml_case_sensitive = module.params['saml_case_sensitive'] - passwd_min_length = module.params['passwd_min_length'] - passwd_check_rules = module.params['passwd_check_rules'] - ldap_jit_status = module.params['ldap_jit_status'] - saml_jit_status = module.params['saml_jit_status'] - jit_provision_interval = module.params['jit_provision_interval'] - disabled_usrgroup = module.params['disabled_usrgroup'] + module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True) + + authentication_type = module.params["authentication_type"] + http_auth_enabled = module.params["http_auth_enabled"] + http_login_form = module.params["http_login_form"] + http_strip_domains = module.params["http_strip_domains"] + http_case_sensitive = module.params["http_case_sensitive"] + ldap_configured = module.params["ldap_configured"] + ldap_auth_enabled = module.params["ldap_auth_enabled"] + ldap_host = module.params["ldap_host"] + ldap_port = module.params["ldap_port"] + ldap_base_dn = module.params["ldap_base_dn"] + ldap_search_attribute = module.params["ldap_search_attribute"] + ldap_bind_dn = module.params["ldap_bind_dn"] + ldap_case_sensitive = module.params["ldap_case_sensitive"] + ldap_bind_password = module.params["ldap_bind_password"] + ldap_userdirectory = module.params["ldap_userdirectory"] + saml_auth_enabled = module.params["saml_auth_enabled"] + saml_idp_entityid = module.params["saml_idp_entityid"] + saml_sso_url = module.params["saml_sso_url"] + saml_slo_url = module.params["saml_slo_url"] + saml_username_attribute = module.params["saml_username_attribute"] + saml_sp_entityid = module.params["saml_sp_entityid"] + saml_nameid_format = module.params["saml_nameid_format"] + saml_sign_messages = module.params["saml_sign_messages"] + saml_sign_assertions = module.params["saml_sign_assertions"] + saml_sign_authn_requests = module.params["saml_sign_authn_requests"] + saml_sign_logout_requests = module.params["saml_sign_logout_requests"] + saml_sign_logout_responses = module.params["saml_sign_logout_responses"] + saml_encrypt_nameid = module.params["saml_encrypt_nameid"] + saml_encrypt_assertions = module.params["saml_encrypt_assertions"] + saml_case_sensitive = module.params["saml_case_sensitive"] + passwd_min_length = module.params["passwd_min_length"] + passwd_check_rules = module.params["passwd_check_rules"] + ldap_jit_status = module.params["ldap_jit_status"] + saml_jit_status = module.params["saml_jit_status"] + jit_provision_interval = module.params["jit_provision_interval"] + disabled_usrgroup = module.params["disabled_usrgroup"] authentication = Authentication(module) @@ -804,9 +844,9 @@ def main(): ldap_jit_status, saml_jit_status, jit_provision_interval, - disabled_usrgroup + disabled_usrgroup, ) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/ansible_collections/community/zabbix/plugins/modules/zabbix_autoregister.py b/ansible_collections/community/zabbix/plugins/modules/zabbix_autoregister.py index c8bf35538..c00f52cbe 100644 --- a/ansible_collections/community/zabbix/plugins/modules/zabbix_autoregister.py +++ b/ansible_collections/community/zabbix/plugins/modules/zabbix_autoregister.py @@ -8,7 +8,7 @@ from __future__ import absolute_import, division, print_function __metaclass__ = type -DOCUMENTATION = ''' +DOCUMENTATION = """ --- module: zabbix_autoregister @@ -22,7 +22,7 @@ author: - ONODERA Masaru(@masa-orca) requirements: - - "python >= 2.6" + - "python >= 3.9" version_added: 1.6.0 @@ -47,7 +47,6 @@ options: type: str notes: - - Only Zabbix >= 4.4 is supported. - This module returns changed=true when any value is set in I(tls_psk_identity) or I(tls_psk) as Zabbix API will not return any sensitive information back for module to compare. - Please note that this module configures B(global Zabbix Server settings). @@ -57,19 +56,19 @@ notes: extends_documentation_fragment: - community.zabbix.zabbix -''' +""" -EXAMPLES = ''' +EXAMPLES = """ # If you want to use Username and Password to be authenticated by Zabbix Server - name: Set credentials to access Zabbix Server API - set_fact: + ansible.builtin.set_fact: ansible_user: Admin ansible_httpapi_pass: zabbix # If you want to use API token to be authenticated by Zabbix Server # https://www.zabbix.com/documentation/current/en/manual/web_interface/frontend_sections/administration/general#api-tokens - name: Set API token - set_fact: + ansible.builtin.set_fact: ansible_zabbix_auth_key: 8ec0d52432c15c91fcafe9888500cf9a607f44091ab554dbee860f6b44fac895 - name: Update autoregistration @@ -80,16 +79,13 @@ EXAMPLES = ''' ansible_httpapi_port: 443 ansible_httpapi_use_ssl: true ansible_httpapi_validate_certs: false - ansible_zabbix_url_path: 'zabbixeu' # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu + ansible_zabbix_url_path: "zabbixeu" # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu ansible_host: zabbix-example-fqdn.org community.zabbix.zabbix_autoregister: - server_url: "http://zabbix.example.com/zabbix/" - login_user: Admin - login_password: secret tls_accept: - unsecure - tls_with_psk - tls_psk_identity: 'PSK 001' + tls_psk_identity: "PSK 001" tls_psk: "11111595725ac58dd977beef14b97461a7c1045b9a1c923453302c5473193478" - name: Set unsecure to tls_accept @@ -100,42 +96,32 @@ EXAMPLES = ''' ansible_httpapi_port: 443 ansible_httpapi_use_ssl: true ansible_httpapi_validate_certs: false - ansible_zabbix_url_path: 'zabbixeu' # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu + ansible_zabbix_url_path: "zabbixeu" # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu ansible_host: zabbix-example-fqdn.org community.zabbix.zabbix_autoregister: - server_url: "http://zabbix.example.com/zabbix/" - login_user: Admin - login_password: secret tls_accept: unsecure -''' +""" -RETURN = ''' +RETURN = """ msg: description: The result of the operation returned: success type: str - sample: 'Successfully updated global autoregistration setting' -''' + sample: "Successfully updated global autoregistration setting" +""" from ansible.module_utils.basic import AnsibleModule - from ansible_collections.community.zabbix.plugins.module_utils.base import ZabbixBase -from ansible.module_utils.compat.version import LooseVersion - import ansible_collections.community.zabbix.plugins.module_utils.helpers as zabbix_utils class Autoregistration(ZabbixBase): - def __init__(self, module, zbx=None, zapi_wrapper=None): - super(Autoregistration, self).__init__(module, zbx, zapi_wrapper) - if LooseVersion(self._zbx_api_version) < LooseVersion('4.4.0'): - module.fail_json(msg="This module doesn't support Zabbix versions lower than 4.4.0") # get autoregistration def get_autoregistration(self): try: - return self._zapi.autoregistration.get({"output": 'extend'}) + return self._zapi.autoregistration.get({"output": "extend"}) except Exception as e: self._module.fail_json(msg="Failed to get autoregistration: %s" % e) @@ -143,38 +129,38 @@ class Autoregistration(ZabbixBase): def update_autoregistration(self, current_setting, tls_accept, tls_psk_identity, tls_psk): tls_accept_values = [ None, - 'unsecure', - 'tls_with_psk' + "unsecure", + "tls_with_psk" ] params = {} try: if isinstance(tls_accept, str): - params['tls_accept'] = zabbix_utils.helper_to_numeric_value( + params["tls_accept"] = zabbix_utils.helper_to_numeric_value( tls_accept_values, tls_accept ) elif isinstance(tls_accept, list): - params['tls_accept'] = 0 + params["tls_accept"] = 0 for _tls_accept_value in tls_accept: - params['tls_accept'] += zabbix_utils.helper_to_numeric_value( + params["tls_accept"] += zabbix_utils.helper_to_numeric_value( tls_accept_values, _tls_accept_value ) else: self._module.fail_json(msg="Value of tls_accept must be list or string.") if tls_psk_identity: - params['tls_psk_identity'] = tls_psk_identity + params["tls_psk_identity"] = tls_psk_identity if tls_psk: - params['tls_psk'] = tls_psk + params["tls_psk"] = tls_psk - current_tls_accept = int(current_setting['tls_accept']) - if (current_tls_accept == tls_accept_values.index('unsecure') - and params['tls_accept'] >= tls_accept_values.index('tls_with_psk')): + current_tls_accept = int(current_setting["tls_accept"]) + if (current_tls_accept == tls_accept_values.index("unsecure") + and params["tls_accept"] >= tls_accept_values.index("tls_with_psk")): if not tls_psk_identity or not tls_psk: self._module.fail_json(msg="Please set tls_psk_identity and tls_psk.") if (not tls_psk_identity and not tls_psk - and params['tls_accept'] == current_tls_accept): + and params["tls_accept"] == current_tls_accept): self._module.exit_json(changed=False, result="Autoregistration is already up to date") if self._module.check_mode: @@ -192,32 +178,26 @@ def main(): argument_spec = zabbix_utils.zabbix_common_argument_spec() argument_spec.update(dict( tls_accept=dict( - type='list', - elements='str', + type="list", + elements="str", required=True ), - tls_psk_identity=dict(type='str', required=False, no_log=True), - tls_psk=dict(type='str', required=False, no_log=True), + tls_psk_identity=dict(type="str", required=False, no_log=True), + tls_psk=dict(type="str", required=False, no_log=True), )) module = AnsibleModule( argument_spec=argument_spec, supports_check_mode=True ) - zabbix_utils.require_creds_params(module) - - for p in ['server_url', 'login_user', 'login_password', 'timeout', 'validate_certs']: - if p in module.params and not module.params[p] is None: - module.warn('Option "%s" is deprecated with the move to httpapi connection and will be removed in the next release' % p) - - tls_accept = module.params['tls_accept'] - tls_psk_identity = module.params['tls_psk_identity'] - tls_psk = module.params['tls_psk'] + tls_accept = module.params["tls_accept"] + tls_psk_identity = module.params["tls_psk_identity"] + tls_psk = module.params["tls_psk"] autoregistration_class_obj = Autoregistration(module) current_setting = autoregistration_class_obj.get_autoregistration() autoregistration_class_obj.update_autoregistration(current_setting, tls_accept, tls_psk_identity, tls_psk) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/ansible_collections/community/zabbix/plugins/modules/zabbix_discovery_rule.py b/ansible_collections/community/zabbix/plugins/modules/zabbix_discovery_rule.py index 11b2f7c7f..2296f3db8 100644 --- a/ansible_collections/community/zabbix/plugins/modules/zabbix_discovery_rule.py +++ b/ansible_collections/community/zabbix/plugins/modules/zabbix_discovery_rule.py @@ -7,7 +7,7 @@ from __future__ import absolute_import, division, print_function __metaclass__ = type -DOCUMENTATION = r''' +DOCUMENTATION = r""" --- module: zabbix_discovery_rule short_description: Create/delete/update Zabbix discovery rules @@ -18,7 +18,7 @@ description: author: - "Tobias Birkefeld (@tcraxs)" requirements: - - "python >= 2.6" + - "python >= 3.9" options: state: description: @@ -46,22 +46,22 @@ options: description: - Type of check. type: str - choices: ['SSH', - 'LDAP', - 'SMTP', - 'FTP', - 'HTTP', - 'POP', - 'NNTP', - 'IMAP', - 'TCP', - 'Zabbix', - 'SNMPv1', - 'SNMPv2', - 'ICMP', - 'SNMPv3', - 'HTTPS', - 'Telnet'] + choices: ["SSH", + "LDAP", + "SMTP", + "FTP", + "HTTP", + "POP", + "NNTP", + "IMAP", + "TCP", + "Zabbix", + "SNMPv1", + "SNMPv2", + "ICMP", + "SNMPv3", + "HTTPS", + "Telnet"] ports: description: - One or several port ranges to check separated by commas. Used for all checks except for ICMP. @@ -173,23 +173,22 @@ options: type: str default: "enabled" choices: ["enabled", "disabled"] -notes: - - Only Zabbix >= 4.0 is supported. + extends_documentation_fragment: - community.zabbix.zabbix -''' +""" -EXAMPLES = r''' +EXAMPLES = r""" # If you want to use Username and Password to be authenticated by Zabbix Server - name: Set credentials to access Zabbix Server API - set_fact: + ansible.builtin.set_fact: ansible_user: Admin ansible_httpapi_pass: zabbix # If you want to use API token to be authenticated by Zabbix Server # https://www.zabbix.com/documentation/current/en/manual/web_interface/frontend_sections/administration/general#api-tokens - name: Set API token - set_fact: + ansible.builtin.set_fact: ansible_zabbix_auth_key: 8ec0d52432c15c91fcafe9888500cf9a607f44091ab554dbee860f6b44fac895 # Base create discovery rule example @@ -201,7 +200,7 @@ EXAMPLES = r''' ansible_httpapi_port: 443 ansible_httpapi_use_ssl: true ansible_httpapi_validate_certs: false - ansible_zabbix_url_path: 'zabbixeu' # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu + ansible_zabbix_url_path: "zabbixeu" # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu ansible_host: zabbix-example-fqdn.org community.zabbix.zabbix_discovery_rule: name: ACME @@ -224,7 +223,7 @@ EXAMPLES = r''' ansible_httpapi_port: 443 ansible_httpapi_use_ssl: true ansible_httpapi_validate_certs: false - ansible_zabbix_url_path: 'zabbixeu' # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu + ansible_zabbix_url_path: "zabbixeu" # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu ansible_host: zabbix-example-fqdn.org community.zabbix.zabbix_discovery_rule: name: ACME @@ -255,35 +254,35 @@ EXAMPLES = r''' ansible_httpapi_port: 443 ansible_httpapi_use_ssl: true ansible_httpapi_validate_certs: false - ansible_zabbix_url_path: 'zabbixeu' # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu + ansible_zabbix_url_path: "zabbixeu" # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu ansible_host: zabbix-example-fqdn.org community.zabbix.zabbix_discovery_rule: name: ACME state: absent -''' +""" -RETURN = r''' +RETURN = r""" state: description: Discovery rule state at the end of execution. returned: on success type: str - sample: 'present' + sample: "present" drule: description: Discovery rule name. returned: on success type: str - sample: 'ACME' + sample: "ACME" druleid: description: Discovery rule id. returned: on success type: str - sample: '42' + sample: "42" msg: description: The result of the operation returned: always type: str - sample: 'Discovery rule created: ACME, ID: 42' -''' + sample: "Discovery rule created: ACME, ID: 42" +""" from ansible.module_utils.basic import AnsibleModule @@ -312,72 +311,71 @@ class Dchecks(ZabbixBase): constructed_data = [] for check in _dchecks: constructed_check = { - 'type': zabbix_utils.helper_to_numeric_value([ - 'SSH', - 'LDAP', - 'SMTP', - 'FTP', - 'HTTP', - 'POP', - 'NNTP', - 'IMAP', - 'TCP', - 'Zabbix', - 'SNMPv1', - 'SNMPv2', - 'ICMP', - 'SNMPv3', - 'HTTPS', - 'Telnet'], check.get('type') + "type": zabbix_utils.helper_to_numeric_value([ + "SSH", + "LDAP", + "SMTP", + "FTP", + "HTTP", + "POP", + "NNTP", + "IMAP", + "TCP", + "Zabbix", + "SNMPv1", + "SNMPv2", + "ICMP", + "SNMPv3", + "HTTPS", + "Telnet"], check.get("type") ), - 'uniq': int(check.get('uniq')) + "uniq": int(check.get("uniq")) } - if LooseVersion(self._zbx_api_version) >= LooseVersion('4.4'): - constructed_check.update({ - 'host_source': zabbix_utils.helper_to_numeric_value([ - 'None', - 'DNS', - 'IP', - 'discovery'], check.get('host_source') - ), - 'name_source': zabbix_utils.helper_to_numeric_value([ - 'None', - 'DNS', - 'IP', - 'discovery'], check.get('name_source') - ) - }) - if constructed_check['type'] in (0, 1, 2, 3, 4, 5, 6, 7, 8, 14, 15): - constructed_check['ports'] = check.get('ports') - if constructed_check['type'] == 9: - constructed_check['ports'] = check.get('ports') - constructed_check['key_'] = check.get('key') - if constructed_check['type'] in (10, 11): - constructed_check['ports'] = check.get('ports') - constructed_check['snmp_community'] = check.get('snmp_community') - constructed_check['key_'] = check.get('key') - if constructed_check['type'] == 13: - constructed_check['ports'] = check.get('ports') - constructed_check['key_'] = check.get('key') - constructed_check['snmpv3_contextname'] = check.get('snmpv3_contextname') - constructed_check['snmpv3_securityname'] = check.get('snmpv3_securityname') - constructed_check['snmpv3_securitylevel'] = zabbix_utils.helper_to_numeric_value([ - 'noAuthNoPriv', - 'authNoPriv', - 'authPriv'], check.get('snmpv3_securitylevel') + constructed_check.update({ + "host_source": zabbix_utils.helper_to_numeric_value([ + "None", + "DNS", + "IP", + "discovery"], check.get("host_source") + ), + "name_source": zabbix_utils.helper_to_numeric_value([ + "None", + "DNS", + "IP", + "discovery"], check.get("name_source") + ) + }) + if constructed_check["type"] in (0, 1, 2, 3, 4, 5, 6, 7, 8, 14, 15): + constructed_check["ports"] = check.get("ports") + if constructed_check["type"] == 9: + constructed_check["ports"] = check.get("ports") + constructed_check["key_"] = check.get("key") + if constructed_check["type"] in (10, 11): + constructed_check["ports"] = check.get("ports") + constructed_check["snmp_community"] = check.get("snmp_community") + constructed_check["key_"] = check.get("key") + if constructed_check["type"] == 13: + constructed_check["ports"] = check.get("ports") + constructed_check["key_"] = check.get("key") + constructed_check["snmpv3_contextname"] = check.get("snmpv3_contextname") + constructed_check["snmpv3_securityname"] = check.get("snmpv3_securityname") + constructed_check["snmpv3_securitylevel"] = zabbix_utils.helper_to_numeric_value([ + "noAuthNoPriv", + "authNoPriv", + "authPriv"], check.get("snmpv3_securitylevel") ) - if constructed_check['snmpv3_securitylevel'] in (1, 2): - constructed_check['snmpv3_authprotocol'] = zabbix_utils.helper_to_numeric_value([ - 'MD5', - 'SHA'], check.get('snmpv3_authprotocol') + if constructed_check["snmpv3_securitylevel"] in (1, 2): + constructed_check["snmpv3_authprotocol"] = zabbix_utils.helper_to_numeric_value([ + "MD5", + "SHA"], check.get("snmpv3_authprotocol") ) - constructed_check['snmpv3_authpassphrase'] = check.get('snmpv3_authpassphrase') - if constructed_check['snmpv3_securitylevel'] == 2: - constructed_check['snmpv3_privprotocol'] = zabbix_utils.helper_to_numeric_value([ - 'DES', - 'AES'], check.get('snmpv3_privprotocol') + constructed_check["snmpv3_authpassphrase"] = check.get("snmpv3_authpassphrase") + if constructed_check["snmpv3_securitylevel"] == 2: + constructed_check["snmpv3_privprotocol"] = zabbix_utils.helper_to_numeric_value([ + "DES", + "AES"], check.get("snmpv3_privprotocol") ) - constructed_check['snmpv3_privpassphrase'] = check.get('snmpv3_privpassphrase') + constructed_check["snmpv3_privpassphrase"] = check.get("snmpv3_privpassphrase") constructed_data.append(constructed_check) return zabbix_utils.helper_cleanup_data(constructed_data) @@ -392,9 +390,9 @@ class DiscoveryRule(ZabbixBase): """ try: _drule = self._zapi.drule.get({ - 'output': 'extend', - 'selectDChecks': 'extend', - 'filter': {'name': [name]} + "output": "extend", + "selectDChecks": "extend", + "filter": {"name": [name]} }) if len(_drule) > 0: return _drule @@ -411,9 +409,9 @@ class DiscoveryRule(ZabbixBase): """ try: drule_list = self._zapi.drule.get({ - 'output': 'extend', - 'selectDChecks': 'extend', - 'filter': {'name': [name]} + "output": "extend", + "selectDChecks": "extend", + "filter": {"name": [name]} }) if len(drule_list) < 1: self._module.fail_json(msg="Discovery rule not found: %s" % name) @@ -431,9 +429,9 @@ class DiscoveryRule(ZabbixBase): """ try: proxy_list = self._zapi.proxy.get({ - 'output': 'extend', - 'selectInterface': 'extend', - 'filter': {'host': [proxy_name]} + "output": "extend", + "selectInterface": "extend", + "filter": {"host": [proxy_name]} }) if len(proxy_list) < 1: self._module.fail_json(msg="Proxy not found: %s" % proxy_name) @@ -450,17 +448,17 @@ class DiscoveryRule(ZabbixBase): dict: dictionary of specified parameters """ _params = { - 'name': kwargs['name'], - 'iprange': ','.join(kwargs['iprange']), - 'delay': kwargs['delay'], - 'status': zabbix_utils.helper_to_numeric_value([ - 'enabled', - 'disabled'], kwargs['status'] + "name": kwargs["name"], + "iprange": ",".join(kwargs["iprange"]), + "delay": kwargs["delay"], + "status": zabbix_utils.helper_to_numeric_value([ + "enabled", + "disabled"], kwargs["status"] ), - 'dchecks': kwargs['dchecks'] + "dchecks": kwargs["dchecks"] } - if kwargs['proxy']: - _params['proxy_hostid'] = self.get_proxy_by_proxy_name(kwargs['proxy'])['proxyid'] + if kwargs["proxy"]: + _params["proxy_hostid"] = self.get_proxy_by_proxy_name(kwargs["proxy"])["proxyid"] return _params def check_difference(self, **kwargs): @@ -470,13 +468,13 @@ class DiscoveryRule(ZabbixBase): Returns: dict: dictionary of differences """ - existing_drule = zabbix_utils.helper_convert_unicode_to_str(self.check_if_drule_exists(kwargs['name'])[0]) + existing_drule = zabbix_utils.helper_convert_unicode_to_str(self.check_if_drule_exists(kwargs["name"])[0]) parameters = zabbix_utils.helper_convert_unicode_to_str(self._construct_parameters(**kwargs)) change_parameters = {} - if LooseVersion(self._zbx_api_version) < LooseVersion('6.4'): - if existing_drule['nextcheck']: - existing_drule.pop('nextcheck') - _diff = zabbix_utils.helper_cleanup_data(compare_dictionaries(parameters, existing_drule, change_parameters)) + if LooseVersion(self._zbx_api_version) < LooseVersion("6.4"): + if existing_drule["nextcheck"]: + existing_drule.pop("nextcheck") + _diff = zabbix_utils.helper_cleanup_data(zabbix_utils.helper_compare_dictionaries(parameters, existing_drule, change_parameters)) return _diff def update_drule(self, **kwargs): @@ -488,11 +486,11 @@ class DiscoveryRule(ZabbixBase): """ try: if self._module.check_mode: - self._module.exit_json(msg="Discovery rule would be updated if check mode was not specified: ID %s" % kwargs['drule_id'], changed=True) - kwargs['druleid'] = kwargs.pop('drule_id') + self._module.exit_json(msg="Discovery rule would be updated if check mode was not specified: ID %s" % kwargs["drule_id"], changed=True) + kwargs["druleid"] = kwargs.pop("drule_id") return self._zapi.drule.update(kwargs) except Exception as e: - self._module.fail_json(msg="Failed to update discovery rule ID '%s': %s" % (kwargs['drule_id'], e)) + self._module.fail_json(msg="Failed to update discovery rule ID '%s': %s" % (kwargs["drule_id"], e)) def add_drule(self, **kwargs): """Add discovery rule @@ -506,9 +504,9 @@ class DiscoveryRule(ZabbixBase): self._module.exit_json(msg="Discovery rule would be added if check mode was not specified", changed=True) parameters = self._construct_parameters(**kwargs) drule_list = self._zapi.drule.create(parameters) - return drule_list['druleids'][0] + return drule_list["druleids"][0] except Exception as e: - self._module.fail_json(msg="Failed to create discovery rule %s: %s" % (kwargs['name'], e)) + self._module.fail_json(msg="Failed to create discovery rule %s: %s" % (kwargs["name"], e)) def delete_drule(self, drule_id): """Delete discovery rule. @@ -525,159 +523,90 @@ class DiscoveryRule(ZabbixBase): self._module.fail_json(msg="Failed to delete discovery rule '%s': %s" % (drule_id, e)) -def compare_lists(l1, l2, diff_dict): - """ - Compares l1 and l2 lists and adds the items that are different - to the diff_dict dictionary. - Used in recursion with compare_dictionaries() function. - Args: - l1: first list to compare - l2: second list to compare - diff_dict: dictionary to store the difference - Returns: - dict: items that are different - """ - if len(l1) != len(l2): - diff_dict.append(l1) - return diff_dict - for i, item in enumerate(l1): - if isinstance(item, dict): - diff_dict.insert(i, {}) - diff_dict[i] = compare_dictionaries(item, l2[i], diff_dict[i]) - else: - if item != l2[i]: - diff_dict.append(item) - while {} in diff_dict: - diff_dict.remove({}) - return diff_dict - - -def compare_dictionaries(d1, d2, diff_dict): - """ - Compares d1 and d2 dictionaries and adds the items that are different - to the diff_dict dictionary. - Used in recursion with compare_lists() function. - Args: - d1: first dictionary to compare - d2: second dictionary to compare - diff_dict: dictionary to store the difference - Returns: - dict: items that are different - """ - for k, v in d1.items(): - if k not in d2: - diff_dict[k] = v - continue - if isinstance(v, dict): - diff_dict[k] = {} - compare_dictionaries(v, d2[k], diff_dict[k]) - if diff_dict[k] == {}: - del diff_dict[k] - else: - diff_dict[k] = v - elif isinstance(v, list): - diff_dict[k] = [] - compare_lists(v, d2[k], diff_dict[k]) - if diff_dict[k] == []: - del diff_dict[k] - else: - diff_dict[k] = v - else: - if v != d2[k]: - diff_dict[k] = v - return diff_dict - - def main(): argument_spec = zabbix_utils.zabbix_common_argument_spec() argument_spec.update( - name=dict(type='str', required=True), - iprange=dict(type='list', required=False, elements='str'), + name=dict(type="str", required=True), + iprange=dict(type="list", required=False, elements="str"), dchecks=dict( - type='list', + type="list", required=False, - aliases=['dcheck'], - elements='dict', + aliases=["dcheck"], + elements="dict", options=dict( - type=dict(type='str', choices=[ - 'SSH', - 'LDAP', - 'SMTP', - 'FTP', - 'HTTP', - 'POP', - 'NNTP', - 'IMAP', - 'TCP', - 'Zabbix', - 'SNMPv1', - 'SNMPv2', - 'ICMP', - 'SNMPv3', - 'HTTPS', - 'Telnet'] + type=dict(type="str", choices=[ + "SSH", + "LDAP", + "SMTP", + "FTP", + "HTTP", + "POP", + "NNTP", + "IMAP", + "TCP", + "Zabbix", + "SNMPv1", + "SNMPv2", + "ICMP", + "SNMPv3", + "HTTPS", + "Telnet"] ), - ports=dict(type='str'), - key=dict(type='str', no_log=False), - snmp_community=dict(type='str'), - snmpv3_authpassphrase=dict(type='str', no_log=True), - snmpv3_authprotocol=dict(type='str', choices=['MD5', 'SHA']), - snmpv3_contextname=dict(type='str'), - snmpv3_privpassphrase=dict(type='str', no_log=True), - snmpv3_privprotocol=dict(type='str', choices=['DES', 'AES']), - snmpv3_securitylevel=dict(type='str', choices=['noAuthNoPriv', 'authNoPriv', 'authPriv']), - snmpv3_securityname=dict(type='str'), - uniq=dict(type='bool', default=False), - host_source=dict(type='str', choices=['DNS', 'IP', 'discovery'], default='DNS'), - name_source=dict(type='str', choices=['None', 'DNS', 'IP', 'discovery'], default='None') + ports=dict(type="str"), + key=dict(type="str", no_log=False), + snmp_community=dict(type="str"), + snmpv3_authpassphrase=dict(type="str", no_log=True), + snmpv3_authprotocol=dict(type="str", choices=["MD5", "SHA"]), + snmpv3_contextname=dict(type="str"), + snmpv3_privpassphrase=dict(type="str", no_log=True), + snmpv3_privprotocol=dict(type="str", choices=["DES", "AES"]), + snmpv3_securitylevel=dict(type="str", choices=["noAuthNoPriv", "authNoPriv", "authPriv"]), + snmpv3_securityname=dict(type="str"), + uniq=dict(type="bool", default=False), + host_source=dict(type="str", choices=["DNS", "IP", "discovery"], default="DNS"), + name_source=dict(type="str", choices=["None", "DNS", "IP", "discovery"], default="None") ), required_if=[ - ['type', 'SSH', ['ports']], - ['type', 'LDAP', ['ports']], - ['type', 'SMTP', ['ports']], - ['type', 'FTP', ['ports']], - ['type', 'HTTP', ['ports']], - ['type', 'POP', ['ports']], - ['type', 'NNTP', ['ports']], - ['type', 'IMAP', ['ports']], - ['type', 'TCP', ['ports']], - ['type', 'Zabbix', ['ports', 'key']], - ['type', 'SNMPv1', ['ports', 'key', 'snmp_community']], - ['type', 'SNMPv2', ['ports', 'key', 'snmp_community']], - ['type', 'SNMPv3', ['ports', 'key']], - ['type', 'HTTPS', ['ports']], - ['type', 'Telnet', ['ports']], - ['snmpv3_securitylevel', 'authPriv', ['snmpv3_privpassphrase']] + ["type", "SSH", ["ports"]], + ["type", "LDAP", ["ports"]], + ["type", "SMTP", ["ports"]], + ["type", "FTP", ["ports"]], + ["type", "HTTP", ["ports"]], + ["type", "POP", ["ports"]], + ["type", "NNTP", ["ports"]], + ["type", "IMAP", ["ports"]], + ["type", "TCP", ["ports"]], + ["type", "Zabbix", ["ports", "key"]], + ["type", "SNMPv1", ["ports", "key", "snmp_community"]], + ["type", "SNMPv2", ["ports", "key", "snmp_community"]], + ["type", "SNMPv3", ["ports", "key"]], + ["type", "HTTPS", ["ports"]], + ["type", "Telnet", ["ports"]], + ["snmpv3_securitylevel", "authPriv", ["snmpv3_privpassphrase"]] ] ), - delay=dict(type='str', required=False, default='1h'), - proxy=dict(type='str', required=False, default=None), - status=dict(type='str', default="enabled", choices=["enabled", "disabled"]), - state=dict(type='str', default='present', choices=['present', 'absent']) + delay=dict(type="str", required=False, default="1h"), + proxy=dict(type="str", required=False, default=None), + status=dict(type="str", default="enabled", choices=["enabled", "disabled"]), + state=dict(type="str", default="present", choices=["present", "absent"]) ) module = AnsibleModule( argument_spec=argument_spec, required_if=[ - ['state', 'present', ['name', 'iprange', 'dchecks']], - ['state', 'absent', ['name']], + ["state", "present", ["name", "iprange", "dchecks"]], + ["state", "absent", ["name"]], ], supports_check_mode=True ) - zabbix_utils.require_creds_params(module) - - for p in ['server_url', 'login_user', 'login_password', 'timeout', 'validate_certs']: - if p in module.params and not module.params[p] is None: - module.warn('Option "%s" is deprecated with the move to httpapi connection and will be removed in the next release' % p) - - state = module.params['state'] - name = module.params['name'] - iprange = module.params['iprange'] - dchecks = module.params['dchecks'] - delay = module.params['delay'] - proxy = module.params['proxy'] - status = module.params['status'] + state = module.params["state"] + name = module.params["name"] + iprange = module.params["iprange"] + dchecks = module.params["dchecks"] + delay = module.params["delay"] + proxy = module.params["proxy"] + status = module.params["status"] drule = DiscoveryRule(module) zbx = drule._zapi @@ -686,7 +615,7 @@ def main(): drule_exists = drule.check_if_drule_exists(name) if drule_exists: - drule_id = drule.get_drule_by_drule_name(name)['druleid'] + drule_id = drule.get_drule_by_drule_name(name)["druleid"] if state == "absent": drule.delete_drule(drule_id) module.exit_json(changed=True, state=state, drule=name, druleid=drule_id, msg="Discovery Rule deleted: %s, ID: %s" % (name, drule_id)) @@ -724,5 +653,5 @@ def main(): module.exit_json(changed=True, state=state, drule=name, druleid=drule_id, msg="Discovery Rule created: %s, ID: %s" % (name, drule_id)) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/ansible_collections/community/zabbix/plugins/modules/zabbix_globalmacro.py b/ansible_collections/community/zabbix/plugins/modules/zabbix_globalmacro.py index d0221223b..111c80c55 100644 --- a/ansible_collections/community/zabbix/plugins/modules/zabbix_globalmacro.py +++ b/ansible_collections/community/zabbix/plugins/modules/zabbix_globalmacro.py @@ -8,7 +8,7 @@ from __future__ import absolute_import, division, print_function __metaclass__ = type -DOCUMENTATION = r''' +DOCUMENTATION = r""" --- module: zabbix_globalmacro short_description: Create/update/delete Zabbix Global macros @@ -21,7 +21,7 @@ author: - Dean Hailin Song (!UNKNOWN) - Timothy Test (@ttestscripting) requirements: - - "python >= 2.6" + - "python >= 3.9" options: macro_name: description: @@ -46,42 +46,41 @@ options: macro_description: description: - Text Description of the global macro. - - Works only with Zabbix >= 4.4 and is silently ignored in lower versions type: str - default: '' + default: "" state: description: - State of the macro. - On C(present), it will create if macro does not exist or update the macro if the associated data is different. - On C(absent) will remove a macro if it exists. required: false - choices: ['present', 'absent'] + choices: ["present", "absent"] type: str default: "present" force: description: - Only updates an existing macro if set to C(yes). - default: 'yes' + default: "yes" type: bool notes: - - This module returns changed=true when I(macro_type=secret) with Zabbix >= 5.0. + - This module returns changed=true when I(macro_type=secret). extends_documentation_fragment: - community.zabbix.zabbix -''' +""" -EXAMPLES = r''' +EXAMPLES = r""" # If you want to use Username and Password to be authenticated by Zabbix Server - name: Set credentials to access Zabbix Server API - set_fact: + ansible.builtin.set_fact: ansible_user: Admin ansible_httpapi_pass: zabbix # If you want to use API token to be authenticated by Zabbix Server # https://www.zabbix.com/documentation/current/en/manual/web_interface/frontend_sections/administration/general#api-tokens - name: Set API token - set_fact: + ansible.builtin.set_fact: ansible_zabbix_auth_key: 8ec0d52432c15c91fcafe9888500cf9a607f44091ab554dbee860f6b44fac895 - name: Create new global macro or update an existing macro's value @@ -92,7 +91,7 @@ EXAMPLES = r''' ansible_httpapi_port: 443 ansible_httpapi_use_ssl: true ansible_httpapi_validate_certs: false - ansible_zabbix_url_path: 'zabbixeu' # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu + ansible_zabbix_url_path: "zabbixeu" # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu ansible_host: zabbix-example-fqdn.org community.zabbix.zabbix_globalmacro: macro_name: EXAMPLE.MACRO @@ -109,7 +108,7 @@ EXAMPLES = r''' ansible_httpapi_port: 443 ansible_httpapi_use_ssl: true ansible_httpapi_validate_certs: false - ansible_zabbix_url_path: 'zabbixeu' # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu + ansible_zabbix_url_path: "zabbixeu" # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu ansible_host: zabbix-example-fqdn.org community.zabbix.zabbix_globalmacro: macro_name: "{$EXAMPLE.MACRO}" @@ -121,7 +120,7 @@ EXAMPLES = r''' community.zabbix.zabbix_globalmacro: macro_name: "{$EXAMPLE.MACRO}" state: absent -''' +""" RETURN = r""" """ @@ -130,7 +129,6 @@ RETURN = r""" from ansible.module_utils.basic import AnsibleModule from ansible_collections.community.zabbix.plugins.module_utils.base import ZabbixBase -from ansible.module_utils.compat.version import LooseVersion import ansible_collections.community.zabbix.plugins.module_utils.helpers as zabbix_utils @@ -139,8 +137,8 @@ class GlobalMacro(ZabbixBase): # get global macro def get_global_macro(self, macro_name): try: - all_global_macro_list = self._zapi.usermacro.get({"globalmacro": 'true'}) - global_macro_list = [d for d in all_global_macro_list if d['macro'] == macro_name] + all_global_macro_list = self._zapi.usermacro.get({"globalmacro": "true"}) + global_macro_list = [d for d in all_global_macro_list if d["macro"] == macro_name] if len(global_macro_list) > 0: return global_macro_list[0] return None @@ -152,61 +150,30 @@ class GlobalMacro(ZabbixBase): try: if self._module.check_mode: self._module.exit_json(changed=True) - if LooseVersion(self._zbx_api_version) < LooseVersion('4.4.0'): - self._zapi.usermacro.createglobal({'macro': macro_name, 'value': macro_value}) - self._module.exit_json(changed=True, result="Successfully added global macro %s" % macro_name) - if LooseVersion(self._zbx_api_version) >= LooseVersion('4.4.0'): - if LooseVersion(self._zbx_api_version) >= LooseVersion('5.0.0'): - if LooseVersion(self._zbx_api_version) < LooseVersion('5.2.0'): - if macro_type == '2': - macro_type = '0' - self._zapi.usermacro.createglobal({'macro': macro_name, 'value': macro_value, 'type': macro_type, 'description': macro_description}) - self._module.exit_json(changed=True, result="Successfully added global macro %s" % macro_name) - else: - self._zapi.usermacro.createglobal({'macro': macro_name, 'value': macro_value, 'description': macro_description}) - self._module.exit_json(changed=True, result="Successfully added global macro %s" % macro_name) + self._zapi.usermacro.createglobal({"macro": macro_name, "value": macro_value, "type": macro_type, "description": macro_description}) + self._module.exit_json(changed=True, result="Successfully added global macro %s" % macro_name) except Exception as e: self._module.fail_json(msg="Failed to create global macro %s: %s" % (macro_name, e)) # update global macro def update_global_macro(self, global_macro_obj, macro_name, macro_value, macro_type, macro_description): - global_macro_id = global_macro_obj['globalmacroid'] + global_macro_id = global_macro_obj["globalmacroid"] try: - if LooseVersion(self._zbx_api_version) < LooseVersion('4.4.0'): - if global_macro_obj['macro'] == macro_name and global_macro_obj['value'] == macro_value: + if global_macro_obj["type"] == "0" or global_macro_obj["type"] == "2": + if (global_macro_obj["macro"] == macro_name and global_macro_obj["value"] == macro_value + and global_macro_obj["type"] == macro_type and global_macro_obj["description"] == macro_description): self._module.exit_json(changed=False, result="Global macro %s already up to date" % macro_name) - if self._module.check_mode: - self._module.exit_json(changed=True) - self._zapi.usermacro.updateglobal({'globalmacroid': global_macro_id, 'macro': macro_name, 'value': macro_value}) - self._module.exit_json(changed=True, result="Successfully updated global macro %s" % macro_name) - elif LooseVersion(self._zbx_api_version) >= LooseVersion('5.0.0'): - if LooseVersion(self._zbx_api_version) < LooseVersion('5.2.0'): - if macro_type == '2': - macro_type = '0' - if global_macro_obj['type'] == '0' or global_macro_obj['type'] == '2': - if (global_macro_obj['macro'] == macro_name and global_macro_obj['value'] == macro_value - and global_macro_obj['type'] == macro_type and global_macro_obj['description'] == macro_description): - self._module.exit_json(changed=False, result="Global macro %s already up to date" % macro_name) - if self._module.check_mode: - self._module.exit_json(changed=True) - self._zapi.usermacro.updateglobal({'globalmacroid': global_macro_id, 'macro': macro_name, - 'value': macro_value, 'type': macro_type, 'description': macro_description}) - self._module.exit_json(changed=True, result="Successfully updated global macro %s" % macro_name) - else: - if (global_macro_obj['macro'] == macro_name and global_macro_obj['value'] == macro_value - and global_macro_obj['description'] == macro_description): - self._module.exit_json(changed=False, result="Global macro %s already up to date" % macro_name) - if self._module.check_mode: - self._module.exit_json(changed=True) - self._zapi.usermacro.updateglobal({'globalmacroid': global_macro_id, 'macro': macro_name, - 'value': macro_value, 'description': macro_description}) - self._module.exit_json(changed=True, result="Successfully updated global macro %s" % macro_name) + if self._module.check_mode: + self._module.exit_json(changed=True) + self._zapi.usermacro.updateglobal({"globalmacroid": global_macro_id, "macro": macro_name, + "value": macro_value, "type": macro_type, "description": macro_description}) + self._module.exit_json(changed=True, result="Successfully updated global macro %s" % macro_name) except Exception as e: self._module.fail_json(msg="Failed to update global macro %s: %s" % (macro_name, e)) # delete global macro def delete_global_macro(self, global_macro_obj, macro_name): - global_macro_id = global_macro_obj['globalmacroid'] + global_macro_id = global_macro_obj["globalmacroid"] try: if self._module.check_mode: self._module.exit_json(changed=True) @@ -218,16 +185,16 @@ class GlobalMacro(ZabbixBase): def normalize_macro_name(macro_name): # Zabbix handles macro names in upper case characters - if ':' in macro_name: - macro_name = ':'.join([macro_name.split(':')[0].upper(), ':'.join(macro_name.split(':')[1:])]) + if ":" in macro_name: + macro_name = ":".join([macro_name.split(":")[0].upper(), ":".join(macro_name.split(":")[1:])]) else: macro_name = macro_name.upper() # Valid format for macro is {$MACRO} - if not macro_name.startswith('{$'): - macro_name = '{$' + macro_name - if not macro_name.endswith('}'): - macro_name = macro_name + '}' + if not macro_name.startswith("{$"): + macro_name = "{$" + macro_name + if not macro_name.endswith("}"): + macro_name = macro_name + "}" return macro_name @@ -235,48 +202,42 @@ def normalize_macro_name(macro_name): def main(): argument_spec = zabbix_utils.zabbix_common_argument_spec() argument_spec.update(dict( - macro_name=dict(type='str', required=True), - macro_value=dict(type='str', required=False, no_log=True), - macro_type=dict(type='str', default='text', choices=['text', 'secret', 'vault']), - macro_description=dict(type='str', default=''), - state=dict(type='str', default='present', choices=['present', 'absent']), - force=dict(type='bool', default=True) + macro_name=dict(type="str", required=True), + macro_value=dict(type="str", required=False, no_log=True), + macro_type=dict(type="str", default="text", choices=["text", "secret", "vault"]), + macro_description=dict(type="str", default=""), + state=dict(type="str", default="present", choices=["present", "absent"]), + force=dict(type="bool", default=True) )) module = AnsibleModule( argument_spec=argument_spec, required_if=[ - ['state', 'present', ['macro_value']] + ["state", "present", ["macro_value"]] ], supports_check_mode=True ) - zabbix_utils.require_creds_params(module) - - for p in ['server_url', 'login_user', 'login_password', 'timeout', 'validate_certs']: - if p in module.params and not module.params[p] is None: - module.warn('Option "%s" is deprecated with the move to httpapi connection and will be removed in the next release' % p) - - macro_name = normalize_macro_name(module.params['macro_name']) - macro_value = module.params['macro_value'] - macro_type = module.params['macro_type'] - macro_value = module.params['macro_value'] - macro_description = module.params['macro_description'] - state = module.params['state'] - force = module.params['force'] - - if macro_type == 'text': - macro_type = '0' - elif macro_type == 'secret': - macro_type = '1' - elif macro_type == 'vault': - macro_type = '2' + macro_name = normalize_macro_name(module.params["macro_name"]) + macro_value = module.params["macro_value"] + macro_type = module.params["macro_type"] + macro_value = module.params["macro_value"] + macro_description = module.params["macro_description"] + state = module.params["state"] + force = module.params["force"] + + if macro_type == "text": + macro_type = "0" + elif macro_type == "secret": + macro_type = "1" + elif macro_type == "vault": + macro_type = "2" global_macro_class_obj = GlobalMacro(module) if macro_name: global_macro_obj = global_macro_class_obj.get_global_macro(macro_name) - if state == 'absent': + if state == "absent": if not global_macro_obj: module.exit_json(changed=False, msg="Global Macro %s does not exist" % macro_name) else: @@ -293,5 +254,5 @@ def main(): module.exit_json(changed=False, result="Global macro %s already exists and force is set to no" % macro_name) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/ansible_collections/community/zabbix/plugins/modules/zabbix_group.py b/ansible_collections/community/zabbix/plugins/modules/zabbix_group.py index 0426f9336..3eb39ab24 100644 --- a/ansible_collections/community/zabbix/plugins/modules/zabbix_group.py +++ b/ansible_collections/community/zabbix/plugins/modules/zabbix_group.py @@ -47,14 +47,14 @@ notes: EXAMPLES = r''' # If you want to use Username and Password to be authenticated by Zabbix Server - name: Set credentials to access Zabbix Server API - set_fact: + ansible.builtin.set_fact: ansible_user: Admin ansible_httpapi_pass: zabbix # If you want to use API token to be authenticated by Zabbix Server # https://www.zabbix.com/documentation/current/en/manual/web_interface/frontend_sections/administration/general#api-tokens - name: Set API token - set_fact: + ansible.builtin.set_fact: ansible_zabbix_auth_key: 8ec0d52432c15c91fcafe9888500cf9a607f44091ab554dbee860f6b44fac895 # Base create host groups example @@ -94,17 +94,6 @@ EXAMPLES = r''' ''' -import traceback - -try: - from zabbix_api import Already_Exists - - HAS_ZABBIX_API = True - ZBX_IMP_ERR = Exception() -except ImportError: - ZBX_IMP_ERR = traceback.format_exc() - HAS_ZABBIX_API = False - from ansible.module_utils.basic import AnsibleModule from ansible_collections.community.zabbix.plugins.module_utils.base import ZabbixBase @@ -119,13 +108,10 @@ class HostGroup(ZabbixBase): for group_name in group_names: result = self._zapi.hostgroup.get({'filter': {'name': group_name}}) if not result: - try: - if self._module.check_mode: - self._module.exit_json(changed=True) - self._zapi.hostgroup.create({'name': group_name}) - group_add_list.append(group_name) - except Already_Exists: - return group_add_list + if self._module.check_mode: + self._module.exit_json(changed=True) + self._zapi.hostgroup.create({'name': group_name}) + group_add_list.append(group_name) return group_add_list except Exception as e: self._module.fail_json(msg="Failed to create host group(s): %s" % e) @@ -153,7 +139,7 @@ class HostGroup(ZabbixBase): def main(): argument_spec = zabbix_utils.zabbix_common_argument_spec() argument_spec.update(dict( - host_groups=dict(type='list', required=True, aliases=['host_group']), + host_groups=dict(type='list', required=True, aliases=['host_group'], elements='str'), state=dict(type='str', default="present", choices=['present', 'absent']), )) module = AnsibleModule( @@ -161,12 +147,6 @@ def main(): supports_check_mode=True ) - zabbix_utils.require_creds_params(module) - - for p in ['server_url', 'login_user', 'login_password']: - if p in module.params and module.params[p] and module.params[p]: - module.warn('Option "%s" is deprecated with the move to httpapi connection and will be removed in the next release' % p) - host_groups = module.params['host_groups'] state = module.params['state'] diff --git a/ansible_collections/community/zabbix/plugins/modules/zabbix_group_events_info.py b/ansible_collections/community/zabbix/plugins/modules/zabbix_group_events_info.py new file mode 100644 index 000000000..e41cab26f --- /dev/null +++ b/ansible_collections/community/zabbix/plugins/modules/zabbix_group_events_info.py @@ -0,0 +1,283 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# built by Martin Eiswirth (@meis4h) on top of the work by Stéphane Travassac (@stravassac) on zabbix_host_events_info.py +# and Michael Miko (@RedWhiteMiko) on zabbix_group_info.py +# 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) + +__metaclass__ = type + +RETURN = """ +--- +triggers_ok: + description: Zabbix Triggers in OK state + returned: On success + type: complex + contains: + comments: + description: Additional description of the trigger + type: str + description: + description: Name of the trigger + type: str + error: + description: Error text if there have been any problems when updating the state of the trigger + type: str + expression: + description: Reduced trigger expression + type: str + flags: + description: Origin of the trigger + type: int + lastchange: + description: Time when the trigger last changed its state (timestamp) + type: int + priority: + description: Severity of the trigger + type: int + state: + description: State of the trigger + type: int + status: + description: Whether the trigger is enabled or disabled + type: int + templateid: + description: ID of the parent template trigger + type: int + triggerid: + description: ID of the trigger + type: int + type: + description: Whether the trigger can generate multiple problem events + type: int + url: + description: URL associated with the trigger + type: str + value: + description: Whether the trigger is in OK or problem state + type: int +triggers_problem: + description: Zabbix Triggers in problem state. See trigger and event objects in API documentation of your zabbix version for more + returned: On success + type: complex + contains: + comments: + description: Additional description of the trigger + type: str + description: + description: Name of the trigger + type: str + error: + description: Error text if there have been any problems when updating the state of the trigger + type: str + expression: + description: Reduced trigger expression + type: str + flags: + description: Origin of the trigger + type: int + last_event: + description: last event informations + type: complex + contains: + acknowledged: + description: If set to true return only acknowledged events + type: int + acknowledges: + description: acknowledges informations + type: complex + contains: + alias: + description: Account who acknowledge + type: str + clock: + description: Time when the event was created (timestamp) + type: int + message: + description: Text of the acknowledgement message + type: str + clock: + description: Time when the event was created (timestamp) + type: int + eventid: + description: ID of the event + type: int + value: + description: State of the related object + type: int + lastchange: + description: Time when the trigger last changed its state (timestamp) + type: int + priority: + description: Severity of the trigger + type: int + state: + description: State of the trigger + type: int + status: + description: Whether the trigger is enabled or disabled + type: int + templateid: + description: ID of the parent template trigger + type: int + triggerid: + description: ID of the trigger + type: int + type: + description: Whether the trigger can generate multiple problem events + type: int + url: + description: URL associated with the trigger + type: str + value: + description: Whether the trigger is in OK or problem state + type: int +""" + +DOCUMENTATION = """ +--- +module: zabbix_group_events_info +short_description: Get all triggers about a Zabbix group +description: + - This module allows you to check the state of triggers of all hosts in a Zabbix hostgroup. +author: + - "Martin Eiswirth (@meis4h)" +requirements: + - "python >= 3.9" +options: + hostgroup_name: + description: + - Name of the hostgroup in Zabbix. + required: true + type: list + elements: str + trigger_severity: + description: + - Zabbix severity for search filter + default: average + required: false + choices: + - not_classified + - information + - warning + - average + - high + - disaster + type: str +extends_documentation_fragment: +- community.zabbix.zabbix + +""" + +EXAMPLES = """ +# If you want to use Username and Password to be authenticated by Zabbix Server +- name: Set credentials to access Zabbix Server API + set_fact: + ansible_user: Admin + ansible_httpapi_pass: zabbix + +# If you want to use API token to be authenticated by Zabbix Server +# https://www.zabbix.com/documentation/current/en/manual/web_interface/frontend_sections/administration/general#api-tokens +- name: Set API token + set_fact: + ansible_zabbix_auth_key: 8ec0d52432c15c91fcafe9888500cf9a607f44091ab554dbee860f6b44fac895 + +- name: Fail if alert active in hostgroup + # set task level variables as we change ansible_connection plugin here + vars: + ansible_network_os: community.zabbix.zabbix + ansible_connection: httpapi + ansible_httpapi_port: 443 + ansible_httpapi_use_ssl: true + ansible_httpapi_validate_certs: false + ansible_zabbix_url_path: "zabbixeu" # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu + ansible_host: zabbix-example-fqdn.org + community.zabbix.zabbix_group_events_info: + hostgroup_name: "{{ inventory_hostname }}" + register: zbx_hostgroup + delegate_to: localhost +- fail: + msg: "Active alert in zabbix" + when: zbx_hostgroup["triggers_problem"] | length > 0 +""" + + +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.community.zabbix.plugins.module_utils.base import ZabbixBase +import ansible_collections.community.zabbix.plugins.module_utils.helpers as zabbix_utils + + +class Host(ZabbixBase): + def get_group_ids_by_group_names(self, group_names): + group_list = self._zapi.hostgroup.get({"output": "extend", "filter": {"name": group_names}}) + if len(group_list) < 1: + self._module.fail_json(msg="Hostgroup not found: %s" % group_names) + return group_list + + def get_triggers_by_group_id_in_problem_state(self, group_id, trigger_severity): + """ Get triggers in problem state from a groupid""" + output = "extend" + triggers_list = self._zapi.trigger.get({"output": output, "groupids": group_id, + "min_severity": trigger_severity}) + return triggers_list + + def get_last_event_by_trigger_id(self, triggers_id): + """ Get the last event from triggerid""" + output = ["eventid", "clock", "acknowledged", "value"] + select_acknowledges = ["clock", "alias", "message"] + event = self._zapi.event.get({"output": output, "objectids": triggers_id, + "select_acknowledges": select_acknowledges, "limit": 1, "sortfield": "clock", + "sortorder": "DESC"}) + return event[0] + + +def main(): + argument_spec = zabbix_utils.zabbix_common_argument_spec() + argument_spec.update(dict( + hostgroup_name=dict(type="list", required=True, elements="str"), + trigger_severity=dict( + type="str", + required=False, + default="average", + choices=["not_classified", "information", "warning", "average", "high", "disaster"]), + )) + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True + ) + + trigger_severity_map = {"not_classified": 0, "information": 1, "warning": 2, "average": 3, "high": 4, "disaster": 5} + trigger_severity = trigger_severity_map[module.params["trigger_severity"]] + + hostgroup_name = module.params["hostgroup_name"] + + host = Host(module) + host_groups = host.get_group_ids_by_group_names(hostgroup_name) + triggers = [] + + for host_group in host_groups: + host_group_id = host_group["groupid"] + host_group_triggers = host.get_triggers_by_group_id_in_problem_state(host_group_id, trigger_severity) + triggers = triggers + host_group_triggers + + triggers_ok = [] + triggers_problem = [] + for trigger in triggers: + # tGet last event for trigger with problem value = 1 + # https://www.zabbix.com/documentation/3.4/manual/api/reference/trigger/object + if int(trigger["value"]) == 1: + event = host.get_last_event_by_trigger_id(trigger["triggerid"]) + trigger["last_event"] = event + triggers_problem.append(trigger) + else: + triggers_ok.append(trigger) + + module.exit_json(ok=True, host_groups=host_groups, triggers_ok=triggers_ok, triggers_problem=triggers_problem) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/community/zabbix/plugins/modules/zabbix_group_facts.py b/ansible_collections/community/zabbix/plugins/modules/zabbix_group_facts.py deleted file mode 100644 index b3f693052..000000000 --- a/ansible_collections/community/zabbix/plugins/modules/zabbix_group_facts.py +++ /dev/null @@ -1,115 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# (c) me@mimiko.me -# 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 -__metaclass__ = type - - -RETURN = r''' ---- -host_groups: - description: List of Zabbix groups. - returned: success - type: dict - sample: [ { "flags": "0", "groupid": "33", "internal": "0", "name": "Hostgruup A" } ] -''' - -DOCUMENTATION = r''' ---- -module: zabbix_group_info -short_description: Gather information about Zabbix hostgroup -description: - - This module allows you to search for Zabbix hostgroup entries. - - This module was called C(zabbix_group_facts) before Ansible 2.9. The usage did not change. -author: - - "Michael Miko (@RedWhiteMiko)" -requirements: - - "python >= 2.6" -options: - hostgroup_name: - description: - - Name of the hostgroup in Zabbix. - - hostgroup is the unique identifier used and cannot be updated using this module. - required: true - type: list - elements: str - -extends_documentation_fragment: -- community.zabbix.zabbix -''' - -EXAMPLES = r''' -# If you want to use Username and Password to be authenticated by Zabbix Server -- name: Set credentials to access Zabbix Server API - set_fact: - ansible_user: Admin - ansible_httpapi_pass: zabbix - -# If you want to use API token to be authenticated by Zabbix Server -# https://www.zabbix.com/documentation/current/en/manual/web_interface/frontend_sections/administration/general#api-tokens -- name: Set API token - set_fact: - ansible_zabbix_auth_key: 8ec0d52432c15c91fcafe9888500cf9a607f44091ab554dbee860f6b44fac895 - -- name: Get hostgroup info - # set task level variables as we change ansible_connection plugin here - vars: - ansible_network_os: community.zabbix.zabbix - ansible_connection: httpapi - ansible_httpapi_port: 443 - ansible_httpapi_use_ssl: true - ansible_httpapi_validate_certs: false - ansible_zabbix_url_path: 'zabbixeu' # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu - ansible_host: zabbix-example-fqdn.org - community.zabbix.zabbix_group_info: - hostgroup_name: - - ExampleHostgroup - timeout: 10 -''' - -from ansible.module_utils.basic import AnsibleModule - -from ansible_collections.community.zabbix.plugins.module_utils.base import ZabbixBase -import ansible_collections.community.zabbix.plugins.module_utils.helpers as zabbix_utils - - -class Host(ZabbixBase): - def get_group_ids_by_group_names(self, group_names): - group_list = self._zapi.hostgroup.get({'output': 'extend', 'filter': {'name': group_names}}) - if len(group_list) < 1: - self._module.fail_json(msg="Hostgroup not found: %s" % group_names) - return group_list - - -def main(): - argument_spec = zabbix_utils.zabbix_common_argument_spec() - argument_spec.update(dict( - hostgroup_name=dict(type='list', required=True), - )) - module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True - ) - - zabbix_utils.require_creds_params(module) - - for p in ['server_url', 'login_user', 'login_password', 'timeout', 'validate_certs']: - if p in module.params and not module.params[p] is None: - module.warn('Option "%s" is deprecated with the move to httpapi connection and will be removed in the next release' % p) - - if module._name == 'zabbix_group_facts': - module.deprecate("The 'zabbix_group_facts' module has been renamed to 'zabbix_group_info'", - collection_name="community.zabbix", version='2.0.0') # was 2.13 - - hostgroup_name = module.params['hostgroup_name'] - - host = Host(module) - host_groups = host.get_group_ids_by_group_names(hostgroup_name) - module.exit_json(host_groups=host_groups) - - -if __name__ == '__main__': - main() diff --git a/ansible_collections/community/zabbix/plugins/modules/zabbix_group_info.py b/ansible_collections/community/zabbix/plugins/modules/zabbix_group_info.py index b3f693052..02784c366 100644 --- a/ansible_collections/community/zabbix/plugins/modules/zabbix_group_info.py +++ b/ansible_collections/community/zabbix/plugins/modules/zabbix_group_info.py @@ -8,16 +8,16 @@ from __future__ import absolute_import, division, print_function __metaclass__ = type -RETURN = r''' +RETURN = r""" --- host_groups: description: List of Zabbix groups. returned: success type: dict sample: [ { "flags": "0", "groupid": "33", "internal": "0", "name": "Hostgruup A" } ] -''' +""" -DOCUMENTATION = r''' +DOCUMENTATION = r""" --- module: zabbix_group_info short_description: Gather information about Zabbix hostgroup @@ -27,7 +27,7 @@ description: author: - "Michael Miko (@RedWhiteMiko)" requirements: - - "python >= 2.6" + - "python >= 3.9" options: hostgroup_name: description: @@ -39,19 +39,19 @@ options: extends_documentation_fragment: - community.zabbix.zabbix -''' +""" -EXAMPLES = r''' +EXAMPLES = r""" # If you want to use Username and Password to be authenticated by Zabbix Server - name: Set credentials to access Zabbix Server API - set_fact: + ansible.builtin.set_fact: ansible_user: Admin ansible_httpapi_pass: zabbix # If you want to use API token to be authenticated by Zabbix Server # https://www.zabbix.com/documentation/current/en/manual/web_interface/frontend_sections/administration/general#api-tokens - name: Set API token - set_fact: + ansible.builtin.set_fact: ansible_zabbix_auth_key: 8ec0d52432c15c91fcafe9888500cf9a607f44091ab554dbee860f6b44fac895 - name: Get hostgroup info @@ -62,13 +62,12 @@ EXAMPLES = r''' ansible_httpapi_port: 443 ansible_httpapi_use_ssl: true ansible_httpapi_validate_certs: false - ansible_zabbix_url_path: 'zabbixeu' # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu + ansible_zabbix_url_path: "zabbixeu" # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu ansible_host: zabbix-example-fqdn.org community.zabbix.zabbix_group_info: hostgroup_name: - ExampleHostgroup - timeout: 10 -''' +""" from ansible.module_utils.basic import AnsibleModule @@ -78,7 +77,7 @@ import ansible_collections.community.zabbix.plugins.module_utils.helpers as zabb class Host(ZabbixBase): def get_group_ids_by_group_names(self, group_names): - group_list = self._zapi.hostgroup.get({'output': 'extend', 'filter': {'name': group_names}}) + group_list = self._zapi.hostgroup.get({"output": "extend", "filter": {"name": group_names}}) if len(group_list) < 1: self._module.fail_json(msg="Hostgroup not found: %s" % group_names) return group_list @@ -87,29 +86,19 @@ class Host(ZabbixBase): def main(): argument_spec = zabbix_utils.zabbix_common_argument_spec() argument_spec.update(dict( - hostgroup_name=dict(type='list', required=True), + hostgroup_name=dict(type="list", required=True, elements="str"), )) module = AnsibleModule( argument_spec=argument_spec, supports_check_mode=True ) - zabbix_utils.require_creds_params(module) - - for p in ['server_url', 'login_user', 'login_password', 'timeout', 'validate_certs']: - if p in module.params and not module.params[p] is None: - module.warn('Option "%s" is deprecated with the move to httpapi connection and will be removed in the next release' % p) - - if module._name == 'zabbix_group_facts': - module.deprecate("The 'zabbix_group_facts' module has been renamed to 'zabbix_group_info'", - collection_name="community.zabbix", version='2.0.0') # was 2.13 - - hostgroup_name = module.params['hostgroup_name'] + hostgroup_name = module.params["hostgroup_name"] host = Host(module) host_groups = host.get_group_ids_by_group_names(hostgroup_name) module.exit_json(host_groups=host_groups) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/ansible_collections/community/zabbix/plugins/modules/zabbix_host.py b/ansible_collections/community/zabbix/plugins/modules/zabbix_host.py index c43742378..22e14d17d 100644 --- a/ansible_collections/community/zabbix/plugins/modules/zabbix_host.py +++ b/ansible_collections/community/zabbix/plugins/modules/zabbix_host.py @@ -8,7 +8,7 @@ from __future__ import absolute_import, division, print_function __metaclass__ = type -DOCUMENTATION = r''' +DOCUMENTATION = r""" --- module: zabbix_host short_description: Create/update/delete Zabbix hosts @@ -21,7 +21,7 @@ author: - Werner Dijkerman (@dj-wasabi) - Eike Frost (@eikef) requirements: - - "python >= 2.6" + - "python >= 3.9" options: host_name: description: @@ -51,27 +51,27 @@ options: inventory_mode: description: - Configure the inventory mode. - choices: ['automatic', 'manual', 'disabled'] + choices: ["automatic", "manual", "disabled"] type: str inventory_zabbix: description: - Add Facts for a zabbix inventory (e.g. Tag) (see example below). - Please review the interface documentation for more information on the supported properties - - U(https://www.zabbix.com/documentation/3.2/manual/api/reference/host/object#host_inventory) + - U(https://www.zabbix.com/documentation/current/en/manual/api/reference/host/object#host-inventory) type: dict status: description: - Monitoring status of the host. - choices: ['enabled', 'disabled'] - default: 'enabled' + choices: ["enabled", "disabled"] + default: "enabled" type: str state: description: - State of the host. - On C(present), it will create if host does not exist or update the host if the associated data is different. - On C(absent) will remove a host if it exists. - choices: ['present', 'absent'] - default: 'present' + choices: ["present", "absent"] + default: "present" type: str proxy: description: @@ -83,7 +83,7 @@ options: description: - List of interfaces to be created for the host (see example below). - For more information, review host interface documentation at - - U(https://www.zabbix.com/documentation/4.0/manual/api/reference/hostinterface/object) + - U(https://www.zabbix.com/documentation/current/en/manual/api/reference/hostinterface/object#host-interface) default: [] suboptions: type: @@ -95,7 +95,7 @@ options: - 2 = snmp - 3 = ipmi - 4 = jmx - choices: ['agent', '1', 'snmp', '2', 'ipmi', '3', 'jmx', '4'] + choices: ["agent", "1", "snmp", "2", "ipmi", "3", "jmx", "4"] required: true main: type: int @@ -127,26 +127,15 @@ options: description: - Port used by host interface. - If not specified, default port for each type of interface is used - - 10050 if I(type='agent') - - 161 if I(type='snmp') - - 623 if I(type='ipmi') - - 12345 if I(type='jmx') - bulk: - type: int - description: - - Whether to use bulk SNMP requests. - - Only valid when interface I(type='snmp'). - - 0 (don't use bulk requests), 1 (use bulk requests) - - Works only with Zabbix <= 4.4 and is silently ignored in higher versions. - - Use I(details) with Zabbix >= 5.0. - choices: [0, 1] - default: 1 + - 10050 if I(type="agent") + - 161 if I(type="snmp") + - 623 if I(type="ipmi") + - 12345 if I(type="jmx") details: type: dict description: - Additional details for SNMP host interfaces. - - Required when I(type='snmp'). - - Works only with Zabbix >= 5.0. + - Required when I(type="snmp"). default: {} suboptions: version: @@ -168,17 +157,17 @@ options: description: - SNMPv1 and SNMPv2 community string. - Required when I(version=1) or I(version=2). - default: '' + default: "" securityname: type: str description: - SNMPv3 security name. - default: '' + default: "" contextname: type: str description: - SNMPv3 context name. - default: '' + default: "" securitylevel: type: int description: @@ -200,7 +189,7 @@ options: description: - SNMPv3 authentication passphrase. - Used when I(securitylevel=1)(authNoPriv) or I(securitylevel=2)(AuthPriv). - default: '' + default: "" privprotocol: type: int description: @@ -215,12 +204,11 @@ options: description: - SNMPv3 privacy passphrase. - Used when I(securitylevel=2)(AuthPriv). - default: '' + default: "" tls_connect: description: - Specifies what encryption to use for outgoing connections. - Possible values, 1 (no encryption), 2 (PSK), 4 (certificate). - - Works only with >= Zabbix 3.0 type: int tls_accept: description: @@ -228,7 +216,6 @@ options: - The tls_accept parameter accepts values of 1 to 7 - Possible values, 1 (no encryption), 2 (PSK), 4 (certificate). - Values can be combined. - - Works only with >= Zabbix 3.0 type: int tls_psk_identity: description: @@ -241,25 +228,22 @@ options: description: - PSK value is a hard to guess string of hexadecimal digits. - The preshared key, at least 32 hex digits. Required if either I(tls_connect) or I(tls_accept) has PSK enabled. - - Works only with >= Zabbix 3.0 - - Using this parameter with Zabbix >= 5.4 makes this module non-idempotent. + - Using this parameter makes this module non-idempotent. type: str ca_cert: description: - Required certificate issuer. - - Works only with >= Zabbix 3.0 aliases: [ tls_issuer ] type: str tls_subject: description: - Required certificate subject. - - Works only with >= Zabbix 3.0 type: str ipmi_authtype: description: - IPMI authentication algorithm. - Please review the Host object documentation for more information on the supported properties - - 'https://www.zabbix.com/documentation/3.4/manual/api/reference/host/object' + - "https://www.zabbix.com/documentation/3.4/manual/api/reference/host/object" - Possible values are, C(0) (none), C(1) (MD2), C(2) (MD5), C(4) (straight), C(5) (OEM), C(6) (RMCP+), with -1 being the API default. - Please note that the Zabbix API will treat absent settings as default when updating @@ -270,7 +254,7 @@ options: description: - IPMI privilege level. - Please review the Host object documentation for more information on the supported properties - - 'https://www.zabbix.com/documentation/3.4/manual/api/reference/host/object' + - "https://www.zabbix.com/documentation/3.4/manual/api/reference/host/object" - Possible values are C(1) (callback), C(2) (user), C(3) (operator), C(4) (admin), C(5) (OEM), with C(2) being the API default. - also see the last note in the I(ipmi_authtype) documentation @@ -289,7 +273,7 @@ options: description: - Overwrite the host configuration, even if already present. type: bool - default: 'yes' + default: "yes" macros: description: - List of user macros to assign to the zabbix host. @@ -311,14 +295,12 @@ options: description: description: - Description of the user macro. - - Works only with >= Zabbix 4.4. type: str required: false - default: '' + default: "" type: description: - Type of the macro. - - Works only with >= Zabbix 5.0. - Since value is not returned by API for secret macros, there is no reliable way to detect changes in the content of secret macro value. - To update secret macro value, please update description alongside it so it passes @@ -331,7 +313,6 @@ options: tags: description: - List of host tags to assign to the zabbix host. - - Works only with >= Zabbix 4.2. - Providing I(tags=[]) with I(force=yes) will clean all of the tags from the host. type: list elements: dict @@ -345,25 +326,25 @@ options: description: - Value of the host tag. type: str - default: '' + default: "" aliases: [ host_tags ] extends_documentation_fragment: - community.zabbix.zabbix -''' +""" -EXAMPLES = r''' +EXAMPLES = r""" # If you want to use Username and Password to be authenticated by Zabbix Server - name: Set credentials to access Zabbix Server API - set_fact: + ansible.builtin.set_fact: ansible_user: Admin ansible_httpapi_pass: zabbix # If you want to use API token to be authenticated by Zabbix Server # https://www.zabbix.com/documentation/current/en/manual/web_interface/frontend_sections/administration/general#api-tokens - name: Set API token - set_fact: + ansible.builtin.set_fact: ansible_zabbix_auth_key: 8ec0d52432c15c91fcafe9888500cf9a607f44091ab554dbee860f6b44fac895 - name: Create a new host or rewrite an existing host's info @@ -374,7 +355,7 @@ EXAMPLES = r''' ansible_httpapi_port: 443 ansible_httpapi_use_ssl: true ansible_httpapi_validate_certs: false - ansible_zabbix_url_path: 'zabbixeu' # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu + ansible_zabbix_url_path: "zabbixeu" # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu become: false delegate_to: zabbix-example-fqdn.org# you can use delegate_to or task level ansible_host like next example community.zabbix.zabbix_host: @@ -417,7 +398,7 @@ EXAMPLES = r''' port: "12345" proxy: a.zabbix.proxy macros: - - macro: '{$EXAMPLEMACRO}' + - macro: "{$EXAMPLEMACRO}" value: ExampleMacroValue - macro: EXAMPLEMACRO2 value: ExampleMacroValue2 @@ -435,7 +416,7 @@ EXAMPLES = r''' ansible_httpapi_port: 443 ansible_httpapi_use_ssl: true ansible_httpapi_validate_certs: false - ansible_zabbix_url_path: 'zabbixeu' # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu + ansible_zabbix_url_path: "zabbixeu" # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu ansible_host: zabbix-example-fqdn.org # you can use task level ansible_host or delegate_to like in previous example become: false community.zabbix.zabbix_host: @@ -447,7 +428,7 @@ EXAMPLES = r''' tls_connect: 2 tls_psk: 123456789abcdef123456789abcdef12 force: false -''' +""" import copy @@ -455,7 +436,6 @@ import copy from ansible.module_utils.basic import AnsibleModule from ansible_collections.community.zabbix.plugins.module_utils.base import ZabbixBase -from ansible.module_utils.compat.version import LooseVersion import ansible_collections.community.zabbix.plugins.module_utils.helpers as zabbix_utils @@ -463,13 +443,13 @@ import ansible_collections.community.zabbix.plugins.module_utils.helpers as zabb class Host(ZabbixBase): # exist host def is_host_exist(self, host_name): - result = self._zapi.host.get({'filter': {'host': host_name}}) + result = self._zapi.host.get({"filter": {"host": host_name}}) return result # check if host group exists def check_host_group_exist(self, group_names): for group_name in group_names: - result = self._zapi.hostgroup.get({'filter': {'name': group_name}}) + result = self._zapi.hostgroup.get({"filter": {"name": group_name}}) if not result: self._module.fail_json(msg="Hostgroup not found: %s" % group_name) return True @@ -479,11 +459,11 @@ class Host(ZabbixBase): if template_list is None or len(template_list) == 0: return template_ids for template in template_list: - template_list = self._zapi.template.get({'output': 'extend', 'filter': {'host': template}}) + template_list = self._zapi.template.get({"output": "extend", "filter": {"host": template}}) if len(template_list) < 1: self._module.fail_json(msg="Template not found: %s" % template) else: - template_id = template_list[0]['templateid'] + template_id = template_list[0]["templateid"] template_ids.append(template_id) return template_ids @@ -493,83 +473,88 @@ class Host(ZabbixBase): try: if self._module.check_mode: self._module.exit_json(changed=True) - parameters = {'host': host_name, 'interfaces': interfaces, 'groups': group_ids, 'status': status} + parameters = {"host": host_name, "interfaces": interfaces, "groups": group_ids, "status": status} if proxy_id: - parameters['proxy_hostid'] = proxy_id + parameters["proxy_hostid"] = proxy_id if visible_name: - parameters['name'] = visible_name + parameters["name"] = visible_name if tls_connect: - parameters['tls_connect'] = tls_connect + parameters["tls_connect"] = tls_connect if tls_accept: - parameters['tls_accept'] = tls_accept + parameters["tls_accept"] = tls_accept if tls_psk_identity is not None: - parameters['tls_psk_identity'] = tls_psk_identity + parameters["tls_psk_identity"] = tls_psk_identity if tls_psk is not None: - parameters['tls_psk'] = tls_psk + parameters["tls_psk"] = tls_psk if tls_issuer is not None: - parameters['tls_issuer'] = tls_issuer + parameters["tls_issuer"] = tls_issuer if tls_subject is not None: - parameters['tls_subject'] = tls_subject + parameters["tls_subject"] = tls_subject if description: - parameters['description'] = description + parameters["description"] = description if ipmi_authtype is not None: - parameters['ipmi_authtype'] = ipmi_authtype + parameters["ipmi_authtype"] = ipmi_authtype if ipmi_privilege is not None: - parameters['ipmi_privilege'] = ipmi_privilege + parameters["ipmi_privilege"] = ipmi_privilege if ipmi_username is not None: - parameters['ipmi_username'] = ipmi_username + parameters["ipmi_username"] = ipmi_username if ipmi_password is not None: - parameters['ipmi_password'] = ipmi_password + parameters["ipmi_password"] = ipmi_password if macros is not None: - parameters['macros'] = macros + parameters["macros"] = macros if tags is not None: - parameters['tags'] = tags + parameters["tags"] = tags host_list = self._zapi.host.create(parameters) if len(host_list) >= 1: - return host_list['hostids'][0] + return host_list["hostids"][0] except Exception as e: self._module.fail_json(msg="Failed to create host %s: %s" % (host_name, e)) def update_host(self, host_name, group_ids, status, host_id, interfaces, exist_interface_list, proxy_id, visible_name, description, tls_connect, tls_accept, tls_psk_identity, tls_psk, tls_issuer, - tls_subject, ipmi_authtype, ipmi_privilege, ipmi_username, ipmi_password, macros, tags): + tls_subject, ipmi_authtype, ipmi_privilege, ipmi_username, ipmi_password, macros, tags, discovered_host): try: if self._module.check_mode: self._module.exit_json(changed=True) - parameters = {'hostid': host_id, 'groups': group_ids, 'status': status} - if proxy_id >= 0: - parameters['proxy_hostid'] = proxy_id - if visible_name: - parameters['name'] = visible_name - if tls_connect: - parameters['tls_connect'] = tls_connect - if tls_accept: - parameters['tls_accept'] = tls_accept - if tls_psk_identity: - parameters['tls_psk_identity'] = tls_psk_identity - if tls_psk: - parameters['tls_psk'] = tls_psk - if tls_issuer: - parameters['tls_issuer'] = tls_issuer - if tls_subject: - parameters['tls_subject'] = tls_subject - if description: - parameters['description'] = description - if ipmi_authtype: - parameters['ipmi_authtype'] = ipmi_authtype - if ipmi_privilege: - parameters['ipmi_privilege'] = ipmi_privilege - if ipmi_username: - parameters['ipmi_username'] = ipmi_username - if ipmi_password: - parameters['ipmi_password'] = ipmi_password + if discovered_host: + # The host was discovered via Discovery Rule + parameters = {"hostid": host_id, "status": status} + else: + # A "plain" host + parameters = {"hostid": host_id, "groups": group_ids, "status": status} + if proxy_id >= 0: + parameters["proxy_hostid"] = proxy_id + if visible_name: + parameters["name"] = visible_name + if tls_connect: + parameters["tls_connect"] = tls_connect + if tls_accept: + parameters["tls_accept"] = tls_accept + if tls_psk_identity: + parameters["tls_psk_identity"] = tls_psk_identity + if tls_psk: + parameters["tls_psk"] = tls_psk + if tls_issuer: + parameters["tls_issuer"] = tls_issuer + if tls_subject: + parameters["tls_subject"] = tls_subject + if description: + parameters["description"] = description + if ipmi_authtype: + parameters["ipmi_authtype"] = ipmi_authtype + if ipmi_privilege: + parameters["ipmi_privilege"] = ipmi_privilege + if ipmi_username: + parameters["ipmi_username"] = ipmi_username + if ipmi_password: + parameters["ipmi_password"] = ipmi_password + if interfaces: + parameters["interfaces"] = interfaces if macros is not None: - parameters['macros'] = macros + parameters["macros"] = macros if tags is not None: - parameters['tags'] = tags - if interfaces: - parameters['interfaces'] = interfaces + parameters["tags"] = tags self._zapi.host.update(parameters) except Exception as e: @@ -586,49 +571,42 @@ class Host(ZabbixBase): # get host by host name def get_host_by_host_name(self, host_name): params = { - 'output': 'extend', - 'selectInventory': 'extend', - 'selectMacros': 'extend', - 'filter': { - 'host': [host_name] + "output": [ + "inventory_mode", + "hostid", + "proxy_hostid", + "host", + "status", + "lastaccess", + "ipmi_authtype", + "ipmi_privilege", + "ipmi_username", + "ipmi_password", + "maintenanceid", + "maintenance_status", + "maintenance_type", + "maintenance_from", + "name", + "flags", + "templateid", + "description", + "tls_connect", + "tls_accept", + "tls_issuer", + "tls_subject", + "proxy_address", + "auto_compress", + "custom_interfaces", + "uuid" + ], + "selectInventory": "extend", + "selectMacros": "extend", + "selectTags": ["tag", "value"], + "filter": { + "host": [host_name] } } - if LooseVersion(self._zbx_api_version) >= LooseVersion('4.2.0'): - params.update({'selectTags': ['tag', 'value']}) - - if LooseVersion(self._zbx_api_version) >= LooseVersion('5.4.0'): - params.update({ - 'output': [ - "inventory_mode", - "hostid", - "proxy_hostid", - "host", - "status", - "lastaccess", - "ipmi_authtype", - "ipmi_privilege", - "ipmi_username", - "ipmi_password", - "maintenanceid", - "maintenance_status", - "maintenance_type", - "maintenance_from", - "name", - "flags", - "templateid", - "description", - "tls_connect", - "tls_accept", - "tls_issuer", - "tls_subject", - "proxy_address", - "auto_compress", - "custom_interfaces", - "uuid" - ] - }) - host_list = self._zapi.host.get(params) if len(host_list) < 1: self._module.fail_json(msg="Host not found: %s" % host_name) @@ -637,27 +615,27 @@ class Host(ZabbixBase): # get proxyid by proxy name def get_proxyid_by_proxy_name(self, proxy_name): - proxy_list = self._zapi.proxy.get({'output': 'extend', 'filter': {'host': [proxy_name]}}) + proxy_list = self._zapi.proxy.get({"output": "extend", "filter": {"host": [proxy_name]}}) if len(proxy_list) < 1: self._module.fail_json(msg="Proxy not found: %s" % proxy_name) else: - return int(proxy_list[0]['proxyid']) + return int(proxy_list[0]["proxyid"]) # get group ids by group names def get_group_ids_by_group_names(self, group_names): if self.check_host_group_exist(group_names): - return self._zapi.hostgroup.get({'output': 'extend', 'filter': {'name': group_names}}) + return self._zapi.hostgroup.get({"output": "extend", "filter": {"name": group_names}}) # get host groups ids by host id def get_group_ids_by_host_id(self, host_id): - return self._zapi.hostgroup.get({'output': 'extend', 'hostids': host_id}) + return self._zapi.hostgroup.get({"output": "extend", "hostids": host_id}) # get host templates by host id def get_host_templates_by_host_id(self, host_id): template_ids = [] - template_list = self._zapi.template.get({'output': 'extend', 'hostids': host_id}) + template_list = self._zapi.template.get({"output": "extend", "hostids": host_id}) for template in template_list: - template_ids.append(template['templateid']) + template_ids.append(template["templateid"]) return template_ids def construct_host_interfaces(self, interfaces): @@ -671,45 +649,40 @@ class Host(ZabbixBase): and ip is any IP address found on interface of type agent (printing purposes only). """ ip = "" - interface_types = {'agent': 1, 'snmp': 2, 'ipmi': 3, 'jmx': 4} - type_to_port = {1: '10050', 2: '161', 3: '623', 4: '12345'} + interface_types = {"agent": 1, "snmp": 2, "ipmi": 3, "jmx": 4} + type_to_port = {1: "10050", 2: "161", 3: "623", 4: "12345"} for interface in interfaces: - if interface['type'] in list(interface_types.keys()): - interface['type'] = interface_types[interface['type']] + if interface["type"] in list(interface_types.keys()): + interface["type"] = interface_types[interface["type"]] else: - interface['type'] = int(interface['type']) + interface["type"] = int(interface["type"]) - if interface['type'] == 1: - ip = interface.get('ip', '') + if interface["type"] == 1: + ip = interface.get("ip", "") - for key in ['ip', 'dns']: + for key in ["ip", "dns"]: if key not in interface or interface[key] is None: - interface[key] = '' - - if 'port' not in interface or interface['port'] is None: - interface['port'] = type_to_port.get(interface['type'], '') + interface[key] = "" - if LooseVersion(self._zbx_api_version) >= LooseVersion('5.0.0'): - if 'bulk' in interface: - del interface['bulk'] + if "port" not in interface or interface["port"] is None: + interface["port"] = type_to_port.get(interface["type"], "") - # Not handled in argument_spec with required_if since only SNMP interfaces are using details - if interface['type'] == 2: - if not interface['details']: - self._module.fail_json(msg='Option "details" required for SNMP interface {0}'.format(interface)) + if "bulk" in interface: + del interface["bulk"] - i_details = interface['details'] - if i_details['version'] < 3 and not i_details.get('community', False): - self._module.fail_json( - msg='Option "community" is required in "details" for SNMP interface {0}'.format(interface)) + # Not handled in argument_spec with required_if since only SNMP interfaces are using details + if interface["type"] == 2: + if not interface["details"]: + self._module.fail_json(msg="Option 'details' required for SNMP interface {0}".format(interface)) - else: - interface['details'] = {} + i_details = interface["details"] + if i_details["version"] < 3 and not i_details.get("community", False): + self._module.fail_json( + msg="Option 'community' is required in 'details' for SNMP interface {0}".format(interface)) else: - if 'details' in interface: - del interface['details'] + interface["details"] = {} return (interfaces, ip) @@ -735,7 +708,7 @@ class Host(ZabbixBase): # get the status of host by host def get_host_status_by_host(self, host): - return host['status'] + return host["status"] # check all the properties before link or clear template def check_all_properties(self, host_id, group_ids, status, interfaces, template_ids, @@ -744,8 +717,8 @@ class Host(ZabbixBase): tls_issuer, tls_subject, tls_connect, ipmi_authtype, ipmi_privilege, ipmi_username, ipmi_password, macros, tags): # get the existing host's groups - exist_host_groups = sorted(self.get_group_ids_by_host_id(host_id), key=lambda k: k['groupid']) - if sorted(group_ids, key=lambda k: k['groupid']) != exist_host_groups: + exist_host_groups = sorted(self.get_group_ids_by_host_id(host_id), key=lambda k: k["groupid"]) + if sorted(group_ids, key=lambda k: k["groupid"]) != exist_host_groups: return True # get the existing status @@ -762,97 +735,82 @@ class Host(ZabbixBase): if set(list(template_ids)) != set(exist_template_ids): return True - if int(host['proxy_hostid']) != int(proxy_id): + if int(host["proxy_hostid"]) != int(proxy_id): return True # Check whether the visible_name has changed; Zabbix defaults to the technical hostname if not set. if visible_name: - if host['name'] != visible_name: + if host["name"] != visible_name: return True # Only compare description if it is given as a module parameter if description: - if host['description'] != description: + if host["description"] != description: return True if inventory_mode: - if LooseVersion(self._zbx_api_version) <= LooseVersion('4.4.0'): - if host['inventory']: - if int(host['inventory']['inventory_mode']) != self.inventory_mode_numeric(inventory_mode): - return True - elif inventory_mode != 'disabled': - return True - else: - if int(host['inventory_mode']) != self.inventory_mode_numeric(inventory_mode): - return True + if int(host["inventory_mode"]) != self.inventory_mode_numeric(inventory_mode): + return True if inventory_zabbix: - proposed_inventory = copy.deepcopy(host['inventory']) + proposed_inventory = copy.deepcopy(host["inventory"]) proposed_inventory.update(inventory_zabbix) - if proposed_inventory != host['inventory']: + if proposed_inventory != host["inventory"]: return True - if tls_accept is not None and 'tls_accept' in host: - if int(host['tls_accept']) != tls_accept: + if tls_accept is not None and "tls_accept" in host: + if int(host["tls_accept"]) != tls_accept: return True - if LooseVersion(self._zbx_api_version) < LooseVersion('5.4'): - if tls_psk_identity is not None and 'tls_psk_identity' in host: - if host['tls_psk_identity'] != tls_psk_identity: - return True - if tls_psk is not None and 'tls_psk' in host: - if host['tls_psk'] != tls_psk: - return True - else: - # in Zabbix >= 5.4 these parameters are write-only and are not returned in host.get response - if tls_psk_identity is not None or tls_psk is not None: - return True + # in Zabbix >= 5.4 these parameters are write-only and are not returned in host.get response + if tls_psk_identity is not None or tls_psk is not None: + return True - if tls_issuer is not None and 'tls_issuer' in host: - if host['tls_issuer'] != tls_issuer: + if tls_issuer is not None and "tls_issuer" in host: + if host["tls_issuer"] != tls_issuer: return True - if tls_subject is not None and 'tls_subject' in host: - if host['tls_subject'] != tls_subject: + if tls_subject is not None and "tls_subject" in host: + if host["tls_subject"] != tls_subject: return True - if tls_connect is not None and 'tls_connect' in host: - if int(host['tls_connect']) != tls_connect: + if tls_connect is not None and "tls_connect" in host: + if int(host["tls_connect"]) != tls_connect: return True if ipmi_authtype is not None: - if int(host['ipmi_authtype']) != ipmi_authtype: + if int(host["ipmi_authtype"]) != ipmi_authtype: return True if ipmi_privilege is not None: - if int(host['ipmi_privilege']) != ipmi_privilege: + if int(host["ipmi_privilege"]) != ipmi_privilege: return True if ipmi_username is not None: - if host['ipmi_username'] != ipmi_username: + if host["ipmi_username"] != ipmi_username: return True if ipmi_password is not None: - if host['ipmi_password'] != ipmi_password: + if host["ipmi_password"] != ipmi_password: return True - # hostmacroid and hostid are present in every item of host['macros'] and need to be removed - if macros is not None and 'macros' in host: + # hostmacroid and hostid are present in every item of host["macros"] and need to be removed + if macros is not None and "macros" in host: t_macros = copy.deepcopy(macros) # make copy to prevent change in original data - for macro in host['macros']: - macro.pop('hostid', False) - macro.pop('hostmacroid', False) + for macro in host["macros"]: + macro.pop("hostid", False) + macro.pop("hostmacroid", False) diff = [] - zabbix_utils.helper_compare_lists(t_macros, host['macros'], diff) + zabbix_utils.helper_compare_lists(t_macros, host["macros"], diff) if diff != []: return True - if tags is not None and 'tags' in host: - if sorted(tags, key=lambda k: k['tag']) != sorted(host['tags'], key=lambda k: k['tag']): + if tags is not None and "tags" in host: + if sorted(tags, key=lambda k: k["tag"]) != sorted(host["tags"], key=lambda k: k["tag"]): return True return False # link or clear template of the host def link_or_clear_template(self, host_id, template_id_list, tls_connect, tls_accept, tls_psk_identity, tls_psk, - tls_issuer, tls_subject, ipmi_authtype, ipmi_privilege, ipmi_username, ipmi_password): + tls_issuer, tls_subject, ipmi_authtype, ipmi_privilege, ipmi_username, ipmi_password, discovered_host): # get host's exist template ids exist_template_id_list = self.get_host_templates_by_host_id(host_id) @@ -863,20 +821,25 @@ class Host(ZabbixBase): # get unlink and clear templates templates_clear = exist_template_ids.difference(template_ids) templates_clear_list = list(templates_clear) - request_str = {'hostid': host_id, 'templates': template_id_list, 'templates_clear': templates_clear_list, - 'ipmi_authtype': ipmi_authtype, 'ipmi_privilege': ipmi_privilege, 'ipmi_username': ipmi_username, 'ipmi_password': ipmi_password} - if tls_connect: - request_str['tls_connect'] = tls_connect - if tls_accept: - request_str['tls_accept'] = tls_accept - if tls_psk_identity is not None: - request_str['tls_psk_identity'] = tls_psk_identity - if tls_psk is not None: - request_str['tls_psk'] = tls_psk - if tls_issuer is not None: - request_str['tls_issuer'] = tls_issuer - if tls_subject is not None: - request_str['tls_subject'] = tls_subject + if discovered_host: + # The host was discovered via Discovery Rule + request_str = {"hostid": host_id, "templates": template_id_list, "templates_clear": templates_clear_list} + else: + # A "plain" host + request_str = {"hostid": host_id, "templates": template_id_list, "templates_clear": templates_clear_list, + "ipmi_authtype": ipmi_authtype, "ipmi_privilege": ipmi_privilege, "ipmi_username": ipmi_username, "ipmi_password": ipmi_password} + if tls_connect: + request_str["tls_connect"] = tls_connect + if tls_accept: + request_str["tls_accept"] = tls_accept + if tls_psk_identity is not None: + request_str["tls_psk_identity"] = tls_psk_identity + if tls_psk is not None: + request_str["tls_psk"] = tls_psk + if tls_issuer is not None: + request_str["tls_issuer"] = tls_issuer + if tls_subject is not None: + request_str["tls_subject"] = tls_subject try: if self._module.check_mode: self._module.exit_json(changed=True) @@ -903,7 +866,7 @@ class Host(ZabbixBase): inventory_mode = self.inventory_mode_numeric(inventory_mode) # watch for - https://support.zabbix.com/browse/ZBX-6033 - request_str = {'hostid': host_id, 'inventory_mode': inventory_mode} + request_str = {"hostid": host_id, "inventory_mode": inventory_mode} try: if self._module.check_mode: self._module.exit_json(changed=True) @@ -916,7 +879,7 @@ class Host(ZabbixBase): if not inventory: return - request_str = {'hostid': host_id, 'inventory': inventory} + request_str = {"hostid": host_id, "inventory": inventory} try: if self._module.check_mode: self._module.exit_json(changed=True) @@ -930,30 +893,30 @@ def update_exist_interfaces_with_defaults(exist_interfaces): new_exist_interfaces = [] default_interface = { - 'main': '0', - 'useip': '0', - 'ip': '', - 'dns': '', - 'port': '' + "main": "0", + "useip": "0", + "ip": "", + "dns": "", + "port": "" } default_interface_details = { - 'version': 2, - 'bulk': 1, - 'community': '', - 'securityname': '', - 'contextname': '', - 'securitylevel': 0, - 'authprotocol': 0, - 'authpassphrase': '', - 'privprotocol': 0, - 'privpassphrase': '' + "version": 2, + "bulk": 1, + "community": "", + "securityname": "", + "contextname": "", + "securitylevel": 0, + "authprotocol": 0, + "authpassphrase": "", + "privprotocol": 0, + "privpassphrase": "" } for interface in exist_interfaces: new_interface = default_interface.copy() new_interface.update(interface) - new_interface['details'] = default_interface_details.copy() - if 'details' in interface: - new_interface['details'].update(interface['details']) + new_interface["details"] = default_interface_details.copy() + if "details" in interface: + new_interface["details"].update(interface["details"]) new_exist_interfaces.append(new_interface) return new_exist_interfaces @@ -961,16 +924,16 @@ def update_exist_interfaces_with_defaults(exist_interfaces): def normalize_macro_name(macro_name): # Zabbix handles macro names in upper case characters - if ':' in macro_name: - macro_name = ':'.join([macro_name.split(':')[0].upper(), ':'.join(macro_name.split(':')[1:])]) + if ":" in macro_name: + macro_name = ":".join([macro_name.split(":")[0].upper(), ":".join(macro_name.split(":")[1:])]) else: macro_name = macro_name.upper() # Valid format for macro is {$MACRO} - if not macro_name.startswith('{$'): - macro_name = '{$' + macro_name - if not macro_name.endswith('}'): - macro_name = macro_name + '}' + if not macro_name.startswith("{$"): + macro_name = "{$" + macro_name + if not macro_name.endswith("}"): + macro_name = macro_name + "}" return macro_name @@ -978,79 +941,78 @@ def normalize_macro_name(macro_name): def main(): argument_spec = zabbix_utils.zabbix_common_argument_spec() argument_spec.update(dict( - host_name=dict(type='str', required=True), - host_groups=dict(type='list', required=False), - link_templates=dict(type='list', required=False), - status=dict(type='str', default="enabled", choices=['enabled', 'disabled']), - state=dict(type='str', default="present", choices=['present', 'absent']), - inventory_mode=dict(type='str', required=False, choices=['automatic', 'manual', 'disabled']), - ipmi_authtype=dict(type='int', default=None), - ipmi_privilege=dict(type='int', default=None), - ipmi_username=dict(type='str', required=False, default=None), - ipmi_password=dict(type='str', required=False, default=None, no_log=True), - tls_connect=dict(type='int', required=False), - tls_accept=dict(type='int', required=False), - tls_psk_identity=dict(type='str', required=False), - tls_psk=dict(type='str', required=False, no_log=True), - ca_cert=dict(type='str', required=False, aliases=['tls_issuer']), - tls_subject=dict(type='str', required=False), - inventory_zabbix=dict(type='dict', required=False), + host_name=dict(type="str", required=True), + host_groups=dict(type="list", required=False, elements="str"), + link_templates=dict(type="list", required=False, elements="str"), + status=dict(type="str", default="enabled", choices=["enabled", "disabled"]), + state=dict(type="str", default="present", choices=["present", "absent"]), + inventory_mode=dict(type="str", required=False, choices=["automatic", "manual", "disabled"]), + ipmi_authtype=dict(type="int", default=None), + ipmi_privilege=dict(type="int", default=None), + ipmi_username=dict(type="str", required=False, default=None), + ipmi_password=dict(type="str", required=False, default=None, no_log=True), + tls_connect=dict(type="int", required=False), + tls_accept=dict(type="int", required=False), + tls_psk_identity=dict(type="str", required=False), + tls_psk=dict(type="str", required=False, no_log=True), + ca_cert=dict(type="str", required=False, aliases=["tls_issuer"]), + tls_subject=dict(type="str", required=False), + inventory_zabbix=dict(type="dict", required=False), interfaces=dict( - type='list', - elements='dict', + type="list", + elements="dict", default=[], options=dict( - type=dict(type='str', required=True, choices=['agent', '1', 'snmp', '2', 'ipmi', '3', 'jmx', '4']), - main=dict(type='int', choices=[0, 1], default=0), - useip=dict(type='int', choices=[0, 1], default=0), - ip=dict(type='str'), - dns=dict(type='str'), - port=dict(type='str'), - bulk=dict(type='int', choices=[0, 1], default=1), + type=dict(type="str", required=True, choices=["agent", "1", "snmp", "2", "ipmi", "3", "jmx", "4"]), + main=dict(type="int", choices=[0, 1], default=0), + useip=dict(type="int", choices=[0, 1], default=0), + ip=dict(type="str"), + dns=dict(type="str"), + port=dict(type="str"), details=dict( - type='dict', + type="dict", default={}, options=dict( - version=dict(type='int', choices=[1, 2, 3], default=2), - bulk=dict(type='int', choices=[0, 1], default=1), - community=dict(type='str', default=''), - securityname=dict(type='str', default=''), - contextname=dict(type='str', default=''), - securitylevel=dict(type='int', choices=[0, 1, 2], default=0), - authprotocol=dict(type='int', choices=[0, 1, 2, 3, 4, 5], default=0), - authpassphrase=dict(type='str', default='', no_log=True), - privprotocol=dict(type='int', choices=[0, 1, 2, 3, 4, 5], default=0), - privpassphrase=dict(type='str', default='', no_log=True) + version=dict(type="int", choices=[1, 2, 3], default=2), + bulk=dict(type="int", choices=[0, 1], default=1), + community=dict(type="str", default=""), + securityname=dict(type="str", default=""), + contextname=dict(type="str", default=""), + securitylevel=dict(type="int", choices=[0, 1, 2], default=0), + authprotocol=dict(type="int", choices=[0, 1, 2, 3, 4, 5], default=0), + authpassphrase=dict(type="str", default="", no_log=True), + privprotocol=dict(type="int", choices=[0, 1, 2, 3, 4, 5], default=0), + privpassphrase=dict(type="str", default="", no_log=True) ) ) ), required_if=[ - ['useip', 0, ['dns']], - ['useip', 1, ['ip']] + ["useip", 0, ["dns"]], + ["useip", 1, ["ip"]] ] ), - force=dict(type='bool', default=True), - proxy=dict(type='str', required=False), - visible_name=dict(type='str', required=False), - description=dict(type='str', required=False), + force=dict(type="bool", default=True), + proxy=dict(type="str", required=False), + visible_name=dict(type="str", required=False), + description=dict(type="str", required=False), macros=dict( - type='list', - elements='dict', - aliases=['user_macros'], + type="list", + elements="dict", + aliases=["user_macros"], options=dict( - macro=dict(type='str', required=True), - value=dict(type='str', required=True), - description=dict(type='str', default=''), - type=dict(type='str', default='text', choices=['text', 'secret']) + macro=dict(type="str", required=True), + value=dict(type="str", required=True), + description=dict(type="str", default=""), + type=dict(type="str", default="text", choices=["text", "secret"]) ) ), tags=dict( - type='list', - elements='dict', - aliases=['host_tags'], + type="list", + elements="dict", + aliases=["host_tags"], options=dict( - tag=dict(type='str', required=True), - value=dict(type='str', default='') + tag=dict(type="str", required=True), + value=dict(type="str", default="") ) ) )) @@ -1059,36 +1021,30 @@ def main(): supports_check_mode=True ) - zabbix_utils.require_creds_params(module) - - for p in ['server_url', 'login_user', 'login_password', 'timeout', 'validate_certs']: - if p in module.params and not module.params[p] is None: - module.warn('Option "%s" is deprecated with the move to httpapi connection and will be removed in the next release' % p) - - host_name = module.params['host_name'] - visible_name = module.params['visible_name'] - description = module.params['description'] - host_groups = module.params['host_groups'] - link_templates = module.params['link_templates'] - inventory_mode = module.params['inventory_mode'] - ipmi_authtype = module.params['ipmi_authtype'] - ipmi_privilege = module.params['ipmi_privilege'] - ipmi_username = module.params['ipmi_username'] - ipmi_password = module.params['ipmi_password'] - tls_connect = module.params['tls_connect'] - tls_accept = module.params['tls_accept'] - tls_psk_identity = module.params['tls_psk_identity'] - tls_psk = module.params['tls_psk'] - tls_issuer = module.params['ca_cert'] - tls_subject = module.params['tls_subject'] - inventory_zabbix = module.params['inventory_zabbix'] - status = module.params['status'] - state = module.params['state'] - interfaces = module.params['interfaces'] - force = module.params['force'] - proxy = module.params['proxy'] - macros = module.params['macros'] - tags = module.params['tags'] + host_name = module.params["host_name"] + visible_name = module.params["visible_name"] + description = module.params["description"] + host_groups = module.params["host_groups"] + link_templates = module.params["link_templates"] + inventory_mode = module.params["inventory_mode"] + ipmi_authtype = module.params["ipmi_authtype"] + ipmi_privilege = module.params["ipmi_privilege"] + ipmi_username = module.params["ipmi_username"] + ipmi_password = module.params["ipmi_password"] + tls_connect = module.params["tls_connect"] + tls_accept = module.params["tls_accept"] + tls_psk_identity = module.params["tls_psk_identity"] + tls_psk = module.params["tls_psk"] + tls_issuer = module.params["ca_cert"] + tls_subject = module.params["tls_subject"] + inventory_zabbix = module.params["inventory_zabbix"] + status = module.params["status"] + state = module.params["state"] + interfaces = module.params["interfaces"] + force = module.params["force"] + proxy = module.params["proxy"] + macros = module.params["macros"] + tags = module.params["tags"] # convert enabled to 0; disabled to 1 status = 1 if status == "disabled" else 0 @@ -1109,20 +1065,13 @@ def main(): if macros: # convert macros to zabbix native format - {$MACRO} for macro in macros: - macro['macro'] = normalize_macro_name(macro['macro']) + macro["macro"] = normalize_macro_name(macro["macro"]) - if LooseVersion(host._zbx_api_version) <= LooseVersion('4.4.0'): - if 'description' in macro: - macro.pop('description', False) - - if 'type' in macro: - if LooseVersion(host._zbx_api_version) < LooseVersion('5.0.0'): - macro.pop('type') - else: - if macro['type'] == 'text': - macro['type'] = '0' - elif macro['type'] == 'secret': - macro['type'] = '1' + if "type" in macro: + if macro["type"] == "text": + macro["type"] = "0" + elif macro["type"] == "secret": + macro["type"] = "1" # Use proxy specified, or set to 0 if proxy: @@ -1136,11 +1085,12 @@ def main(): if is_host_exist: # get host id by host name zabbix_host_obj = host.get_host_by_host_name(host_name) - host_id = zabbix_host_obj['hostid'] + host_id = zabbix_host_obj["hostid"] + discovered_host = zabbix_host_obj["flags"] == '4' # If proxy is not specified as a module parameter, use the existing setting if proxy is None: - proxy_id = int(zabbix_host_obj['proxy_hostid']) + proxy_id = int(zabbix_host_obj["proxy_hostid"]) if state == "absent": # remove host @@ -1153,29 +1103,29 @@ def main(): group_ids = host.get_group_ids_by_host_id(host_id) # get existing host's interfaces - exist_interfaces = host._zapi.hostinterface.get({'output': 'extend', 'hostids': host_id}) - exist_interfaces.sort(key=lambda x: int(x['interfaceid'])) + exist_interfaces = host._zapi.hostinterface.get({"output": "extend", "hostids": host_id}) + exist_interfaces.sort(key=lambda x: int(x["interfaceid"])) exist_interfaces = update_exist_interfaces_with_defaults(exist_interfaces) # Convert integer parameters from strings to ints for idx, interface in enumerate(copy.deepcopy(exist_interfaces)): for key in tuple(interface.keys()): # fix values for properties - if key in ['useip', 'main', 'type', 'bulk']: + if key in ["useip", "main", "type", "bulk"]: exist_interfaces[idx][key] = int(interface[key]) - elif key == 'details': + elif key == "details": if not interface[key]: exist_interfaces[idx][key] = {} else: for d_key in interface[key].keys(): - if d_key in ['version', 'bulk', 'securitylevel', 'authprotocol', 'privprotocol']: + if d_key in ["version", "bulk", "securitylevel", "authprotocol", "privprotocol"]: exist_interfaces[idx][key][d_key] = int(interface[key][d_key]) interfaces_copy = copy.deepcopy(interfaces) found_in_interfaces = [] for idx, interface in enumerate(copy.deepcopy(exist_interfaces)): - interfaceid = interface['interfaceid'] - hostid = interface['hostid'] + interfaceid = interface["interfaceid"] + hostid = interface["hostid"] if not interfaces_copy: # Whe no interfaces specified, copy existing interfaces @@ -1183,8 +1133,8 @@ def main(): continue # Find already configured interfaces in requested interfaces and compile final list of - # interfaces in 'interfaces' variable. Every element of the list defines one interface. - # If an element has 'interfaceid' field then Zabbix will update existing interface otherwise + # interfaces in "interfaces" variable. Every element of the list defines one interface. + # If an element has "interfaceid" field then Zabbix will update existing interface otherwise # a new interface will be added. found = False for idx1, iface in enumerate(interfaces_copy): @@ -1193,8 +1143,8 @@ def main(): if diff_dict == {}: found = True found_in_interfaces.append(iface) - interfaces[idx1]['interfaceid'] = interfaceid - interfaces[idx1]['hostid'] = hostid + interfaces[idx1]["interfaceid"] = interfaceid + interfaces[idx1]["hostid"] = hostid break if not found: @@ -1203,10 +1153,10 @@ def main(): else: # if force == True overwrite existing interfaces with provided interfaces with the same type for idx1, iface in enumerate(interfaces_copy): - if interface['type'] == iface['type'] and iface not in found_in_interfaces: + if interface["type"] == iface["type"] and iface not in found_in_interfaces: found_in_interfaces.append(iface) - interfaces[idx1]['interfaceid'] = interfaceid - interfaces[idx1]['hostid'] = hostid + interfaces[idx1]["interfaceid"] = interfaceid + interfaces[idx1]["hostid"] = hostid break if not force or link_templates is None: @@ -1218,12 +1168,12 @@ def main(): group_ids.append(group_id) # Macros not present in host.update will be removed if we dont copy them when force=no - if macros is not None and 'macros' in zabbix_host_obj.keys(): - existing_macros = zabbix_host_obj['macros'] + if macros is not None and "macros" in zabbix_host_obj.keys(): + existing_macros = zabbix_host_obj["macros"] for macro in existing_macros: - macro.pop('hostmacroid', None) - macro.pop('hostid', None) - macro.pop('automatic', None) + macro.pop("hostmacroid", None) + macro.pop("hostid", None) + macro.pop("automatic", None) found = False for idx1, prov_macro in enumerate(macros): diff_dict = {} @@ -1237,11 +1187,11 @@ def main(): macros.append(macro) # Tags not present in host.update will be removed if we dont copy them when force=no - if tags is not None and 'tags' in zabbix_host_obj.keys(): - provided_tags = [t['tag'] for t in tags] - existing_tags = zabbix_host_obj['tags'] + if tags is not None and "tags" in zabbix_host_obj.keys(): + provided_tags = [t["tag"] for t in tags] + existing_tags = zabbix_host_obj["tags"] for tag in existing_tags: - if tag['tag'] not in provided_tags: + if tag["tag"] not in provided_tags: tags.append(tag) # update host @@ -1254,11 +1204,11 @@ def main(): host.update_host( host_name, group_ids, status, host_id, interfaces, exist_interfaces, proxy_id, visible_name, description, tls_connect, tls_accept, tls_psk_identity, tls_psk, tls_issuer, tls_subject, - ipmi_authtype, ipmi_privilege, ipmi_username, ipmi_password, macros, tags) + ipmi_authtype, ipmi_privilege, ipmi_username, ipmi_password, macros, tags, discovered_host) host.link_or_clear_template( host_id, template_ids, tls_connect, tls_accept, tls_psk_identity, tls_psk, tls_issuer, - tls_subject, ipmi_authtype, ipmi_privilege, ipmi_username, ipmi_password) + tls_subject, ipmi_authtype, ipmi_privilege, ipmi_username, ipmi_password, discovered_host) host.update_inventory_mode(host_id, inventory_mode) host.update_inventory_zabbix(host_id, inventory_zabbix) @@ -1270,6 +1220,8 @@ def main(): module.exit_json(changed=False) else: + discovered_host = False + if state == "absent": # the host is already deleted. module.exit_json(changed=False) @@ -1277,10 +1229,6 @@ def main(): if not group_ids: module.fail_json(msg="Specify at least one group for creating host '%s'." % host_name) - if not interfaces or (interfaces and len(interfaces) == 0): - if LooseVersion(host._zbx_api_version) < LooseVersion('5.2.0'): - module.fail_json(msg="Specify at least one interface for creating host '%s'." % host_name) - # create host host_id = host.add_host( host_name, group_ids, status, interfaces, proxy_id, visible_name, description, tls_connect, tls_accept, @@ -1289,7 +1237,7 @@ def main(): host.link_or_clear_template( host_id, template_ids, tls_connect, tls_accept, tls_psk_identity, tls_psk, tls_issuer, tls_subject, - ipmi_authtype, ipmi_privilege, ipmi_username, ipmi_password) + ipmi_authtype, ipmi_privilege, ipmi_username, ipmi_password, discovered_host) host.update_inventory_mode(host_id, inventory_mode) host.update_inventory_zabbix(host_id, inventory_zabbix) @@ -1298,5 +1246,5 @@ def main(): host_name, ip, link_templates)) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/ansible_collections/community/zabbix/plugins/modules/zabbix_host_events_info.py b/ansible_collections/community/zabbix/plugins/modules/zabbix_host_events_info.py index eb22ce23c..fde86bc12 100644 --- a/ansible_collections/community/zabbix/plugins/modules/zabbix_host_events_info.py +++ b/ansible_collections/community/zabbix/plugins/modules/zabbix_host_events_info.py @@ -9,7 +9,7 @@ from __future__ import (absolute_import, division, print_function) __metaclass__ = type -RETURN = ''' +RETURN = """ --- triggers_ok: description: Host Zabbix Triggers in OK state @@ -134,20 +134,20 @@ triggers_problem: value: description: Whether the trigger is in OK or problem state type: int -''' +""" -DOCUMENTATION = ''' +DOCUMENTATION = """ --- module: zabbix_host_events_info short_description: Get all triggers about a Zabbix host description: - This module allows you to see if a Zabbix host have no active alert to make actions on it. - For this case use module Ansible 'fail' to exclude host in trouble. + For this case use module Ansible "fail" to exclude host in trouble. - Length of "triggers_ok" allow if template's triggers exist for Zabbix Host author: - "Stéphane Travassac (@stravassac)" requirements: - - "python >= 2.7" + - "python >= 3.9" options: host_identifier: description: @@ -180,19 +180,19 @@ options: extends_documentation_fragment: - community.zabbix.zabbix -''' +""" -EXAMPLES = ''' +EXAMPLES = """ # If you want to use Username and Password to be authenticated by Zabbix Server - name: Set credentials to access Zabbix Server API - set_fact: + ansible.builtin.set_fact: ansible_user: Admin ansible_httpapi_pass: zabbix # If you want to use API token to be authenticated by Zabbix Server # https://www.zabbix.com/documentation/current/en/manual/web_interface/frontend_sections/administration/general#api-tokens - name: Set API token - set_fact: + ansible.builtin.set_fact: ansible_zabbix_auth_key: 8ec0d52432c15c91fcafe9888500cf9a607f44091ab554dbee860f6b44fac895 - name: exclude machine if alert active on it @@ -203,18 +203,17 @@ EXAMPLES = ''' ansible_httpapi_port: 443 ansible_httpapi_use_ssl: true ansible_httpapi_validate_certs: false - ansible_zabbix_url_path: 'zabbixeu' # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu + ansible_zabbix_url_path: "zabbixeu" # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu ansible_host: zabbix-example-fqdn.org community.zabbix.zabbix_host_events_info: host_identifier: "{{inventory_hostname}}" host_id_type: "hostname" - timeout: 120 register: zbx_host delegate_to: localhost - fail: msg: "machine alert in zabbix" - when: zbx_host['triggers_problem']|length > 0 -''' + when: zbx_host["triggers_problem"]|length > 0 +""" from ansible.module_utils.basic import AnsibleModule @@ -227,8 +226,8 @@ class Host(ZabbixBase): def get_host(self, host_identifier, host_inventory, search_key): """ Get host by hostname|visible_name|hostid """ host = self._zapi.host.get( - {'output': 'extend', 'selectParentTemplates': ['name'], 'filter': {search_key: host_identifier}, - 'selectInventory': host_inventory}) + {"output": "extend", "selectParentTemplates": ["name"], "filter": {search_key: host_identifier}, + "selectInventory": host_inventory}) if len(host) < 1: self._module.fail_json(msg="Host not found: %s" % host_identifier) else: @@ -236,18 +235,17 @@ class Host(ZabbixBase): def get_triggers_by_host_id_in_problem_state(self, host_id, trigger_severity): """ Get triggers in problem state from a hostid""" - # https://www.zabbix.com/documentation/3.4/manual/api/reference/trigger/get - output = 'extend' - triggers_list = self._zapi.trigger.get({'output': output, 'hostids': host_id, - 'min_severity': trigger_severity}) + output = "extend" + triggers_list = self._zapi.trigger.get({"output": output, "hostids": host_id, + "min_severity": trigger_severity}) return triggers_list def get_last_event_by_trigger_id(self, triggers_id): """ Get the last event from triggerid""" - output = ['eventid', 'clock', 'acknowledged', 'value'] - select_acknowledges = ['clock', 'alias', 'message'] - event = self._zapi.event.get({'output': output, 'objectids': triggers_id, - 'select_acknowledges': select_acknowledges, "limit": 1, "sortfield": "clock", + output = ["eventid", "clock", "acknowledged", "value"] + select_acknowledges = ["clock", "alias", "message"] + event = self._zapi.event.get({"output": output, "objectids": triggers_id, + "select_acknowledges": select_acknowledges, "limit": 1, "sortfield": "clock", "sortorder": "DESC"}) return event[0] @@ -255,48 +253,42 @@ class Host(ZabbixBase): def main(): argument_spec = zabbix_utils.zabbix_common_argument_spec() argument_spec.update(dict( - host_identifier=dict(type='str', required=True), + host_identifier=dict(type="str", required=True), host_id_type=dict( - default='hostname', - type='str', - choices=['hostname', 'visible_name', 'hostid']), + default="hostname", + type="str", + choices=["hostname", "visible_name", "hostid"]), trigger_severity=dict( - type='str', + type="str", required=False, - default='average', - choices=['not_classified', 'information', 'warning', 'average', 'high', 'disaster']), + default="average", + choices=["not_classified", "information", "warning", "average", "high", "disaster"]), )) module = AnsibleModule( argument_spec=argument_spec, supports_check_mode=True ) - zabbix_utils.require_creds_params(module) + trigger_severity_map = {"not_classified": 0, "information": 1, "warning": 2, "average": 3, "high": 4, "disaster": 5} + host_id = module.params["host_identifier"] + host_id_type = module.params["host_id_type"] + trigger_severity = trigger_severity_map[module.params["trigger_severity"]] - for p in ['server_url', 'login_user', 'login_password', 'timeout', 'validate_certs']: - if p in module.params and not module.params[p] is None: - module.warn('Option "%s" is deprecated with the move to httpapi connection and will be removed in the next release' % p) - - trigger_severity_map = {'not_classified': 0, 'information': 1, 'warning': 2, 'average': 3, 'high': 4, 'disaster': 5} - host_id = module.params['host_identifier'] - host_id_type = module.params['host_id_type'] - trigger_severity = trigger_severity_map[module.params['trigger_severity']] - - host_inventory = 'hostid' + host_inventory = "hostid" host = Host(module) - if host_id_type == 'hostname': - zabbix_host = host.get_host(host_id, host_inventory, 'host') - host_id = zabbix_host['hostid'] + if host_id_type == "hostname": + zabbix_host = host.get_host(host_id, host_inventory, "host") + host_id = zabbix_host["hostid"] - elif host_id_type == 'visible_name': - zabbix_host = host.get_host(host_id, host_inventory, 'name') - host_id = zabbix_host['hostid'] + elif host_id_type == "visible_name": + zabbix_host = host.get_host(host_id, host_inventory, "name") + host_id = zabbix_host["hostid"] - elif host_id_type == 'hostid': - ''' check hostid exist''' - zabbix_host = host.get_host(host_id, host_inventory, 'hostid') + elif host_id_type == "hostid": + # check hostid exist + zabbix_host = host.get_host(host_id, host_inventory, "hostid") triggers = host.get_triggers_by_host_id_in_problem_state(host_id, trigger_severity) @@ -305,9 +297,9 @@ def main(): for trigger in triggers: # tGet last event for trigger with problem value = 1 # https://www.zabbix.com/documentation/3.4/manual/api/reference/trigger/object - if int(trigger['value']) == 1: - event = host.get_last_event_by_trigger_id(trigger['triggerid']) - trigger['last_event'] = event + if int(trigger["value"]) == 1: + event = host.get_last_event_by_trigger_id(trigger["triggerid"]) + trigger["last_event"] = event triggers_problem.append(trigger) else: triggers_ok.append(trigger) @@ -315,5 +307,5 @@ def main(): module.exit_json(ok=True, triggers_ok=triggers_ok, triggers_problem=triggers_problem) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/ansible_collections/community/zabbix/plugins/modules/zabbix_host_facts.py b/ansible_collections/community/zabbix/plugins/modules/zabbix_host_facts.py deleted file mode 100644 index 58e3343ab..000000000 --- a/ansible_collections/community/zabbix/plugins/modules/zabbix_host_facts.py +++ /dev/null @@ -1,239 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# (c) me@mimiko.me -# 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) -__metaclass__ = type - - -RETURN = r''' ---- -hosts: - description: List of Zabbix hosts. See https://www.zabbix.com/documentation/4.0/manual/api/reference/host/get for list of host values. - returned: success - type: dict - sample: [ { "available": "1", "description": "", "disable_until": "0", "error": "", "flags": "0", "groups": ["1"], "host": "Host A", ... } ] -''' - -DOCUMENTATION = r''' ---- -module: zabbix_host_info -short_description: Gather information about Zabbix host -description: - - This module allows you to search for Zabbix host entries. - - This module was called C(zabbix_host_facts) before Ansible 2.9. The usage did not change. -author: - - "Michael Miko (@RedWhiteMiko)" -requirements: - - "python >= 2.6" -options: - host_name: - description: - - Name of the host in Zabbix. - - host_name is the unique identifier used and cannot be updated using this module. - - Required when I(host_ip) is not used. - required: false - type: str - default: '' - host_ip: - description: - - Host interface IP of the host in Zabbix. - - Required when I(host_name) is not used. - required: false - type: list - elements: str - default: [] - exact_match: - description: - - Find the exact match - type: bool - default: no - remove_duplicate: - description: - - Remove duplicate host from host result - type: bool - default: yes - host_inventory: - description: - - List of host inventory keys to display in result. - - Whole host inventory is retrieved if keys are not specified. - type: list - elements: str - required: false - default: [] -extends_documentation_fragment: -- community.zabbix.zabbix - -''' - -EXAMPLES = r''' -# If you want to use Username and Password to be authenticated by Zabbix Server -- name: Set credentials to access Zabbix Server API - set_fact: - ansible_user: Admin - ansible_httpapi_pass: zabbix - -# If you want to use API token to be authenticated by Zabbix Server -# https://www.zabbix.com/documentation/current/en/manual/web_interface/frontend_sections/administration/general#api-tokens -- name: Set API token - set_fact: - ansible_zabbix_auth_key: 8ec0d52432c15c91fcafe9888500cf9a607f44091ab554dbee860f6b44fac895 - -- name: Get host info - # set task level variables as we change ansible_connection plugin here - vars: - ansible_network_os: community.zabbix.zabbix - ansible_connection: httpapi - ansible_httpapi_port: 443 - ansible_httpapi_use_ssl: true - ansible_httpapi_validate_certs: false - ansible_zabbix_url_path: 'zabbixeu' # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu - ansible_host: zabbix-example-fqdn.org - community.zabbix.zabbix_host_info: - host_name: ExampleHost - host_ip: 127.0.0.1 - timeout: 10 - exact_match: no - remove_duplicate: yes - -- name: Reduce host inventory information to provided keys - # set task level variables as we change ansible_connection plugin here - vars: - ansible_network_os: community.zabbix.zabbix - ansible_connection: httpapi - ansible_httpapi_port: 443 - ansible_httpapi_use_ssl: true - ansible_httpapi_validate_certs: false - ansible_zabbix_url_path: 'zabbixeu' # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu - ansible_host: zabbix-example-fqdn.org - community.zabbix.zabbix_host_info: - host_name: ExampleHost - host_inventory: - - os - - tag - host_ip: 127.0.0.1 - timeout: 10 - exact_match: no - remove_duplicate: yes -''' - - -from ansible.module_utils.basic import AnsibleModule - -from ansible_collections.community.zabbix.plugins.module_utils.base import ZabbixBase -import ansible_collections.community.zabbix.plugins.module_utils.helpers as zabbix_utils - - -class Host(ZabbixBase): - def get_hosts_by_host_name(self, host_name, exact_match, host_inventory): - """ Get host by host name """ - search_key = 'search' - if exact_match: - search_key = 'filter' - host_list = self._zapi.host.get({ - 'output': 'extend', - 'selectParentTemplates': ['name'], - search_key: {'host': [host_name]}, - 'selectInventory': host_inventory, - 'selectGroups': 'extend', - 'selectTags': 'extend', - 'selectMacros': 'extend' - }) - if len(host_list) < 1: - self._module.fail_json(msg="Host not found: %s" % host_name) - else: - return host_list - - def get_hosts_by_ip(self, host_ips, host_inventory): - """ Get host by host ip(s) """ - hostinterfaces = self._zapi.hostinterface.get({ - 'output': 'extend', - 'filter': { - 'ip': host_ips - } - }) - if len(hostinterfaces) < 1: - self._module.fail_json(msg="Host not found: %s" % host_ips) - host_list = [] - for hostinterface in hostinterfaces: - host = self._zapi.host.get({ - 'output': 'extend', - 'selectGroups': 'extend', - 'selectParentTemplates': ['name'], - 'hostids': hostinterface['hostid'], - 'selectInventory': host_inventory, - 'selectTags': 'extend', - 'selectMacros': 'extend' - }) - host[0]['hostinterfaces'] = hostinterface - host_list.append(host[0]) - return host_list - - def delete_duplicate_hosts(self, hosts): - """ Delete duplicated hosts """ - unique_hosts = [] - listed_hostnames = [] - for zabbix_host in hosts: - if zabbix_host['name'] in listed_hostnames: - continue - unique_hosts.append(zabbix_host) - listed_hostnames.append(zabbix_host['name']) - return unique_hosts - - -def main(): - argument_spec = zabbix_utils.zabbix_common_argument_spec() - argument_spec.update(dict( - host_name=dict(type='str', default='', required=False), - host_ip=dict(type='list', default=[], required=False), - exact_match=dict(type='bool', required=False, default=False), - remove_duplicate=dict(type='bool', required=False, default=True), - host_inventory=dict(type='list', default=[], required=False) - )) - module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True - ) - if module._name == 'zabbix_host_facts': - module.deprecate("The 'zabbix_host_facts' module has been renamed to 'zabbix_host_info'", - collection_name="community.zabbix", version='2.0.0') # was 2.13 - - zabbix_utils.require_creds_params(module) - - host_name = module.params['host_name'] - host_ips = module.params['host_ip'] - exact_match = module.params['exact_match'] - is_remove_duplicate = module.params['remove_duplicate'] - host_inventory = module.params['host_inventory'] - - if not host_inventory: - host_inventory = 'extend' - - host = Host(module) - - if host_name: - hosts = host.get_hosts_by_host_name(host_name, exact_match, host_inventory) - if is_remove_duplicate: - hosts = host.delete_duplicate_hosts(hosts) - extended_hosts = [] - for zabbix_host in hosts: - zabbix_host['hostinterfaces'] = host._zapi.hostinterface.get({ - 'output': 'extend', 'hostids': zabbix_host['hostid'] - }) - extended_hosts.append(zabbix_host) - module.exit_json(ok=True, hosts=extended_hosts) - - elif host_ips: - extended_hosts = host.get_hosts_by_ip(host_ips, host_inventory) - if is_remove_duplicate: - hosts = host.delete_duplicate_hosts(extended_hosts) - module.exit_json(ok=True, hosts=extended_hosts) - else: - module.exit_json(ok=False, hosts=[], result="No Host present") - - -if __name__ == '__main__': - main() diff --git a/ansible_collections/community/zabbix/plugins/modules/zabbix_host_info.py b/ansible_collections/community/zabbix/plugins/modules/zabbix_host_info.py index 58e3343ab..92c185227 100644 --- a/ansible_collections/community/zabbix/plugins/modules/zabbix_host_info.py +++ b/ansible_collections/community/zabbix/plugins/modules/zabbix_host_info.py @@ -9,16 +9,16 @@ from __future__ import (absolute_import, division, print_function) __metaclass__ = type -RETURN = r''' +RETURN = r""" --- hosts: description: List of Zabbix hosts. See https://www.zabbix.com/documentation/4.0/manual/api/reference/host/get for list of host values. returned: success type: dict sample: [ { "available": "1", "description": "", "disable_until": "0", "error": "", "flags": "0", "groups": ["1"], "host": "Host A", ... } ] -''' +""" -DOCUMENTATION = r''' +DOCUMENTATION = r""" --- module: zabbix_host_info short_description: Gather information about Zabbix host @@ -28,16 +28,17 @@ description: author: - "Michael Miko (@RedWhiteMiko)" requirements: - - "python >= 2.6" + - "python >= 3.9" options: host_name: description: - Name of the host in Zabbix. - host_name is the unique identifier used and cannot be updated using this module. - Required when I(host_ip) is not used. + - If neither host_name nor host_ip specified then all the hosts configured in Zabbix returned. required: false type: str - default: '' + default: "" host_ip: description: - Host interface IP of the host in Zabbix. @@ -67,19 +68,19 @@ options: extends_documentation_fragment: - community.zabbix.zabbix -''' +""" -EXAMPLES = r''' +EXAMPLES = r""" # If you want to use Username and Password to be authenticated by Zabbix Server - name: Set credentials to access Zabbix Server API - set_fact: + ansible.builtin.set_fact: ansible_user: Admin ansible_httpapi_pass: zabbix # If you want to use API token to be authenticated by Zabbix Server # https://www.zabbix.com/documentation/current/en/manual/web_interface/frontend_sections/administration/general#api-tokens - name: Set API token - set_fact: + ansible.builtin.set_fact: ansible_zabbix_auth_key: 8ec0d52432c15c91fcafe9888500cf9a607f44091ab554dbee860f6b44fac895 - name: Get host info @@ -90,12 +91,11 @@ EXAMPLES = r''' ansible_httpapi_port: 443 ansible_httpapi_use_ssl: true ansible_httpapi_validate_certs: false - ansible_zabbix_url_path: 'zabbixeu' # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu + ansible_zabbix_url_path: "zabbixeu" # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu ansible_host: zabbix-example-fqdn.org community.zabbix.zabbix_host_info: host_name: ExampleHost host_ip: 127.0.0.1 - timeout: 10 exact_match: no remove_duplicate: yes @@ -107,7 +107,7 @@ EXAMPLES = r''' ansible_httpapi_port: 443 ansible_httpapi_use_ssl: true ansible_httpapi_validate_certs: false - ansible_zabbix_url_path: 'zabbixeu' # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu + ansible_zabbix_url_path: "zabbixeu" # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu ansible_host: zabbix-example-fqdn.org community.zabbix.zabbix_host_info: host_name: ExampleHost @@ -115,10 +115,9 @@ EXAMPLES = r''' - os - tag host_ip: 127.0.0.1 - timeout: 10 exact_match: no remove_duplicate: yes -''' +""" from ansible.module_utils.basic import AnsibleModule @@ -130,17 +129,17 @@ import ansible_collections.community.zabbix.plugins.module_utils.helpers as zabb class Host(ZabbixBase): def get_hosts_by_host_name(self, host_name, exact_match, host_inventory): """ Get host by host name """ - search_key = 'search' + search_key = "search" if exact_match: - search_key = 'filter' + search_key = "filter" host_list = self._zapi.host.get({ - 'output': 'extend', - 'selectParentTemplates': ['name'], - search_key: {'host': [host_name]}, - 'selectInventory': host_inventory, - 'selectGroups': 'extend', - 'selectTags': 'extend', - 'selectMacros': 'extend' + "output": "extend", + "selectParentTemplates": ["name"], + search_key: {"host": [host_name]}, + "selectInventory": host_inventory, + "selectGroups": "extend", + "selectTags": "extend", + "selectMacros": "extend" }) if len(host_list) < 1: self._module.fail_json(msg="Host not found: %s" % host_name) @@ -150,9 +149,9 @@ class Host(ZabbixBase): def get_hosts_by_ip(self, host_ips, host_inventory): """ Get host by host ip(s) """ hostinterfaces = self._zapi.hostinterface.get({ - 'output': 'extend', - 'filter': { - 'ip': host_ips + "output": "extend", + "filter": { + "ip": host_ips } }) if len(hostinterfaces) < 1: @@ -160,15 +159,15 @@ class Host(ZabbixBase): host_list = [] for hostinterface in hostinterfaces: host = self._zapi.host.get({ - 'output': 'extend', - 'selectGroups': 'extend', - 'selectParentTemplates': ['name'], - 'hostids': hostinterface['hostid'], - 'selectInventory': host_inventory, - 'selectTags': 'extend', - 'selectMacros': 'extend' + "output": "extend", + "selectGroups": "extend", + "selectParentTemplates": ["name"], + "hostids": hostinterface["hostid"], + "selectInventory": host_inventory, + "selectTags": "extend", + "selectMacros": "extend" }) - host[0]['hostinterfaces'] = hostinterface + host[0]["hostinterfaces"] = hostinterface host_list.append(host[0]) return host_list @@ -177,51 +176,46 @@ class Host(ZabbixBase): unique_hosts = [] listed_hostnames = [] for zabbix_host in hosts: - if zabbix_host['name'] in listed_hostnames: + if zabbix_host["name"] in listed_hostnames: continue unique_hosts.append(zabbix_host) - listed_hostnames.append(zabbix_host['name']) + listed_hostnames.append(zabbix_host["name"]) return unique_hosts def main(): argument_spec = zabbix_utils.zabbix_common_argument_spec() argument_spec.update(dict( - host_name=dict(type='str', default='', required=False), - host_ip=dict(type='list', default=[], required=False), - exact_match=dict(type='bool', required=False, default=False), - remove_duplicate=dict(type='bool', required=False, default=True), - host_inventory=dict(type='list', default=[], required=False) + host_name=dict(type="str", default="", required=False), + host_ip=dict(type="list", default=[], required=False, elements="str"), + exact_match=dict(type="bool", required=False, default=False), + remove_duplicate=dict(type="bool", required=False, default=True), + host_inventory=dict(type="list", default=[], required=False, elements="str") )) module = AnsibleModule( argument_spec=argument_spec, supports_check_mode=True ) - if module._name == 'zabbix_host_facts': - module.deprecate("The 'zabbix_host_facts' module has been renamed to 'zabbix_host_info'", - collection_name="community.zabbix", version='2.0.0') # was 2.13 - zabbix_utils.require_creds_params(module) - - host_name = module.params['host_name'] - host_ips = module.params['host_ip'] - exact_match = module.params['exact_match'] - is_remove_duplicate = module.params['remove_duplicate'] - host_inventory = module.params['host_inventory'] + host_name = module.params["host_name"] + host_ips = module.params["host_ip"] + exact_match = module.params["exact_match"] + is_remove_duplicate = module.params["remove_duplicate"] + host_inventory = module.params["host_inventory"] if not host_inventory: - host_inventory = 'extend' + host_inventory = "extend" host = Host(module) - if host_name: + if host_name != "" or (host_name == "" and len(host_ips) == 0): hosts = host.get_hosts_by_host_name(host_name, exact_match, host_inventory) if is_remove_duplicate: hosts = host.delete_duplicate_hosts(hosts) extended_hosts = [] for zabbix_host in hosts: - zabbix_host['hostinterfaces'] = host._zapi.hostinterface.get({ - 'output': 'extend', 'hostids': zabbix_host['hostid'] + zabbix_host["hostinterfaces"] = host._zapi.hostinterface.get({ + "output": "extend", "hostids": zabbix_host["hostid"] }) extended_hosts.append(zabbix_host) module.exit_json(ok=True, hosts=extended_hosts) @@ -235,5 +229,5 @@ def main(): module.exit_json(ok=False, hosts=[], result="No Host present") -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/ansible_collections/community/zabbix/plugins/modules/zabbix_hostmacro.py b/ansible_collections/community/zabbix/plugins/modules/zabbix_hostmacro.py index 1fed5d889..802a00559 100644 --- a/ansible_collections/community/zabbix/plugins/modules/zabbix_hostmacro.py +++ b/ansible_collections/community/zabbix/plugins/modules/zabbix_hostmacro.py @@ -8,7 +8,7 @@ from __future__ import absolute_import, division, print_function __metaclass__ = type -DOCUMENTATION = r''' +DOCUMENTATION = r""" --- module: zabbix_hostmacro short_description: Create/update/delete Zabbix host macros @@ -18,7 +18,7 @@ author: - "Cove (@cove)" - Dean Hailin Song (!UNKNOWN) requirements: - - "python >= 2.6" + - "python >= 3.9" options: host_name: description: @@ -40,42 +40,45 @@ options: description: - Type of the host macro. - text (default) - - secret (Works only with Zabbix >= 5.0) - - vault (Works only with Zabbix >= 5.2) required: false - choices: ['text', 'secret', 'vault'] - default: 'text' + choices: ["text", "secret", "vault"] + default: "text" + macro_description: + description: + - Text Description of the global macro. + type: str + default: "" state: description: - State of the macro. - On C(present), it will create if macro does not exist or update the macro if the associated data is different. - On C(absent) will remove a macro if it exists. required: false - choices: ['present', 'absent'] + choices: ["present", "absent"] type: str default: "present" force: description: - Only updates an existing macro if set to C(yes). - default: 'yes' + default: "yes" type: bool extends_documentation_fragment: - community.zabbix.zabbix -''' +""" -EXAMPLES = r''' +EXAMPLES = r""" # If you want to use Username and Password to be authenticated by Zabbix Server - name: Set credentials to access Zabbix Server API - set_fact: + ansible.builtin.set_fact: ansible_user: Admin ansible_httpapi_pass: zabbix # If you want to use API token to be authenticated by Zabbix Server # https://www.zabbix.com/documentation/current/en/manual/web_interface/frontend_sections/administration/general#api-tokens - name: Set API token - set_fact: + ansible.builtin.set_fact: ansible_zabbix_auth_key: 8ec0d52432c15c91fcafe9888500cf9a607f44091ab554dbee860f6b44fac895 - name: Create new host macro or update an existing macro's value @@ -86,12 +89,13 @@ EXAMPLES = r''' ansible_httpapi_port: 443 ansible_httpapi_use_ssl: true ansible_httpapi_validate_certs: false - ansible_zabbix_url_path: 'zabbixeu' # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu + ansible_zabbix_url_path: "zabbixeu" # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu ansible_host: zabbix-example-fqdn.org community.zabbix.zabbix_hostmacro: host_name: ExampleHost macro_name: EXAMPLE.MACRO macro_value: Example value + macro_description: Example description state: present # Values with curly brackets need to be quoted otherwise they will be interpreted as a dictionary @@ -103,12 +107,13 @@ EXAMPLES = r''' ansible_httpapi_port: 443 ansible_httpapi_use_ssl: true ansible_httpapi_validate_certs: false - ansible_zabbix_url_path: 'zabbixeu' # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu + ansible_zabbix_url_path: "zabbixeu" # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu ansible_host: zabbix-example-fqdn.org community.zabbix.zabbix_hostmacro: host_name: ExampleHost macro_name: "{$EXAMPLE.MACRO}" macro_value: Example value + macro_description: Example description state: present - name: Delete existing host macro @@ -119,19 +124,18 @@ EXAMPLES = r''' ansible_httpapi_port: 443 ansible_httpapi_use_ssl: true ansible_httpapi_validate_certs: false - ansible_zabbix_url_path: 'zabbixeu' # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu + ansible_zabbix_url_path: "zabbixeu" # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu ansible_host: zabbix-example-fqdn.org community.zabbix.zabbix_hostmacro: host_name: ExampleHost macro_name: "{$EXAMPLE.MACRO}" state: absent -''' +""" from ansible.module_utils.basic import AnsibleModule from ansible_collections.community.zabbix.plugins.module_utils.base import ZabbixBase -from ansible.module_utils.compat.version import LooseVersion import ansible_collections.community.zabbix.plugins.module_utils.helpers as zabbix_utils @@ -140,11 +144,11 @@ class HostMacro(ZabbixBase): # get host id by host name def get_host_id(self, host_name): try: - host_list = self._zapi.host.get({'output': 'extend', 'filter': {'host': host_name}}) + host_list = self._zapi.host.get({"output": "extend", "filter": {"host": host_name}}) if len(host_list) < 1: self._module.fail_json(msg="Host not found: %s" % host_name) else: - host_id = host_list[0]['hostid'] + host_id = host_list[0]["hostid"] return host_id except Exception as e: self._module.fail_json(msg="Failed to get the host %s id: %s." % (host_name, e)) @@ -153,7 +157,7 @@ class HostMacro(ZabbixBase): def get_host_macro(self, macro_name, host_id): try: host_macro_list = self._zapi.usermacro.get( - {"output": "extend", "selectSteps": "extend", 'hostids': [host_id], 'filter': {'macro': macro_name}}) + {"output": "extend", "selectSteps": "extend", "hostids": [host_id], "filter": {"macro": macro_name}}) if len(host_macro_list) > 0: return host_macro_list[0] return None @@ -161,43 +165,34 @@ class HostMacro(ZabbixBase): self._module.fail_json(msg="Failed to get host macro %s: %s" % (macro_name, e)) # create host macro - def create_host_macro(self, macro_name, macro_value, macro_type, host_id): + def create_host_macro(self, macro_name, macro_value, macro_type, macro_description, host_id): try: if self._module.check_mode: self._module.exit_json(changed=True) - if LooseVersion(self._zbx_api_version) >= LooseVersion('5.0'): - self._zapi.usermacro.create({'hostid': host_id, 'macro': macro_name, 'value': macro_value, 'type': macro_type}) - else: - self._zapi.usermacro.create({'hostid': host_id, 'macro': macro_name, 'value': macro_value}) + self._zapi.usermacro.create({"hostid": host_id, "macro": macro_name, "value": macro_value, "type": macro_type, "description": macro_description}) self._module.exit_json(changed=True, result="Successfully added host macro %s" % macro_name) except Exception as e: self._module.fail_json(msg="Failed to create host macro %s: %s" % (macro_name, e)) # update host macro - def update_host_macro(self, host_macro_obj, macro_name, macro_value, macro_type): - host_macro_id = host_macro_obj['hostmacroid'] - if host_macro_obj['macro'] == macro_name: - if LooseVersion(self._zbx_api_version) >= LooseVersion('5.0'): - # no change only when macro type == 0. when type = 1 or 2 zabbix will not output value of it. - if host_macro_obj['type'] == '0' and macro_type == '0' and host_macro_obj['value'] == macro_value: - self._module.exit_json(changed=False, result="Host macro %s already up to date" % macro_name) - else: - if host_macro_obj['value'] == macro_value: - self._module.exit_json(changed=False, result="Host macro %s already up to date" % macro_name) + def update_host_macro(self, host_macro_obj, macro_name, macro_value, macro_type, macro_description): + host_macro_id = host_macro_obj["hostmacroid"] + if host_macro_obj["macro"] == macro_name: + # no change only when macro type == 0. when type = 1 or 2 zabbix will not output value of it. + if (host_macro_obj["type"] == "0" and macro_type == "0" and host_macro_obj["value"] == macro_value + and host_macro_obj["description"] == macro_description): + self._module.exit_json(changed=False, result="Host macro %s already up to date" % macro_name) try: if self._module.check_mode: self._module.exit_json(changed=True) - if LooseVersion(self._zbx_api_version) >= LooseVersion('5.0'): - self._zapi.usermacro.update({'hostmacroid': host_macro_id, 'value': macro_value, 'type': macro_type}) - else: - self._zapi.usermacro.update({'hostmacroid': host_macro_id, 'value': macro_value}) + self._zapi.usermacro.update({"hostmacroid": host_macro_id, "value": macro_value, "type": macro_type, "description": macro_description}) self._module.exit_json(changed=True, result="Successfully updated host macro %s" % macro_name) except Exception as e: self._module.fail_json(msg="Failed to update host macro %s: %s" % (macro_name, e)) # delete host macro def delete_host_macro(self, host_macro_obj, macro_name): - host_macro_id = host_macro_obj['hostmacroid'] + host_macro_id = host_macro_obj["hostmacroid"] try: if self._module.check_mode: self._module.exit_json(changed=True) @@ -209,16 +204,16 @@ class HostMacro(ZabbixBase): def normalize_macro_name(macro_name): # Zabbix handles macro names in upper case characters - if ':' in macro_name: - macro_name = ':'.join([macro_name.split(':')[0].upper(), ':'.join(macro_name.split(':')[1:])]) + if ":" in macro_name: + macro_name = ":".join([macro_name.split(":")[0].upper(), ":".join(macro_name.split(":")[1:])]) else: macro_name = macro_name.upper() # Valid format for macro is {$MACRO} - if not macro_name.startswith('{$'): - macro_name = '{$' + macro_name - if not macro_name.endswith('}'): - macro_name = macro_name + '}' + if not macro_name.startswith("{$"): + macro_name = "{$" + macro_name + if not macro_name.endswith("}"): + macro_name = macro_name + "}" return macro_name @@ -226,38 +221,34 @@ def normalize_macro_name(macro_name): def main(): argument_spec = zabbix_utils.zabbix_common_argument_spec() argument_spec.update(dict( - host_name=dict(type='str', required=True), - macro_name=dict(type='str', required=True), - macro_value=dict(type='str', required=False), - macro_type=dict(type='str', default='text', choices=['text', 'secret', 'vault']), - state=dict(type='str', default='present', choices=['present', 'absent']), - force=dict(type='bool', default=True) + host_name=dict(type="str", required=True), + macro_name=dict(type="str", required=True), + macro_value=dict(type="str", required=False), + macro_type=dict(type="str", default="text", choices=["text", "secret", "vault"]), + macro_description=dict(type="str", default=""), + state=dict(type="str", default="present", choices=["present", "absent"]), + force=dict(type="bool", default=True) )) module = AnsibleModule( argument_spec=argument_spec, required_if=[ - ['state', 'present', ['macro_value']] + ["state", "present", ["macro_value"]] ], supports_check_mode=True ) - zabbix_utils.require_creds_params(module) - - for p in ['server_url', 'login_user', 'login_password', 'timeout', 'validate_certs']: - if p in module.params and not module.params[p] is None: - module.warn('Option "%s" is deprecated with the move to httpapi connection and will be removed in the next release' % p) - - host_name = module.params['host_name'] - macro_name = normalize_macro_name(module.params['macro_name']) - macro_value = module.params['macro_value'] - state = module.params['state'] - force = module.params['force'] - if module.params['macro_type'] == 'secret': - macro_type = '1' - elif module.params['macro_type'] == 'vault': - macro_type = '2' + host_name = module.params["host_name"] + macro_name = normalize_macro_name(module.params["macro_name"]) + macro_value = module.params["macro_value"] + macro_description = module.params["macro_description"] + state = module.params["state"] + force = module.params["force"] + if module.params["macro_type"] == "secret": + macro_type = "1" + elif module.params["macro_type"] == "vault": + macro_type = "2" else: - macro_type = '0' + macro_type = "0" host_macro_class_obj = HostMacro(module) @@ -265,7 +256,7 @@ def main(): host_id = host_macro_class_obj.get_host_id(host_name) host_macro_obj = host_macro_class_obj.get_host_macro(macro_name, host_id) - if state == 'absent': + if state == "absent": if not host_macro_obj: module.exit_json(changed=False, msg="Host Macro %s does not exist" % macro_name) else: @@ -274,13 +265,13 @@ def main(): else: if not host_macro_obj: # create host macro - host_macro_class_obj.create_host_macro(macro_name, macro_value, macro_type, host_id) + host_macro_class_obj.create_host_macro(macro_name, macro_value, macro_type, macro_description, host_id) elif force: # update host macro - host_macro_class_obj.update_host_macro(host_macro_obj, macro_name, macro_value, macro_type) + host_macro_class_obj.update_host_macro(host_macro_obj, macro_name, macro_value, macro_type, macro_description) else: module.exit_json(changed=False, result="Host macro %s already exists and force is set to no" % macro_name) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/ansible_collections/community/zabbix/plugins/modules/zabbix_housekeeping.py b/ansible_collections/community/zabbix/plugins/modules/zabbix_housekeeping.py index 901ff965a..3f6e5d733 100644 --- a/ansible_collections/community/zabbix/plugins/modules/zabbix_housekeeping.py +++ b/ansible_collections/community/zabbix/plugins/modules/zabbix_housekeeping.py @@ -8,7 +8,7 @@ from __future__ import absolute_import, division, print_function __metaclass__ = type -DOCUMENTATION = ''' +DOCUMENTATION = """ --- module: zabbix_housekeeping @@ -21,7 +21,7 @@ author: - ONODERA Masaru(@masa-orca) requirements: - - "python >= 2.6" + - "python >= 3.9" version_added: 1.6.0 @@ -39,7 +39,6 @@ options: hk_events_service: description: - Storage period of service data (e.g. 365d). - - This parameter is available since Zabbix 6.0. required: false type: str hk_events_internal: @@ -128,24 +127,21 @@ options: required: false type: str -notes: - - Zabbix 5.2 version and higher are supported. - extends_documentation_fragment: - community.zabbix.zabbix -''' +""" -EXAMPLES = ''' +EXAMPLES = """ # If you want to use Username and Password to be authenticated by Zabbix Server - name: Set credentials to access Zabbix Server API - set_fact: + ansible.builtin.set_fact: ansible_user: Admin ansible_httpapi_pass: zabbix # If you want to use API token to be authenticated by Zabbix Server # https://www.zabbix.com/documentation/current/en/manual/web_interface/frontend_sections/administration/general#api-tokens - name: Set API token - set_fact: + ansible.builtin.set_fact: ansible_zabbix_auth_key: 8ec0d52432c15c91fcafe9888500cf9a607f44091ab554dbee860f6b44fac895 - name: Update housekeeping all parameter @@ -156,7 +152,7 @@ EXAMPLES = ''' ansible_httpapi_port: 443 ansible_httpapi_use_ssl: true ansible_httpapi_validate_certs: false - ansible_zabbix_url_path: 'zabbixeu' # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu + ansible_zabbix_url_path: "zabbixeu" # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu ansible_host: zabbix-example-fqdn.org community.zabbix.zabbix_housekeeping: login_user: Admin @@ -181,35 +177,29 @@ EXAMPLES = ''' hk_trends: 365d compression_status: off compress_older: 7d -''' +""" -RETURN = ''' +RETURN = """ msg: description: The result of the operation returned: success type: str - sample: 'Successfully update housekeeping setting' -''' + sample: "Successfully update housekeeping setting" +""" import re from ansible.module_utils.basic import AnsibleModule from ansible_collections.community.zabbix.plugins.module_utils.base import ZabbixBase -from ansible.module_utils.compat.version import LooseVersion import ansible_collections.community.zabbix.plugins.module_utils.helpers as zabbix_utils class Housekeeping(ZabbixBase): - def __init__(self, module, zbx=None, zapi_wrapper=None): - super(Housekeeping, self).__init__(module, zbx, zapi_wrapper) - if LooseVersion(self._zbx_api_version) < LooseVersion('5.2.0'): - module.fail_json(msg="This module doesn't support Zabbix versions lower than 5.2.0") - # get housekeeping setting def get_housekeeping(self): try: - return self._zapi.housekeeping.get({'output': 'extend'}) + return self._zapi.housekeeping.get({"output": "extend"}) except Exception as e: self._module.fail_json(msg="Failed to get housekeeping setting: %s" % e) @@ -247,78 +237,75 @@ class Housekeeping(ZabbixBase): params = {} if isinstance(hk_events_mode, bool): - params['hk_events_mode'] = str(int(hk_events_mode)) + params["hk_events_mode"] = str(int(hk_events_mode)) if hk_events_trigger: - self.check_time_parameter('hk_events_trigger', hk_events_trigger) - params['hk_events_trigger'] = hk_events_trigger + self.check_time_parameter("hk_events_trigger", hk_events_trigger) + params["hk_events_trigger"] = hk_events_trigger if hk_events_service: - if LooseVersion(self._zbx_api_version) < LooseVersion('6.0'): - self._module.warn('hk_events_service is ignored with <= Zabbix 5.4.') - else: - self.check_time_parameter('hk_events_service', hk_events_service) - params['hk_events_service'] = hk_events_service + self.check_time_parameter("hk_events_service", hk_events_service) + params["hk_events_service"] = hk_events_service if hk_events_internal: - self.check_time_parameter('hk_events_internal', hk_events_internal) - params['hk_events_internal'] = hk_events_internal + self.check_time_parameter("hk_events_internal", hk_events_internal) + params["hk_events_internal"] = hk_events_internal if hk_events_discovery: - self.check_time_parameter('hk_events_discovery', hk_events_discovery) - params['hk_events_discovery'] = hk_events_discovery + self.check_time_parameter("hk_events_discovery", hk_events_discovery) + params["hk_events_discovery"] = hk_events_discovery if hk_events_autoreg: - self.check_time_parameter('hk_events_autoreg', hk_events_autoreg) - params['hk_events_autoreg'] = hk_events_autoreg + self.check_time_parameter("hk_events_autoreg", hk_events_autoreg) + params["hk_events_autoreg"] = hk_events_autoreg if isinstance(hk_services_mode, bool): - params['hk_services_mode'] = str(int(hk_services_mode)) + params["hk_services_mode"] = str(int(hk_services_mode)) if hk_services: - self.check_time_parameter('hk_services', hk_services) - params['hk_services'] = hk_services + self.check_time_parameter("hk_services", hk_services) + params["hk_services"] = hk_services if isinstance(hk_audit_mode, bool): - params['hk_audit_mode'] = str(int(hk_audit_mode)) + params["hk_audit_mode"] = str(int(hk_audit_mode)) if hk_audit: - self.check_time_parameter('hk_audit', hk_audit) - params['hk_audit'] = hk_audit + self.check_time_parameter("hk_audit", hk_audit) + params["hk_audit"] = hk_audit if isinstance(hk_sessions_mode, bool): - params['hk_sessions_mode'] = str(int(hk_sessions_mode)) + params["hk_sessions_mode"] = str(int(hk_sessions_mode)) if hk_sessions: - self.check_time_parameter('hk_sessions', hk_sessions) - params['hk_sessions'] = hk_sessions + self.check_time_parameter("hk_sessions", hk_sessions) + params["hk_sessions"] = hk_sessions if isinstance(hk_history_mode, bool): - params['hk_history_mode'] = str(int(hk_history_mode)) + params["hk_history_mode"] = str(int(hk_history_mode)) if isinstance(hk_history_global, bool): - params['hk_history_global'] = str(int(hk_history_global)) + params["hk_history_global"] = str(int(hk_history_global)) if hk_history: - self.check_time_parameter('hk_history', hk_history) - params['hk_history'] = hk_history + self.check_time_parameter("hk_history", hk_history) + params["hk_history"] = hk_history if isinstance(hk_trends_mode, bool): - params['hk_trends_mode'] = str(int(hk_trends_mode)) + params["hk_trends_mode"] = str(int(hk_trends_mode)) if isinstance(hk_trends_global, bool): - params['hk_trends_global'] = str(int(hk_trends_global)) + params["hk_trends_global"] = str(int(hk_trends_global)) if hk_trends: - self.check_time_parameter('hk_trends', hk_trends) - params['hk_trends'] = hk_trends + self.check_time_parameter("hk_trends", hk_trends) + params["hk_trends"] = hk_trends if isinstance(compression_status, bool): - params['compression_status'] = str(int(compression_status)) + params["compression_status"] = str(int(compression_status)) if compress_older: - self.check_time_parameter('compress_older', compress_older) - params['compress_older'] = compress_older + self.check_time_parameter("compress_older", compress_older) + params["compress_older"] = compress_older future_housekeeping = current_housekeeping.copy() future_housekeeping.update(params) @@ -338,58 +325,52 @@ class Housekeeping(ZabbixBase): def main(): argument_spec = zabbix_utils.zabbix_common_argument_spec() argument_spec.update(dict( - hk_events_mode=dict(type='bool'), - hk_events_trigger=dict(type='str'), - hk_events_service=dict(type='str'), - hk_events_internal=dict(type='str'), - hk_events_discovery=dict(type='str'), - hk_events_autoreg=dict(type='str'), - hk_services_mode=dict(type='bool'), - hk_services=dict(type='str'), - hk_audit_mode=dict(type='bool'), - hk_audit=dict(type='str'), - hk_sessions_mode=dict(type='bool'), - hk_sessions=dict(type='str'), - hk_history_mode=dict(type='bool'), - hk_history_global=dict(type='bool'), - hk_history=dict(type='str'), - hk_trends_mode=dict(type='bool'), - hk_trends_global=dict(type='bool'), - hk_trends=dict(type='str'), - compression_status=dict(type='bool'), - compress_older=dict(type='str') + hk_events_mode=dict(type="bool"), + hk_events_trigger=dict(type="str"), + hk_events_service=dict(type="str"), + hk_events_internal=dict(type="str"), + hk_events_discovery=dict(type="str"), + hk_events_autoreg=dict(type="str"), + hk_services_mode=dict(type="bool"), + hk_services=dict(type="str"), + hk_audit_mode=dict(type="bool"), + hk_audit=dict(type="str"), + hk_sessions_mode=dict(type="bool"), + hk_sessions=dict(type="str"), + hk_history_mode=dict(type="bool"), + hk_history_global=dict(type="bool"), + hk_history=dict(type="str"), + hk_trends_mode=dict(type="bool"), + hk_trends_global=dict(type="bool"), + hk_trends=dict(type="str"), + compression_status=dict(type="bool"), + compress_older=dict(type="str") )) module = AnsibleModule( argument_spec=argument_spec, supports_check_mode=True ) - zabbix_utils.require_creds_params(module) - - for p in ['server_url', 'login_user', 'login_password', 'timeout', 'validate_certs']: - if p in module.params and not module.params[p] is None: - module.warn('Option "%s" is deprecated with the move to httpapi connection and will be removed in the next release' % p) - - hk_events_mode = module.params['hk_events_mode'] - hk_events_trigger = module.params['hk_events_trigger'] - hk_events_service = module.params['hk_events_service'] - hk_events_internal = module.params['hk_events_internal'] - hk_events_discovery = module.params['hk_events_discovery'] - hk_events_autoreg = module.params['hk_events_autoreg'] - hk_services_mode = module.params['hk_services_mode'] - hk_services = module.params['hk_services'] - hk_audit_mode = module.params['hk_audit_mode'] - hk_audit = module.params['hk_audit'] - hk_sessions_mode = module.params['hk_sessions_mode'] - hk_sessions = module.params['hk_sessions'] - hk_history_mode = module.params['hk_history_mode'] - hk_history_global = module.params['hk_history_global'] - hk_history = module.params['hk_history'] - hk_trends_mode = module.params['hk_trends_mode'] - hk_trends_global = module.params['hk_trends_global'] - hk_trends = module.params['hk_trends'] - compression_status = module.params['compression_status'] - compress_older = module.params['compress_older'] + hk_events_mode = module.params["hk_events_mode"] + hk_events_trigger = module.params["hk_events_trigger"] + hk_events_service = module.params["hk_events_service"] + hk_events_internal = module.params["hk_events_internal"] + hk_events_discovery = module.params["hk_events_discovery"] + hk_events_autoreg = module.params["hk_events_autoreg"] + hk_services_mode = module.params["hk_services_mode"] + hk_services = module.params["hk_services"] + hk_audit_mode = module.params["hk_audit_mode"] + hk_audit = module.params["hk_audit"] + hk_sessions_mode = module.params["hk_sessions_mode"] + hk_sessions = module.params["hk_sessions"] + hk_history_mode = module.params["hk_history_mode"] + hk_history_global = module.params["hk_history_global"] + hk_history = module.params["hk_history"] + hk_trends_mode = module.params["hk_trends_mode"] + hk_trends_global = module.params["hk_trends_global"] + hk_trends = module.params["hk_trends"] + compression_status = module.params["compression_status"] + compress_older = module.params["compress_older"] housekeeping = Housekeeping(module) @@ -419,5 +400,5 @@ def main(): ) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/ansible_collections/community/zabbix/plugins/modules/zabbix_maintenance.py b/ansible_collections/community/zabbix/plugins/modules/zabbix_maintenance.py index 139db5057..a2c635eb0 100644 --- a/ansible_collections/community/zabbix/plugins/modules/zabbix_maintenance.py +++ b/ansible_collections/community/zabbix/plugins/modules/zabbix_maintenance.py @@ -8,7 +8,7 @@ from __future__ import absolute_import, division, print_function __metaclass__ = type -DOCUMENTATION = r''' +DOCUMENTATION = r""" --- module: zabbix_maintenance short_description: Create Zabbix maintenance windows @@ -16,7 +16,7 @@ description: - This module will let you create Zabbix maintenance windows. author: "Alexander Bulimov (@abulimov)" requirements: - - "python >= 2.6" + - "python >= 3.9" options: state: description: @@ -57,14 +57,13 @@ options: description: - Type of maintenance. With data collection, or without. type: bool - default: 'yes' + default: "yes" visible_name: description: - Type of zabbix host name to use for identifying hosts to include in the maintenance. - I(visible_name=yes) to search by visible name, I(visible_name=no) to search by technical name. type: bool - default: 'yes' - version_added: '2.0.0' + default: "yes" tags: description: - List of tags to assign to the hosts in maintenance. @@ -81,7 +80,7 @@ options: description: - Value of the tag. type: str - default: '' + default: "" operator: description: - Condition operator. @@ -101,19 +100,19 @@ notes: - Module creates maintenance window from now() to now() + minutes, so if Zabbix server's time and host's time are not synchronized, you will get strange results. -''' +""" -EXAMPLES = r''' +EXAMPLES = r""" # If you want to use Username and Password to be authenticated by Zabbix Server - name: Set credentials to access Zabbix Server API - set_fact: + ansible.builtin.set_fact: ansible_user: Admin ansible_httpapi_pass: zabbix # If you want to use API token to be authenticated by Zabbix Server # https://www.zabbix.com/documentation/current/en/manual/web_interface/frontend_sections/administration/general#api-tokens - name: Set API token - set_fact: + ansible.builtin.set_fact: ansible_zabbix_auth_key: 8ec0d52432c15c91fcafe9888500cf9a607f44091ab554dbee860f6b44fac895 - name: Create a named maintenance window for host www1 for 90 minutes @@ -124,7 +123,7 @@ EXAMPLES = r''' ansible_httpapi_port: 443 ansible_httpapi_use_ssl: true ansible_httpapi_validate_certs: false - ansible_zabbix_url_path: 'zabbixeu' # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu + ansible_zabbix_url_path: "zabbixeu" # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu ansible_host: zabbix-example-fqdn.org community.zabbix.zabbix_maintenance: name: Update of www1 @@ -140,7 +139,7 @@ EXAMPLES = r''' ansible_httpapi_port: 443 ansible_httpapi_use_ssl: true ansible_httpapi_validate_certs: false - ansible_zabbix_url_path: 'zabbixeu' # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu + ansible_zabbix_url_path: "zabbixeu" # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu ansible_host: zabbix-example-fqdn.org community.zabbix.zabbix_maintenance: name: Update of www1 @@ -165,7 +164,7 @@ EXAMPLES = r''' ansible_httpapi_port: 443 ansible_httpapi_use_ssl: true ansible_httpapi_validate_certs: false - ansible_zabbix_url_path: 'zabbixeu' # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu + ansible_zabbix_url_path: "zabbixeu" # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu ansible_host: zabbix-example-fqdn.org community.zabbix.zabbix_maintenance: name: update @@ -183,12 +182,12 @@ EXAMPLES = r''' ansible_httpapi_port: 443 ansible_httpapi_use_ssl: true ansible_httpapi_validate_certs: false - ansible_zabbix_url_path: 'zabbixeu' # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu + ansible_zabbix_url_path: "zabbixeu" # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu ansible_host: zabbix-example-fqdn.org community.zabbix.zabbix_maintenance: name: Test1 state: absent -''' +""" import datetime import time @@ -197,81 +196,65 @@ from ansible.module_utils.basic import AnsibleModule from ansible_collections.community.zabbix.plugins.module_utils.base import ZabbixBase import ansible_collections.community.zabbix.plugins.module_utils.helpers as zabbix_utils -from ansible.module_utils.compat.version import LooseVersion class MaintenanceModule(ZabbixBase): def create_maintenance(self, group_ids, host_ids, start_time, maintenance_type, period, name, desc, tags): end_time = start_time + period - try: - parameters = { - "groupids": group_ids, - "hostids": host_ids, - "name": name, - "maintenance_type": maintenance_type, - "active_since": str(start_time), - "active_till": str(end_time), - "description": desc, - "timeperiods": [{ - "timeperiod_type": "0", - "start_date": str(start_time), - "period": str(period), - }] - } - if tags is not None: - parameters['tags'] = tags - self._zapi.maintenance.create(parameters) - # zabbix_api can call sys.exit() so we need to catch SystemExit here - except (Exception, SystemExit) as e: - return 1, None, str(e) + parameters = { + "groupids": group_ids, + "hostids": host_ids, + "name": name, + "maintenance_type": maintenance_type, + "active_since": str(start_time), + "active_till": str(end_time), + "description": desc, + "timeperiods": [{ + "timeperiod_type": "0", + "start_date": str(start_time), + "period": str(period), + }] + } + if tags is not None: + parameters["tags"] = tags + self._zapi.maintenance.create(parameters) return 0, None, None def update_maintenance(self, maintenance_id, group_ids, host_ids, start_time, maintenance_type, period, desc, tags): end_time = start_time + period - try: - parameters = { - "maintenanceid": maintenance_id, - "groupids": group_ids, - "hostids": host_ids, - "maintenance_type": maintenance_type, - "active_since": str(start_time), - "active_till": str(end_time), - "description": desc, - "timeperiods": [{ - "timeperiod_type": "0", - "start_date": str(start_time), - "period": str(period), - }] - } - if tags is not None: - parameters['tags'] = tags - else: - if LooseVersion(self._zbx_api_version) < LooseVersion('6.0'): - parameters['tags'] = [] - self._zapi.maintenance.update(parameters) - # zabbix_api can call sys.exit() so we need to catch SystemExit here - except (Exception, SystemExit) as e: - return 1, None, str(e) + parameters = { + "maintenanceid": maintenance_id, + "groupids": group_ids, + "hostids": host_ids, + "maintenance_type": maintenance_type, + "active_since": str(start_time), + "active_till": str(end_time), + "description": desc, + "timeperiods": [{ + "timeperiod_type": "0", + "start_date": str(start_time), + "period": str(period), + }] + } + if tags is not None: + parameters["tags"] = tags + self._zapi.maintenance.update(parameters) return 0, None, None def get_maintenance(self, name): - try: - maintenances = self._zapi.maintenance.get( + maintenances = self._zapi.maintenance.get( + { + "filter": { - "filter": - { - "name": name, - }, - "selectGroups": "extend", - "selectHosts": "extend", - "selectTags": "extend" - } - ) - # zabbix_api can call sys.exit() so we need to catch SystemExit here - except (Exception, SystemExit) as e: - return 1, None, str(e) + "name": name, + }, + "selectGroups": "extend", + "selectHosts": "extend", + "selectTags": "extend" + } + ) for maintenance in maintenances: maintenance["groupids"] = [group["groupid"] for group @@ -283,29 +266,21 @@ class MaintenanceModule(ZabbixBase): return 0, None, None def delete_maintenance(self, maintenance_id): - try: - self._zapi.maintenance.delete([maintenance_id]) - # zabbix_api can call sys.exit() so we need to catch SystemExit here - except (Exception, SystemExit) as e: - return 1, None, str(e) + self._zapi.maintenance.delete([maintenance_id]) return 0, None, None def get_group_ids(self, host_groups): group_ids = [] for group in host_groups: - try: - result = self._zapi.hostgroup.get( + result = self._zapi.hostgroup.get( + { + "output": "extend", + "filter": { - "output": "extend", - "filter": - { - "name": group - } + "name": group } - ) - # zabbix_api can call sys.exit() so we need to catch SystemExit here - except (Exception, SystemExit) as e: - return 1, None, str(e) + } + ) if not result: return 1, None, "Group id for group %s not found" % group @@ -317,19 +292,15 @@ class MaintenanceModule(ZabbixBase): def get_host_ids(self, host_names, zabbix_host): host_ids = [] for host in host_names: - try: - result = self._zapi.host.get( + result = self._zapi.host.get( + { + "output": "extend", + "filter": { - "output": "extend", - "filter": - { - zabbix_host: host - } + zabbix_host: host } - ) - # zabbix_api can call sys.exit() so we need to catch SystemExit here - except (Exception, SystemExit) as e: - return 1, None, str(e) + } + ) if not result: return 1, None, "Host id for host %s not found" % host @@ -350,35 +321,35 @@ class MaintenanceModule(ZabbixBase): return True if str(int(start_time + period)) != maintenance["active_till"]: return True - if str(desc) != maintenance['description']: + if str(desc) != maintenance["description"]: return True - if tags is not None and 'tags' in maintenance: - if sorted(tags, key=lambda k: k['tag']) != sorted(maintenance['tags'], key=lambda k: k['tag']): + if tags is not None and "tags" in maintenance: + if sorted(tags, key=lambda k: k["tag"]) != sorted(maintenance["tags"], key=lambda k: k["tag"]): return True def main(): argument_spec = zabbix_utils.zabbix_common_argument_spec() argument_spec.update(dict( - state=dict(type='str', required=False, default='present', - choices=['present', 'absent']), - host_names=dict(type='list', required=False, - default=None, aliases=['host_name']), - minutes=dict(type='int', required=False, default=10), - host_groups=dict(type='list', required=False, - default=None, aliases=['host_group']), - name=dict(type='str', required=True), - desc=dict(type='str', required=False, default="Created by Ansible"), - collect_data=dict(type='bool', required=False, default=True), - visible_name=dict(type='bool', required=False, default=True), + state=dict(type="str", required=False, default="present", + choices=["present", "absent"]), + host_names=dict(type="list", required=False, + default=None, aliases=["host_name"], elements="str"), + minutes=dict(type="int", required=False, default=10), + host_groups=dict(type="list", required=False, + default=None, aliases=["host_group"], elements="str"), + name=dict(type="str", required=True), + desc=dict(type="str", required=False, default="Created by Ansible"), + collect_data=dict(type="bool", required=False, default=True), + visible_name=dict(type="bool", required=False, default=True), tags=dict( - type='list', - elements='dict', + type="list", + elements="dict", required=False, options=dict( - tag=dict(type='str', required=True), - operator=dict(type='int', default=2), - value=dict(type='str', default='') + tag=dict(type="str", required=True), + operator=dict(type="int", default=2), + value=dict(type="str", default="") ) ) )) @@ -387,23 +358,17 @@ def main(): supports_check_mode=True ) - zabbix_utils.require_creds_params(module) - - for p in ['server_url', 'login_user', 'login_password', 'timeout', 'validate_certs']: - if p in module.params and not module.params[p] is None: - module.warn('Option "%s" is deprecated with the move to httpapi connection and will be removed in the next release' % p) - maint = MaintenanceModule(module) - host_names = module.params['host_names'] - host_groups = module.params['host_groups'] - state = module.params['state'] - minutes = module.params['minutes'] - name = module.params['name'] - desc = module.params['desc'] - collect_data = module.params['collect_data'] - visible_name = module.params['visible_name'] - tags = module.params['tags'] + host_names = module.params["host_names"] + host_groups = module.params["host_groups"] + state = module.params["state"] + minutes = module.params["minutes"] + name = module.params["name"] + desc = module.params["desc"] + collect_data = module.params["collect_data"] + visible_name = module.params["visible_name"] + tags = module.params["tags"] if collect_data: maintenance_type = 0 @@ -494,5 +459,5 @@ def main(): module.exit_json(changed=changed) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/ansible_collections/community/zabbix/plugins/modules/zabbix_map.py b/ansible_collections/community/zabbix/plugins/modules/zabbix_map.py index 175b96df6..a0f3cc655 100644 --- a/ansible_collections/community/zabbix/plugins/modules/zabbix_map.py +++ b/ansible_collections/community/zabbix/plugins/modules/zabbix_map.py @@ -7,7 +7,7 @@ from __future__ import absolute_import, division, print_function __metaclass__ = type -DOCUMENTATION = r''' +DOCUMENTATION = r""" --- module: zabbix_map author: @@ -37,7 +37,7 @@ description: C(zbx_trigger_color) contains indicator color specified either as CSS3 name or as a hexadecimal code starting with C(#). C(zbx_trigger_draw_style) contains indicator draw style. Possible values are the same as for C(zbx_draw_style)." requirements: - - "python >= 2.6" + - "python >= 3.9" - pydotplus - webcolors - Pillow @@ -61,7 +61,7 @@ options: - On C(present), it will create if map does not exist or update the map if the associated data is different. - On C(absent) will remove the map if it exists. required: false - choices: ['present', 'absent'] + choices: ["present", "absent"] default: "present" type: str width: @@ -98,7 +98,7 @@ options: description: - Map element label type. required: false - choices: ['label', 'ip', 'name', 'status', 'nothing', 'custom'] + choices: ["label", "ip", "name", "status", "nothing", "custom"] default: "name" type: str default_image: @@ -111,21 +111,21 @@ options: extends_documentation_fragment: - community.zabbix.zabbix -''' +""" -RETURN = r''' # ''' +RETURN = r""" # """ -EXAMPLES = r''' +EXAMPLES = r""" # If you want to use Username and Password to be authenticated by Zabbix Server - name: Set credentials to access Zabbix Server API - set_fact: + ansible.builtin.set_fact: ansible_user: Admin ansible_httpapi_pass: zabbix # If you want to use API token to be authenticated by Zabbix Server # https://www.zabbix.com/documentation/current/en/manual/web_interface/frontend_sections/administration/general#api-tokens - name: Set API token - set_fact: + ansible.builtin.set_fact: ansible_zabbix_auth_key: 8ec0d52432c15c91fcafe9888500cf9a607f44091ab554dbee860f6b44fac895 ### @@ -139,7 +139,7 @@ EXAMPLES = r''' ### ### Each inventory host is present in Zabbix with a matching name. ### -### Contents of 'map.j2': +### Contents of "map.j2": # digraph G { # graph [layout=dot splines=false overlap=scale] # INTERNET [zbx_url="Google:https://google.com" zbx_image="Cloud_(96)"] @@ -168,7 +168,7 @@ EXAMPLES = r''' # } # } ### -### Create Zabbix map "Demo Map" made of template 'map.j2' +### Create Zabbix map "Demo Map" made of template "map.j2" - name: Create Zabbix map # set task level variables as we change ansible_connection plugin here vars: @@ -177,7 +177,7 @@ EXAMPLES = r''' ansible_httpapi_port: 443 ansible_httpapi_use_ssl: true ansible_httpapi_validate_certs: false - ansible_zabbix_url_path: 'zabbixeu' # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu + ansible_zabbix_url_path: "zabbixeu" # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu ansible_host: zabbix-example-fqdn.org community.zabbix.zabbix_map: name: Demo map @@ -189,7 +189,7 @@ EXAMPLES = r''' label_type: label delegate_to: localhost run_once: yes -''' +""" import base64 @@ -201,7 +201,6 @@ from operator import itemgetter from ansible.module_utils.basic import AnsibleModule, missing_required_lib from ansible_collections.community.zabbix.plugins.module_utils.base import ZabbixBase -from ansible.module_utils.compat.version import LooseVersion import ansible_collections.community.zabbix.plugins.module_utils.helpers as zabbix_utils @@ -234,17 +233,17 @@ except ImportError: class Map(ZabbixBase): def __init__(self, module, zbx=None, zapi_wrapper=None): super(Map, self).__init__(module, zbx, zapi_wrapper) - self.map_name = module.params['name'] - self.dot_data = module.params['data'] - self.width = module.params['width'] - self.height = module.params['height'] - self.state = module.params['state'] - self.default_image = module.params['default_image'] + self.map_name = module.params["name"] + self.dot_data = module.params["data"] + self.width = module.params["width"] + self.height = module.params["height"] + self.state = module.params["state"] + self.default_image = module.params["default_image"] self.map_id = self._get_sysmap_id(self.map_name) - self.margin = module.params['margin'] - self.expand_problem = module.params['expand_problem'] - self.highlight = module.params['highlight'] - self.label_type = module.params['label_type'] + self.margin = module.params["margin"] + self.expand_problem = module.params["expand_problem"] + self.highlight = module.params["highlight"] + self.label_type = module.params["label_type"] self.selements_sort_keys = self._get_selements_sort_keys() def _build_graph(self): @@ -265,25 +264,25 @@ class Map(ZabbixBase): edges = self._get_graph_edges(graph) icon_ids = self._get_icon_ids() map_config = { - 'name': self.map_name, - 'label_type': self._get_label_type_id(self.label_type), - 'expandproblem': int(self.expand_problem), - 'highlight': int(self.highlight), - 'width': self.width, - 'height': self.height, - 'selements': self._get_selements(graph, nodes, icon_ids), - 'links': self._get_links(nodes, edges), + "name": self.map_name, + "label_type": self._get_label_type_id(self.label_type), + "expandproblem": int(self.expand_problem), + "highlight": int(self.highlight), + "width": self.width, + "height": self.height, + "selements": self._get_selements(graph, nodes, icon_ids), + "links": self._get_links(nodes, edges), } return map_config def _get_label_type_id(self, label_type): label_type_ids = { - 'label': 0, - 'ip': 1, - 'name': 2, - 'status': 3, - 'nothing': 4, - 'custom': 5, + "label": 0, + "ip": 1, + "name": 2, + "status": 3, + "nothing": 4, + "custom": 5, } try: label_type_id = label_type_ids[label_type] @@ -294,82 +293,73 @@ class Map(ZabbixBase): def _get_images_info(self, data, icon_ids): images = [ { - 'dot_tag': 'zbx_image', - 'zbx_property': 'iconid_off', - 'mandatory': True + "dot_tag": "zbx_image", + "zbx_property": "iconid_off", + "mandatory": True }, { - 'dot_tag': 'zbx_image_disabled', - 'zbx_property': 'iconid_disabled', - 'mandatory': False + "dot_tag": "zbx_image_disabled", + "zbx_property": "iconid_disabled", + "mandatory": False }, { - 'dot_tag': 'zbx_image_maintenance', - 'zbx_property': 'iconid_maintenance', - 'mandatory': False + "dot_tag": "zbx_image_maintenance", + "zbx_property": "iconid_maintenance", + "mandatory": False }, { - 'dot_tag': 'zbx_image_problem', - 'zbx_property': 'iconid_on', - 'mandatory': False + "dot_tag": "zbx_image_problem", + "zbx_property": "iconid_on", + "mandatory": False } ] images_info = {} default_image = self.default_image if self.default_image else sorted(icon_ids.items())[0][0] for image in images: - image_name = data.get(image['dot_tag'], None) + image_name = data.get(image["dot_tag"], None) if not image_name: - if image['mandatory']: + if image["mandatory"]: image_name = default_image else: continue image_name = remove_quotes(image_name) if image_name in icon_ids: - images_info[image['zbx_property']] = icon_ids[image_name] - if not image['mandatory']: - images_info['use_iconmap'] = 0 + images_info[image["zbx_property"]] = icon_ids[image_name] + if not image["mandatory"]: + images_info["use_iconmap"] = 0 else: self._module.fail_json(msg="Failed to find id for image '%s'" % image_name) return images_info def _get_element_type(self, data): types = { - 'host': 0, - 'sysmap': 1, - 'trigger': 2, - 'group': 3, - 'image': 4 + "host": 0, + "sysmap": 1, + "trigger": 2, + "group": 3, + "image": 4 } element_type = { - 'elementtype': types['image'], + "elementtype": types["image"], } - if LooseVersion(self._zbx_api_version) < LooseVersion('3.4'): - element_type.update({ - 'elementid': "0", - }) for type_name, type_id in sorted(types.items()): - field_name = 'zbx_' + type_name + field_name = "zbx_" + type_name if field_name in data: - method_name = '_get_' + type_name + '_id' + method_name = "_get_" + type_name + "_id" element_name = remove_quotes(data[field_name]) get_element_id = getattr(self, method_name, None) if get_element_id: elementid = get_element_id(element_name) if elementid and int(elementid) > 0: element_type.update({ - 'elementtype': type_id, - 'label': element_name + "elementtype": type_id, + "label": element_name + }) + element_type.update({ + "elements": [{ + type_name + "id": elementid, + }], }) - if LooseVersion(self._zbx_api_version) < LooseVersion('3.4'): - element_type.update({ - 'elementid': elementid, - }) - else: - element_type.update({ - 'elements': [{ - type_name + 'id': elementid, - }], - }) break else: self._module.fail_json(msg="Failed to find id for %s '%s'" % (type_name, element_name)) @@ -382,30 +372,30 @@ class Map(ZabbixBase): scales = self._get_scales(graph) for selementid, (node, data) in enumerate(nodes.items(), start=1): selement = { - 'selementid': selementid + "selementid": selementid } - data['selementid'] = selementid + data["selementid"] = selementid images_info = self._get_images_info(data, icon_ids) selement.update(images_info) - image_id = images_info['iconid_off'] + image_id = images_info["iconid_off"] if image_id not in icon_sizes: icon_sizes[image_id] = self._get_icon_size(image_id) - pos = self._convert_coordinates(data['pos'], scales, icon_sizes[image_id]) + pos = self._convert_coordinates(data["pos"], scales, icon_sizes[image_id]) selement.update(pos) - selement['label'] = remove_quotes(node) + selement["label"] = remove_quotes(node) element_type = self._get_element_type(data) selement.update(element_type) label = self._get_label(data) if label: - selement['label'] = label + selement["label"] = label urls = self._get_urls(data) if urls: - selement['urls'] = urls + selement["urls"] = urls selements.append(selement) return selements @@ -413,35 +403,35 @@ class Map(ZabbixBase): def _get_links(self, nodes, edges): links = {} for edge in edges: - link_id = tuple(sorted(edge.obj_dict['points'])) + link_id = tuple(sorted(edge.obj_dict["points"])) node1, node2 = link_id - data = edge.obj_dict['attributes'] + data = edge.obj_dict["attributes"] - if "style" in data and data['style'] == "invis": + if "style" in data and data["style"] == "invis": continue if link_id not in links: links[link_id] = { - 'selementid1': min(nodes[node1]['selementid'], nodes[node2]['selementid']), - 'selementid2': max(nodes[node1]['selementid'], nodes[node2]['selementid']), + "selementid1": min(nodes[node1]["selementid"], nodes[node2]["selementid"]), + "selementid2": max(nodes[node1]["selementid"], nodes[node2]["selementid"]), } link = links[link_id] if "color" not in link: - link['color'] = self._get_color_hex(remove_quotes(data.get('color', 'green'))) + link["color"] = self._get_color_hex(remove_quotes(data.get("color", "green"))) if "zbx_draw_style" not in link: - link['drawtype'] = self._get_link_draw_style_id(remove_quotes(data.get('zbx_draw_style', 'line'))) + link["drawtype"] = self._get_link_draw_style_id(remove_quotes(data.get("zbx_draw_style", "line"))) label = self._get_label(data) if label and "label" not in link: - link['label'] = label + link["label"] = label triggers = self._get_triggers(data) if triggers: if "linktriggers" not in link: - link['linktriggers'] = [] - link['linktriggers'] += triggers + link["linktriggers"] = [] + link["linktriggers"] += triggers return list(links.values()) @@ -449,12 +439,12 @@ class Map(ZabbixBase): urls = [] for url_raw in [remove_quotes(value) for key, value in data.items() if key.startswith("zbx_url")]: try: - name, url = url_raw.split(':', 1) + name, url = url_raw.split(":", 1) except Exception as e: self._module.fail_json(msg="Failed to parse zbx_url='%s': %s" % (url_raw, e)) urls.append({ - 'name': name, - 'url': url, + "name": name, + "url": url, }) return urls @@ -464,9 +454,9 @@ class Map(ZabbixBase): triggerid = self._get_trigger_id(trigger_definition) if triggerid: triggers.append({ - 'triggerid': triggerid, - 'color': self._get_color_hex(remove_quotes(data.get('zbx_trigger_color', 'red'))), - 'drawtype': self._get_link_draw_style_id(remove_quotes(data.get('zbx_trigger_draw_style', 'bold'))), + "triggerid": triggerid, + "color": self._get_color_hex(remove_quotes(data.get("zbx_trigger_color", "red"))), + "drawtype": self._get_link_draw_style_id(remove_quotes(data.get("zbx_trigger_draw_style", "bold"))), }) else: self._module.fail_json(msg="Failed to find trigger '%s'" % (trigger_definition)) @@ -475,23 +465,23 @@ class Map(ZabbixBase): @staticmethod def _get_label(data, default=None): if "zbx_label" in data: - label = remove_quotes(data['zbx_label']).replace('\\n', '\n') + label = remove_quotes(data["zbx_label"]).replace("\\n", "\n") elif "label" in data: - label = remove_quotes(data['label']) + label = remove_quotes(data["label"]) else: label = default return label def _get_sysmap_id(self, map_name): - exist_map = self._zapi.map.get({'filter': {'name': map_name}}) + exist_map = self._zapi.map.get({"filter": {"name": map_name}}) if exist_map: - return exist_map[0]['sysmapid'] + return exist_map[0]["sysmapid"] return None def _get_group_id(self, group_name): - exist_group = self._zapi.hostgroup.get({'filter': {'name': group_name}}) + exist_group = self._zapi.hostgroup.get({"filter": {"name": group_name}}) if exist_group: - return exist_group[0]['groupid'] + return exist_group[0]["groupid"] return None def map_exists(self): @@ -513,7 +503,7 @@ class Map(ZabbixBase): try: if self._module.check_mode: self._module.exit_json(changed=True) - map_config['sysmapid'] = self.map_id + map_config["sysmapid"] = self.map_id result = self._zapi.map.update(map_config) if result: return result @@ -532,24 +522,22 @@ class Map(ZabbixBase): def is_exist_map_correct(self, generated_map_config): exist_map_configs = self._zapi.map.get({ - 'sysmapids': self.map_id, - 'selectLinks': 'extend', - 'selectSelements': 'extend' + "sysmapids": self.map_id, + "selectLinks": "extend", + "selectSelements": "extend" }) exist_map_config = exist_map_configs[0] if not self._is_dicts_equal(generated_map_config, exist_map_config): return False - if not self._is_selements_equal(generated_map_config['selements'], exist_map_config['selements']): + if not self._is_selements_equal(generated_map_config["selements"], exist_map_config["selements"]): return False self._update_ids(generated_map_config, exist_map_config) - if not self._is_links_equal(generated_map_config['links'], exist_map_config['links']): + if not self._is_links_equal(generated_map_config["links"], exist_map_config["links"]): return False return True def _get_selements_sort_keys(self): - keys_to_sort = ['label'] - if LooseVersion(self._zbx_api_version) < LooseVersion('3.4'): - keys_to_sort.insert(0, 'elementid') + keys_to_sort = ["label"] return keys_to_sort def _is_selements_equal(self, generated_selements, exist_selements): @@ -558,22 +546,21 @@ class Map(ZabbixBase): generated_selements_sorted = sorted(generated_selements, key=itemgetter(*self.selements_sort_keys)) exist_selements_sorted = sorted(exist_selements, key=itemgetter(*self.selements_sort_keys)) for (generated_selement, exist_selement) in zip(generated_selements_sorted, exist_selements_sorted): - if LooseVersion(self._zbx_api_version) >= LooseVersion('3.4'): - if not self._is_elements_equal(generated_selement.get('elements', []), exist_selement.get('elements', [])): - return False - if not self._is_dicts_equal(generated_selement, exist_selement, ['selementid']): + if not self._is_elements_equal(generated_selement.get("elements", []), exist_selement.get("elements", [])): return False - if not self._is_urls_equal(generated_selement.get('urls', []), exist_selement.get('urls', [])): + if not self._is_dicts_equal(generated_selement, exist_selement, ["selementid"]): + return False + if not self._is_urls_equal(generated_selement.get("urls", []), exist_selement.get("urls", [])): return False return True def _is_urls_equal(self, generated_urls, exist_urls): if len(generated_urls) != len(exist_urls): return False - generated_urls_sorted = sorted(generated_urls, key=itemgetter('name', 'url')) - exist_urls_sorted = sorted(exist_urls, key=itemgetter('name', 'url')) + generated_urls_sorted = sorted(generated_urls, key=itemgetter("name", "url")) + exist_urls_sorted = sorted(exist_urls, key=itemgetter("name", "url")) for (generated_url, exist_url) in zip(generated_urls_sorted, exist_urls_sorted): - if not self._is_dicts_equal(generated_url, exist_url, ['selementid']): + if not self._is_dicts_equal(generated_url, exist_url, ["selementid"]): return False return True @@ -583,40 +570,40 @@ class Map(ZabbixBase): generated_elements_sorted = sorted(generated_elements, key=lambda k: k.values()[0]) exist_elements_sorted = sorted(exist_elements, key=lambda k: k.values()[0]) for (generated_element, exist_element) in zip(generated_elements_sorted, exist_elements_sorted): - if not self._is_dicts_equal(generated_element, exist_element, ['selementid']): + if not self._is_dicts_equal(generated_element, exist_element, ["selementid"]): return False return True # since generated IDs differ from real Zabbix ones, make real IDs match generated ones def _update_ids(self, generated_map_config, exist_map_config): - generated_selements_sorted = sorted(generated_map_config['selements'], key=itemgetter(*self.selements_sort_keys)) - exist_selements_sorted = sorted(exist_map_config['selements'], key=itemgetter(*self.selements_sort_keys)) + generated_selements_sorted = sorted(generated_map_config["selements"], key=itemgetter(*self.selements_sort_keys)) + exist_selements_sorted = sorted(exist_map_config["selements"], key=itemgetter(*self.selements_sort_keys)) id_mapping = {} for (generated_selement, exist_selement) in zip(generated_selements_sorted, exist_selements_sorted): - id_mapping[exist_selement['selementid']] = generated_selement['selementid'] - for link in exist_map_config['links']: - link['selementid1'] = id_mapping[link['selementid1']] - link['selementid2'] = id_mapping[link['selementid2']] - if link['selementid2'] < link['selementid1']: - link['selementid1'], link['selementid2'] = link['selementid2'], link['selementid1'] + id_mapping[exist_selement["selementid"]] = generated_selement["selementid"] + for link in exist_map_config["links"]: + link["selementid1"] = id_mapping[link["selementid1"]] + link["selementid2"] = id_mapping[link["selementid2"]] + if link["selementid2"] < link["selementid1"]: + link["selementid1"], link["selementid2"] = link["selementid2"], link["selementid1"] def _is_links_equal(self, generated_links, exist_links): if len(generated_links) != len(exist_links): return False - generated_links_sorted = sorted(generated_links, key=itemgetter('selementid1', 'selementid2', 'color', 'drawtype')) - exist_links_sorted = sorted(exist_links, key=itemgetter('selementid1', 'selementid2', 'color', 'drawtype')) + generated_links_sorted = sorted(generated_links, key=itemgetter("selementid1", "selementid2", "color", "drawtype")) + exist_links_sorted = sorted(exist_links, key=itemgetter("selementid1", "selementid2", "color", "drawtype")) for (generated_link, exist_link) in zip(generated_links_sorted, exist_links_sorted): - if not self._is_dicts_equal(generated_link, exist_link, ['selementid1', 'selementid2']): + if not self._is_dicts_equal(generated_link, exist_link, ["selementid1", "selementid2"]): return False - if not self._is_triggers_equal(generated_link.get('linktriggers', []), exist_link.get('linktriggers', [])): + if not self._is_triggers_equal(generated_link.get("linktriggers", []), exist_link.get("linktriggers", [])): return False return True def _is_triggers_equal(self, generated_triggers, exist_triggers): if len(generated_triggers) != len(exist_triggers): return False - generated_triggers_sorted = sorted(generated_triggers, key=itemgetter('triggerid')) - exist_triggers_sorted = sorted(exist_triggers, key=itemgetter('triggerid')) + generated_triggers_sorted = sorted(generated_triggers, key=itemgetter("triggerid")) + exist_triggers_sorted = sorted(exist_triggers, key=itemgetter("triggerid")) for (generated_trigger, exist_trigger) in zip(generated_triggers_sorted, exist_triggers_sorted): if not self._is_dicts_equal(generated_trigger, exist_trigger): return False @@ -637,40 +624,40 @@ class Map(ZabbixBase): return True def _get_host_id(self, hostname): - hostid = self._zapi.host.get({'filter': {'host': hostname}}) + hostid = self._zapi.host.get({"filter": {"host": hostname}}) if hostid: - return str(hostid[0]['hostid']) + return str(hostid[0]["hostid"]) def _get_trigger_id(self, trigger_definition): try: - host, trigger = trigger_definition.split(':', 1) + host, trigger = trigger_definition.split(":", 1) except Exception as e: self._module.fail_json(msg="Failed to parse zbx_trigger='%s': %s" % (trigger_definition, e)) triggerid = self._zapi.trigger.get({ - 'host': host, - 'filter': { - 'description': trigger + "host": host, + "filter": { + "description": trigger } }) if triggerid: - return str(triggerid[0]['triggerid']) + return str(triggerid[0]["triggerid"]) def _get_icon_ids(self): icons_list = self._zapi.image.get({}) icon_ids = {} for icon in icons_list: - icon_ids[icon['name']] = icon['imageid'] + icon_ids[icon["name"]] = icon["imageid"] return icon_ids def _get_icon_size(self, icon_id): icons_list = self._zapi.image.get({ - 'imageids': [ + "imageids": [ icon_id ], - 'select_image': True + "select_image": True }) if len(icons_list) > 0: - icon_base64 = icons_list[0]['image'] + icon_base64 = icons_list[0]["image"] else: self._module.fail_json(msg="Failed to find image with id %s" % icon_id) image = Image.open(BytesIO(base64.b64decode(icon_base64))) @@ -681,19 +668,19 @@ class Map(ZabbixBase): def _get_node_attributes(node): attr = {} if "attributes" in node.obj_dict: - attr.update(node.obj_dict['attributes']) + attr.update(node.obj_dict["attributes"]) pos = node.get_pos() if pos is not None: pos = remove_quotes(pos) xx, yy = pos.split(",") - attr['pos'] = (float(xx), float(yy)) + attr["pos"] = (float(xx), float(yy)) return attr def _get_graph_nodes(self, parent): nodes = {} for node in parent.get_nodes(): node_name = node.get_name() - if node_name in ('node', 'graph', 'edge'): + if node_name in ("node", "graph", "edge"): continue nodes[node_name] = self._get_node_attributes(node) for subgraph in parent.get_subgraphs(): @@ -714,38 +701,38 @@ class Map(ZabbixBase): scale_x = (self.width - self.margin * 2) / (float(max_x) - float(min_x)) if float(max_x) != float(min_x) else 0 scale_y = (self.height - self.margin * 2) / (float(max_y) - float(min_y)) if float(max_y) != float(min_y) else 0 return { - 'min_x': float(min_x), - 'min_y': float(min_y), - 'max_x': float(max_x), - 'max_y': float(max_y), - 'scale_x': float(scale_x), - 'scale_y': float(scale_y), + "min_x": float(min_x), + "min_y": float(min_y), + "max_x": float(max_x), + "max_y": float(max_y), + "scale_x": float(scale_x), + "scale_y": float(scale_y), } # transform Graphviz coordinates to Zabbix's ones def _convert_coordinates(self, pos, scales, icon_size): return { - 'x': int((pos[0] - scales['min_x']) * scales['scale_x'] - icon_size[0] / 2 + self.margin), - 'y': int((scales['max_y'] - pos[1] + scales['min_y']) * scales['scale_y'] - icon_size[1] / 2 + self.margin), + "x": int((pos[0] - scales["min_x"]) * scales["scale_x"] - icon_size[0] / 2 + self.margin), + "y": int((scales["max_y"] - pos[1] + scales["min_y"]) * scales["scale_y"] - icon_size[1] / 2 + self.margin), } def _get_color_hex(self, color_name): - if color_name.startswith('#'): + if color_name.startswith("#"): color_hex = color_name else: try: color_hex = webcolors.name_to_hex(color_name) except Exception as e: self._module.fail_json(msg="Failed to get RGB hex for color '%s': %s" % (color_name, e)) - color_hex = color_hex.strip('#').upper() + color_hex = color_hex.strip("#").upper() return color_hex def _get_link_draw_style_id(self, draw_style): draw_style_ids = { - 'line': 0, - 'bold': 2, - 'dotted': 3, - 'dashed': 4 + "line": 0, + "bold": 2, + "dotted": 3, + "dashed": 4 } try: draw_style_id = draw_style_ids[draw_style] @@ -764,34 +751,28 @@ def remove_quotes(s): def main(): argument_spec = zabbix_utils.zabbix_common_argument_spec() argument_spec.update(dict( - name=dict(type='str', required=True, aliases=['map_name']), - data=dict(type='str', required=False, aliases=['dot_data']), - width=dict(type='int', default=800), - height=dict(type='int', default=600), - state=dict(type='str', default="present", choices=['present', 'absent']), - default_image=dict(type='str', required=False, aliases=['image']), - margin=dict(type='int', default=40), - expand_problem=dict(type='bool', default=True), - highlight=dict(type='bool', default=True), - label_type=dict(type='str', default='name', choices=['label', 'ip', 'name', 'status', 'nothing', 'custom']), + name=dict(type="str", required=True, aliases=["map_name"]), + data=dict(type="str", required=False, aliases=["dot_data"]), + width=dict(type="int", default=800), + height=dict(type="int", default=600), + state=dict(type="str", default="present", choices=["present", "absent"]), + default_image=dict(type="str", required=False, aliases=["image"]), + margin=dict(type="int", default=40), + expand_problem=dict(type="bool", default=True), + highlight=dict(type="bool", default=True), + label_type=dict(type="str", default="name", choices=["label", "ip", "name", "status", "nothing", "custom"]), )) module = AnsibleModule( argument_spec=argument_spec, supports_check_mode=True ) - zabbix_utils.require_creds_params(module) - - for p in ['server_url', 'login_user', 'login_password', 'timeout', 'validate_certs']: - if p in module.params and not module.params[p] is None: - module.warn('Option "%s" is deprecated with the move to httpapi connection and will be removed in the next release' % p) - if not HAS_PYDOTPLUS: - module.fail_json(msg=missing_required_lib('pydotplus', url='https://pypi.org/project/pydotplus/'), exception=PYDOT_IMP_ERR) + module.fail_json(msg=missing_required_lib("pydotplus", url="https://pypi.org/project/pydotplus/"), exception=PYDOT_IMP_ERR) if not HAS_WEBCOLORS: - module.fail_json(msg=missing_required_lib('webcolors', url='https://pypi.org/project/webcolors/'), exception=WEBCOLORS_IMP_ERR) + module.fail_json(msg=missing_required_lib("webcolors", url="https://pypi.org/project/webcolors/"), exception=WEBCOLORS_IMP_ERR) if not HAS_PIL: - module.fail_json(msg=missing_required_lib('Pillow', url='https://pypi.org/project/Pillow/'), exception=PIL_IMP_ERR) + module.fail_json(msg=missing_required_lib("Pillow", url="https://pypi.org/project/Pillow/"), exception=PIL_IMP_ERR) sysmap = Map(module) @@ -814,5 +795,5 @@ def main(): module.exit_json(changed=True, result="Successfully created map: %s" % sysmap.map_name) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/ansible_collections/community/zabbix/plugins/modules/zabbix_mediatype.py b/ansible_collections/community/zabbix/plugins/modules/zabbix_mediatype.py index c5693f467..1e2514bf3 100644 --- a/ansible_collections/community/zabbix/plugins/modules/zabbix_mediatype.py +++ b/ansible_collections/community/zabbix/plugins/modules/zabbix_mediatype.py @@ -7,7 +7,7 @@ from __future__ import absolute_import, division, print_function __metaclass__ = type -DOCUMENTATION = r''' +DOCUMENTATION = r""" --- module: zabbix_mediatype short_description: Create/Update/Delete Zabbix media types @@ -16,22 +16,21 @@ description: author: - Ruben Tsirunyan (@rubentsirunyan) requirements: - - "python >= 2.6" + - "python >= 3.9" options: name: - type: 'str' + type: "str" description: - Name of the media type. required: true description: - type: 'str' + type: "str" description: - Description of the media type. - - Works only with Zabbix versions 4.4 or newer. - default: '' + default: "" state: - type: 'str' + type: "str" description: - Desired state of the mediatype. - On C(present), it will create a mediatype if it does not exist or update the mediatype if the associated data is different. @@ -39,13 +38,12 @@ options: choices: - present - absent - default: 'present' + default: "present" type: - type: 'str' + type: "str" description: - Type of the media type. - Media types I(jabber) and I(ez_texting) works only with Zabbix versions 4.2 or older. - - Media type I(webhook) works only with Zabbix versions 4.4 or newer. choices: - email - script @@ -55,93 +53,90 @@ options: - ez_texting required: true status: - type: 'str' + type: "str" description: - Whether the media type is enabled or no. choices: - enabled - disabled - default: 'enabled' + default: "enabled" max_sessions: - type: 'int' + type: "int" description: - The maximum number of alerts that can be processed in parallel. - Possible value is 1 when I(type=sms) and 0-100 otherwise. - - Works only with Zabbix versions 3.4 or newer. default: 1 max_attempts: - type: 'int' + type: "int" description: - The maximum number of attempts to send an alert. - Possible range is 0-10. - - Works only with Zabbix versions 3.4 or newer. default: 3 attempt_interval: - type: 'str' + type: "str" description: - The interval between retry attempts. - - Possible range is 0-60s in Zabbix < 5.0 or 0-1h in Zabbix >= 5.0. - - Works only with Zabbix versions 3.4 or newer. + - Possible range is 0-1h. default: 10s script_name: - type: 'str' + type: "str" description: - The name of the executed script. - Required when I(type=script). script_params: - type: 'list' + type: "list" elements: str description: - List of script parameters. - Required when I(type=script). gsm_modem: - type: 'str' + type: "str" description: - Serial device name of the gsm modem. - Required when I(type=sms). username: - type: 'str' + type: "str" description: - Username or Jabber identifier. - Required when I(type=jabber) or I(type=ez_texting). - Required when I(type=email) and I(smtp_authentication=true). password: - type: 'str' + type: "str" description: - Authentication password. - Required when I(type=jabber) or I(type=ez_texting). - Required when I(type=email) and I(smtp_authentication=true). smtp_server: - type: 'str' + type: "str" description: - SMTP server host. - Required when I(type=email). - default: 'localhost' + default: "localhost" smtp_server_port: - type: 'int' + type: "int" description: - SMTP server port. - Required when I(type=email). default: 25 smtp_helo: - type: 'str' + type: "str" description: - SMTP HELO. - Required when I(type=email). - default: 'localhost' + default: "localhost" smtp_email: - type: 'str' + type: "str" description: - Email address from which notifications will be sent. - Required when I(type=email). smtp_authentication: - type: 'bool' + type: "bool" description: - Whether SMTP authentication with username and password should be enabled or not. - If set to C(true), C(username) and C(password) should be specified. default: false smtp_security: - type: 'str' + type: "str" description: - SMTP connection security level to use. choices: @@ -149,19 +144,19 @@ options: - STARTTLS - SSL/TLS smtp_verify_host: - type: 'bool' + type: "bool" description: - SSL verify host for SMTP. - Can be specified when I(smtp_security=STARTTLS) or I(smtp_security=SSL/TLS) default: false smtp_verify_peer: - type: 'bool' + type: "bool" description: - SSL verify peer for SMTP. - Can be specified when I(smtp_security=STARTTLS) or I(smtp_security=SSL/TLS) default: false message_text_limit: - type: 'str' + type: "str" description: - The message text limit. - Required when I(type=ez_texting). @@ -170,74 +165,72 @@ options: - USA - Canada webhook_script: - type: 'str' + type: "str" description: - Required when I(type=webhook). - JavaScript code that will perform webhook operation. - This code has access to all parameters in I(webhook_params). - It may perform HTTP GET, POST, PUT and DELETE requests and has control over HTTP headers and request body. - It may return OK status along with an optional list of tags and tag values or an error string. - - Works only with Zabbix versions 4.4 or newer. webhook_timeout: - type: 'str' + type: "str" description: - Can be used when I(type=webhook). - Execution timeout for JavaScript code in I(webhook_script). - Possible values are 1-60s. default: 30s process_tags: - type: 'bool' + type: "bool" description: - Can be used when I(type=webhook). - Process returned JSON property values as tags. - These tags are added to the already existing (if any) problem event tags in Zabbix. default: false event_menu: - type: 'bool' + type: "bool" description: - Can be used when I(type=webhook). - Includes entry in Event menu with link to created external ticket. default: false event_menu_url: - type: 'str' + type: "str" description: - Requred when I(event_menu=true). - Event menu entry underlying URL. event_menu_name: - type: 'str' + type: "str" description: - Requred when I(event_menu=true). - Event menu entry name. webhook_params: - type: 'list' - elements: 'dict' + type: "list" + elements: "dict" description: - Can be used when I(type=webhook). - Webhook variables that are passed to webhook script when executed. default: [] suboptions: name: - type: 'str' + type: "str" description: - Name of the parameter. required: true value: - type: 'str' + type: "str" description: - Value of the parameter. - All macros that are supported in problem notifications are supported in the parameters. - Values are URL-encoded automatically. Values from macros are resolved and then URL-encoded automatically. - default: '' + default: "" message_templates: - type: 'list' - elements: 'dict' + type: "list" + elements: "dict" description: - Default notification messages for the event types. - - Works only with Zabbix versions 5.0 or newer. default: [] suboptions: eventsource: - type: 'str' + type: "str" description: - Event source. - Required when I(recovery) is used. @@ -247,7 +240,7 @@ options: - autoregistration - internal recovery: - type: 'str' + type: "str" description: - Operation mode. - Required when I(eventsource) is used. @@ -256,40 +249,40 @@ options: - recovery_operations - update_operations subject: - type: 'str' + type: "str" description: - Subject of the default message. - May contain macros and is limited to 255 characters. - default: '' + default: "" body: - type: 'str' + type: "str" description: - Body of the default message. - May contain macros. - default: '' + default: "" extends_documentation_fragment: - community.zabbix.zabbix -''' +""" -RETURN = r''' # ''' +RETURN = r""" # """ -EXAMPLES = r''' +EXAMPLES = r""" # If you want to use Username and Password to be authenticated by Zabbix Server - name: Set credentials to access Zabbix Server API - set_fact: + ansible.builtin.set_fact: ansible_user: Admin ansible_httpapi_pass: zabbix # If you want to use API token to be authenticated by Zabbix Server # https://www.zabbix.com/documentation/current/en/manual/web_interface/frontend_sections/administration/general#api-tokens - name: Set API token - set_fact: + ansible.builtin.set_fact: ansible_zabbix_auth_key: 8ec0d52432c15c91fcafe9888500cf9a607f44091ab554dbee860f6b44fac895 -- name: 'Create an email mediatype with SMTP authentication' +- name: "Create an email mediatype with SMTP authentication" # set task level variables as we change ansible_connection plugin here vars: ansible_network_os: community.zabbix.zabbix @@ -297,19 +290,19 @@ EXAMPLES = r''' ansible_httpapi_port: 443 ansible_httpapi_use_ssl: true ansible_httpapi_validate_certs: false - ansible_zabbix_url_path: 'zabbixeu' # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu + ansible_zabbix_url_path: "zabbixeu" # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu ansible_host: zabbix-example-fqdn.org community.zabbix.zabbix_mediatype: name: "Ops email" - type: 'email' - smtp_server: 'example.com' + type: "email" + smtp_server: "example.com" smtp_server_port: 2000 - smtp_email: 'ops@example.com' + smtp_email: "ops@example.com" smtp_authentication: true - username: 'smtp_user' - password: 'smtp_pass' + username: "smtp_user" + password: "smtp_pass" -- name: 'Create a script mediatype' +- name: "Create a script mediatype" # set task level variables as we change ansible_connection plugin here vars: ansible_network_os: community.zabbix.zabbix @@ -317,17 +310,17 @@ EXAMPLES = r''' ansible_httpapi_port: 443 ansible_httpapi_use_ssl: true ansible_httpapi_validate_certs: false - ansible_zabbix_url_path: 'zabbixeu' # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu + ansible_zabbix_url_path: "zabbixeu" # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu ansible_host: zabbix-example-fqdn.org community.zabbix.zabbix_mediatype: name: "my script" - type: 'script' - script_name: 'my_script.py' + type: "script" + script_name: "my_script.py" script_params: - - 'arg1' - - 'arg2' + - "arg1" + - "arg2" -- name: 'Create a jabber mediatype' +- name: "Create a jabber mediatype" # set task level variables as we change ansible_connection plugin here vars: ansible_network_os: community.zabbix.zabbix @@ -335,15 +328,15 @@ EXAMPLES = r''' ansible_httpapi_port: 443 ansible_httpapi_use_ssl: true ansible_httpapi_validate_certs: false - ansible_zabbix_url_path: 'zabbixeu' # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu + ansible_zabbix_url_path: "zabbixeu" # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu ansible_host: zabbix-example-fqdn.org community.zabbix.zabbix_mediatype: name: "My jabber" - type: 'jabber' - username: 'jabber_id' - password: 'jabber_pass' + type: "jabber" + username: "jabber_id" + password: "jabber_pass" -- name: 'Create a SMS mediatype' +- name: "Create a SMS mediatype" # set task level variables as we change ansible_connection plugin here vars: ansible_network_os: community.zabbix.zabbix @@ -351,15 +344,14 @@ EXAMPLES = r''' ansible_httpapi_port: 443 ansible_httpapi_use_ssl: true ansible_httpapi_validate_certs: false - ansible_zabbix_url_path: 'zabbixeu' # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu + ansible_zabbix_url_path: "zabbixeu" # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu ansible_host: zabbix-example-fqdn.org community.zabbix.zabbix_mediatype: name: "My SMS Mediatype" - type: 'sms' - gsm_modem: '/dev/ttyS0' + type: "sms" + gsm_modem: "/dev/ttyS0" -# Supported since Zabbix 4.4 -- name: 'Create a webhook mediatype' +- name: "Create a webhook mediatype" # set task level variables as we change ansible_connection plugin here vars: ansible_network_os: community.zabbix.zabbix @@ -367,24 +359,23 @@ EXAMPLES = r''' ansible_httpapi_port: 443 ansible_httpapi_use_ssl: true ansible_httpapi_validate_certs: false - ansible_zabbix_url_path: 'zabbixeu' # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu + ansible_zabbix_url_path: "zabbixeu" # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu ansible_host: zabbix-example-fqdn.org community.zabbix.zabbix_mediatype: name: "My webhook Mediatype" - type: 'webhook' + type: "webhook" webhook_script: "{{ lookup('file', 'slack.js') }}" webhook_params: - name: alert_message - value: '{ALERT.MESSAGE}' + value: "{ALERT.MESSAGE}" - name: zabbix_url - value: '{$ZABBIX.URL}' + value: "{$ZABBIX.URL}" process_tags: true event_menu: true event_menu_name: "Open in Slack: '{EVENT.TAGS.__channel_name}'" - event_menu_url: '{EVENT.TAGS.__message_link}' + event_menu_url: "{EVENT.TAGS.__message_link}" -# Supported since Zabbix 5.0 -- name: 'Create an email mediatype with message templates' +- name: "Create an email mediatype with message templates" # set task level variables as we change ansible_connection plugin here vars: ansible_network_os: community.zabbix.zabbix @@ -392,12 +383,12 @@ EXAMPLES = r''' ansible_httpapi_port: 443 ansible_httpapi_use_ssl: true ansible_httpapi_validate_certs: false - ansible_zabbix_url_path: 'zabbixeu' # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu + ansible_zabbix_url_path: "zabbixeu" # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu ansible_host: zabbix-example-fqdn.org community.zabbix.zabbix_mediatype: name: "Ops email" - type: 'email' - smtp_email: 'ops@example.com' + type: "email" + smtp_email: "ops@example.com" message_templates: - eventsource: triggers recovery: operations @@ -419,7 +410,7 @@ EXAMPLES = r''' recovery: operations subject: "Autoregistration: {HOST.HOST}" body: "Host name: {HOST.HOST}\r\nHost IP: {HOST.IP}\r\nAgent port: {HOST.PORT}" -''' +""" from ansible.module_utils.basic import AnsibleModule @@ -446,10 +437,10 @@ def diff(existing, new): for key in new: before[key] = existing[key] if new[key] is None: - after[key] = '' + after[key] = "" else: after[key] = new[key] - return {'before': before, 'after': after} + return {"before": before, "after": after} class MediaTypeModule(ZabbixBase): @@ -462,20 +453,18 @@ class MediaTypeModule(ZabbixBase): Returns: Tuple of (True, `id of the mediatype`) if mediatype exists, (False, None) otherwise """ - filter_key_name = 'description' - if LooseVersion(self._zbx_api_version) >= LooseVersion('4.4'): - # description key changed to name key from zabbix 4.4 - filter_key_name = 'name' + filter_key_name = "description" + filter_key_name = "name" try: mediatype_list = self._zapi.mediatype.get({ - 'output': 'extend', - 'filter': {filter_key_name: [name]} + "output": "extend", + "filter": {filter_key_name: [name]} }) if len(mediatype_list) < 1: return False, None else: - return True, mediatype_list[0]['mediatypeid'] + return True, mediatype_list[0]["mediatypeid"] except Exception as e: self._module.fail_json(msg="Failed to get ID of the mediatype '{name}': {e}".format(name=name, e=e)) @@ -487,127 +476,104 @@ class MediaTypeModule(ZabbixBase): A dictionary of arguments that are related to transport type, and are in a format that is understandable by Zabbix API. """ - truths = {'False': '0', 'True': '1'} + truths = {"False": "0", "True": "1"} parameters = dict( - status='0' if self._module.params['status'] == 'enabled' else '1', + status="0" if self._module.params["status"] == "enabled" else "1", type={ - 'email': '0', - 'script': '1', - 'sms': '2', - 'jabber': '3', - 'webhook': '4', - 'ez_texting': '100' - }.get(self._module.params['type']), + "email": "0", + "script": "1", + "sms": "2", + "jabber": "3", + "webhook": "4", + "ez_texting": "100" + }.get(self._module.params["type"]), ) - if LooseVersion(self._zbx_api_version) >= LooseVersion('4.4'): - parameters.update(dict( - name=self._module.params['name'], - description=self._module.params['description'], - )) - else: - parameters.update(dict(description=self._module.params['name'])) + parameters.update(dict( + name=self._module.params["name"], + description=self._module.params["description"], + maxsessions=str(self._module.params["max_sessions"]), + maxattempts=str(self._module.params["max_attempts"]), + attempt_interval=str(self._module.params["attempt_interval"]) + )) - if LooseVersion(self._zbx_api_version) >= LooseVersion('3.4'): - parameters.update(dict( - maxsessions=str(self._module.params['max_sessions']), - maxattempts=str(self._module.params['max_attempts']), - attempt_interval=str(self._module.params['attempt_interval']) - )) - - if self._module.params['message_templates'] and LooseVersion(self._zbx_api_version) >= LooseVersion('5.0'): + if self._module.params["message_templates"]: msg_templates = [] - for template in self._module.params['message_templates']: + for template in self._module.params["message_templates"]: msg_templates.append(dict( eventsource={ - 'triggers': '0', - 'discovery': '1', - 'autoregistration': '2', - 'internal': '3'}.get(template['eventsource']), + "triggers": "0", + "discovery": "1", + "autoregistration": "2", + "internal": "3"}.get(template["eventsource"]), recovery={ - 'operations': '0', - 'recovery_operations': '1', - 'update_operations': '2'}.get(template['recovery']), - subject=template['subject'], - message=template['body'] + "operations": "0", + "recovery_operations": "1", + "update_operations": "2"}.get(template["recovery"]), + subject=template["subject"], + message=template["body"] )) parameters.update(dict(message_templates=msg_templates)) - if self._module.params['type'] == 'email': + if self._module.params["type"] == "email": parameters.update(dict( - smtp_server=self._module.params['smtp_server'], - smtp_port=str(self._module.params['smtp_server_port']), - smtp_helo=self._module.params['smtp_helo'], - smtp_email=self._module.params['smtp_email'], - smtp_security={'None': '0', 'STARTTLS': '1', 'SSL/TLS': '2'}.get(str(self._module.params['smtp_security'])), - smtp_authentication=truths.get(str(self._module.params['smtp_authentication'])), - smtp_verify_host=truths.get(str(self._module.params['smtp_verify_host'])), - smtp_verify_peer=truths.get(str(self._module.params['smtp_verify_peer'])), - username=self._module.params['username'], - passwd=self._module.params['password'] + smtp_server=self._module.params["smtp_server"], + smtp_port=str(self._module.params["smtp_server_port"]), + smtp_helo=self._module.params["smtp_helo"], + smtp_email=self._module.params["smtp_email"], + smtp_security={"None": "0", "STARTTLS": "1", "SSL/TLS": "2"}.get(str(self._module.params["smtp_security"])), + smtp_authentication=truths.get(str(self._module.params["smtp_authentication"])), + smtp_verify_host=truths.get(str(self._module.params["smtp_verify_host"])), + smtp_verify_peer=truths.get(str(self._module.params["smtp_verify_peer"])), + username=self._module.params["username"], + passwd=self._module.params["password"] )) - if LooseVersion(self._zbx_api_version) >= LooseVersion('6.0'): - if parameters['smtp_authentication'] == '0': - parameters.pop('username') - parameters.pop('passwd') + if parameters["smtp_authentication"] == "0": + parameters.pop("username") + parameters.pop("passwd") return parameters - elif self._module.params['type'] == 'script': - if LooseVersion(self._zbx_api_version) < LooseVersion('6.4'): - if self._module.params['script_params'] is None: - _script_params = '' # ZBX-15706 + elif self._module.params["type"] == "script": + if LooseVersion(self._zbx_api_version) < LooseVersion("6.4"): + if self._module.params["script_params"] is None: + _script_params = "" # ZBX-15706 else: - _script_params = '\n'.join(str(i) for i in self._module.params['script_params']) + '\n' + _script_params = "\n".join(str(i) for i in self._module.params["script_params"]) + "\n" parameters.update(dict( - exec_path=self._module.params['script_name'], + exec_path=self._module.params["script_name"], exec_params=_script_params )) else: _script_params = [] - if self._module.params['script_params']: - for i, val in enumerate(self._module.params['script_params']): - _script_params.append({'sortorder': str(i), 'value': val}) + if self._module.params["script_params"]: + for i, val in enumerate(self._module.params["script_params"]): + _script_params.append({"sortorder": str(i), "value": val}) parameters.update(dict( - exec_path=self._module.params['script_name'], + exec_path=self._module.params["script_name"], parameters=_script_params )) return parameters - elif self._module.params['type'] == 'sms': - parameters.update(dict(gsm_modem=self._module.params['gsm_modem'])) + elif self._module.params["type"] == "sms": + parameters.update(dict(gsm_modem=self._module.params["gsm_modem"])) return parameters - elif self._module.params['type'] == 'webhook' and LooseVersion(self._zbx_api_version) >= LooseVersion('4.4'): + elif self._module.params["type"] == "webhook": parameters.update(dict( - script=self._module.params['webhook_script'], - timeout=self._module.params['webhook_timeout'], - process_tags=truths.get(str(self._module.params['process_tags'])), - show_event_menu=truths.get(str(self._module.params['event_menu'])), - parameters=self._module.params['webhook_params'] + script=self._module.params["webhook_script"], + timeout=self._module.params["webhook_timeout"], + process_tags=truths.get(str(self._module.params["process_tags"])), + show_event_menu=truths.get(str(self._module.params["event_menu"])), + parameters=self._module.params["webhook_params"] )) - if self._module.params['event_menu']: + if self._module.params["event_menu"]: parameters.update(dict( - event_menu_url=self._module.params['event_menu_url'], - event_menu_name=self._module.params['event_menu_name'] + event_menu_url=self._module.params["event_menu_url"], + event_menu_name=self._module.params["event_menu_name"] )) return parameters - elif self._module.params['type'] == 'jabber' and LooseVersion(self._zbx_api_version) <= LooseVersion('4.2'): - parameters.update(dict( - username=self._module.params['username'], - passwd=self._module.params['password'] - )) - return parameters - - elif self._module.params['type'] == 'ez_texting' and LooseVersion(self._zbx_api_version) <= LooseVersion('4.2'): - parameters.update(dict( - username=self._module.params['username'], - passwd=self._module.params['password'], - exec_path={'USA': '0', 'Canada': '1'}.get(self._module.params['message_text_limit']), - )) - return parameters - - self._module.fail_json(msg="%s is unsupported for Zabbix version %s" % (parameters['unsupported_parameter'], parameters['zbx_api_version'])) + self._module.fail_json(msg="%s is unsupported for Zabbix version %s" % (self._module.params["type"], self._zbx_api_version)) def validate_params(self, params): """Validates arguments that are required together. @@ -629,7 +595,7 @@ class MediaTypeModule(ZabbixBase): msg="Following arguments are required when {key} is {value}: {arguments}".format( key=param[0], value=param[1], - arguments=', '.join(param[2]) + arguments=", ".join(param[2]) ) ) @@ -646,35 +612,37 @@ class MediaTypeModule(ZabbixBase): returned by diff() function with existing mediatype data and new params passed to it. """ - get_params = {'output': 'extend', 'mediatypeids': [mediatype_id]} - if LooseVersion(self._zbx_api_version) >= LooseVersion('5.0'): - get_params.update({'selectMessageTemplates': 'extend'}) + get_params = { + "output": "extend", + "mediatypeids": [mediatype_id], + "selectMessageTemplates": "extend" + } existing_mediatype = self._zapi.mediatype.get(get_params)[0] - if existing_mediatype['type'] != kwargs['type']: + if existing_mediatype["type"] != kwargs["type"]: return kwargs, diff(existing_mediatype, kwargs) else: params_to_update = {} for key in kwargs: # sort list of parameters to prevent mismatch due to reordering - if key == 'parameters' and (kwargs[key] != [] or existing_mediatype[key] != []): - if LooseVersion(self._zbx_api_version) < LooseVersion('6.4'): - kwargs[key] = sorted(kwargs[key], key=lambda x: x['name']) - existing_mediatype[key] = sorted(existing_mediatype[key], key=lambda x: x['name']) + if key == "parameters" and (kwargs[key] != [] or existing_mediatype[key] != []): + if LooseVersion(self._zbx_api_version) < LooseVersion("6.4"): + kwargs[key] = sorted(kwargs[key], key=lambda x: x["name"]) + existing_mediatype[key] = sorted(existing_mediatype[key], key=lambda x: x["name"]) else: - if kwargs['type'] == '1': # script - kwargs[key] = sorted(kwargs[key], key=lambda x: x['sortorder']) - existing_mediatype[key] = sorted(existing_mediatype[key], key=lambda x: x['sortorder']) - elif kwargs['type'] == '4': # webhook - kwargs[key] = sorted(kwargs[key], key=lambda x: x['name']) - existing_mediatype[key] = sorted(existing_mediatype[key], key=lambda x: x['name']) - - if key == 'message_templates' and (kwargs[key] != [] or existing_mediatype[key] != []): - kwargs[key] = sorted(kwargs[key], key=lambda x: x['subject']) - existing_mediatype[key] = sorted(existing_mediatype[key], key=lambda x: x['subject']) - - if (not (kwargs[key] is None and existing_mediatype[key] == '')) and kwargs[key] != existing_mediatype[key]: + if kwargs["type"] == "1": # script + kwargs[key] = sorted(kwargs[key], key=lambda x: x["sortorder"]) + existing_mediatype[key] = sorted(existing_mediatype[key], key=lambda x: x["sortorder"]) + elif kwargs["type"] == "4": # webhook + kwargs[key] = sorted(kwargs[key], key=lambda x: x["name"]) + existing_mediatype[key] = sorted(existing_mediatype[key], key=lambda x: x["name"]) + + if key == "message_templates" and (kwargs[key] != [] or existing_mediatype[key] != []): + kwargs[key] = sorted(kwargs[key], key=lambda x: x["subject"]) + existing_mediatype[key] = sorted(existing_mediatype[key], key=lambda x: x["subject"]) + + if (not (kwargs[key] is None and existing_mediatype[key] == "")) and kwargs[key] != existing_mediatype[key]: params_to_update[key] = kwargs[key] return params_to_update, diff(existing_mediatype, kwargs) @@ -688,89 +656,89 @@ class MediaTypeModule(ZabbixBase): try: self._zapi.mediatype.update(kwargs) except Exception as e: - self._module.fail_json(msg="Failed to update mediatype '{_id}': {e}".format(_id=kwargs['mediatypeid'], e=e)) + self._module.fail_json(msg="Failed to update mediatype '{_id}': {e}".format(_id=kwargs["mediatypeid"], e=e)) def create_mediatype(self, **kwargs): try: self._zapi.mediatype.create(kwargs) except Exception as e: - self._module.fail_json(msg="Failed to create mediatype '{name}': {e}".format(name=kwargs['name'], e=e)) + self._module.fail_json(msg="Failed to create mediatype '{name}': {e}".format(name=kwargs["name"], e=e)) def main(): argument_spec = zabbix_utils.zabbix_common_argument_spec() argument_spec.update(dict( - name=dict(type='str', required=True), - description=dict(type='str', required=False, default=''), - state=dict(type='str', default='present', choices=['present', 'absent']), - type=dict(type='str', choices=['email', 'script', 'sms', 'webhook', 'jabber', 'ez_texting'], required=True), - status=dict(type='str', default='enabled', choices=['enabled', 'disabled'], required=False), - max_sessions=dict(type='int', default=1, required=False), - max_attempts=dict(type='int', default=3, required=False), - attempt_interval=dict(type='str', default='10s', required=False), + name=dict(type="str", required=True), + description=dict(type="str", required=False, default=""), + state=dict(type="str", default="present", choices=["present", "absent"]), + type=dict(type="str", choices=["email", "script", "sms", "webhook", "jabber", "ez_texting"], required=True), + status=dict(type="str", default="enabled", choices=["enabled", "disabled"], required=False), + max_sessions=dict(type="int", default=1, required=False), + max_attempts=dict(type="int", default=3, required=False), + attempt_interval=dict(type="str", default="10s", required=False), # Script - script_name=dict(type='str', required=False), - script_params=dict(type='list', required=False), + script_name=dict(type="str", required=False), + script_params=dict(type="list", required=False, elements="str"), # SMS - gsm_modem=dict(type='str', required=False), + gsm_modem=dict(type="str", required=False), # Jabber - username=dict(type='str', required=False), - password=dict(type='str', required=False, no_log=True), + username=dict(type="str", required=False), + password=dict(type="str", required=False, no_log=True), # Email - smtp_server=dict(type='str', default='localhost', required=False), - smtp_server_port=dict(type='int', default=25, required=False), - smtp_helo=dict(type='str', default='localhost', required=False), - smtp_email=dict(type='str', required=False), - smtp_security=dict(type='str', required=False, choices=['None', 'STARTTLS', 'SSL/TLS']), - smtp_authentication=dict(type='bool', default=False, required=False), - smtp_verify_host=dict(type='bool', default=False, required=False), - smtp_verify_peer=dict(type='bool', default=False, required=False), + smtp_server=dict(type="str", default="localhost", required=False), + smtp_server_port=dict(type="int", default=25, required=False), + smtp_helo=dict(type="str", default="localhost", required=False), + smtp_email=dict(type="str", required=False), + smtp_security=dict(type="str", required=False, choices=["None", "STARTTLS", "SSL/TLS"]), + smtp_authentication=dict(type="bool", default=False, required=False), + smtp_verify_host=dict(type="bool", default=False, required=False), + smtp_verify_peer=dict(type="bool", default=False, required=False), # EZ Text - message_text_limit=dict(type='str', required=False, choices=['USA', 'Canada']), + message_text_limit=dict(type="str", required=False, choices=["USA", "Canada"]), # Webhook - webhook_script=dict(type='str'), - webhook_timeout=dict(type='str', default='30s'), - process_tags=dict(type='bool', default=False), - event_menu=dict(type='bool', default=False), - event_menu_url=dict(type='str'), - event_menu_name=dict(type='str'), + webhook_script=dict(type="str"), + webhook_timeout=dict(type="str", default="30s"), + process_tags=dict(type="bool", default=False), + event_menu=dict(type="bool", default=False), + event_menu_url=dict(type="str"), + event_menu_name=dict(type="str"), webhook_params=dict( - type='list', - elements='dict', + type="list", + elements="dict", default=[], required=False, options=dict( - name=dict(type='str', required=True), - value=dict(type='str', default='') + name=dict(type="str", required=True), + value=dict(type="str", default="") ) ), message_templates=dict( - type='list', - elements='dict', + type="list", + elements="dict", default=[], required=False, options=dict( - eventsource=dict(type='str', choices=['triggers', 'discovery', 'autoregistration', 'internal']), - recovery=dict(type='str', choices=['operations', 'recovery_operations', 'update_operations']), - subject=dict(type='str', default=''), - body=dict(type='str', default='') + eventsource=dict(type="str", choices=["triggers", "discovery", "autoregistration", "internal"]), + recovery=dict(type="str", choices=["operations", "recovery_operations", "update_operations"]), + subject=dict(type="str", default=""), + body=dict(type="str", default="") ), required_together=[ - ['eventsource', 'recovery'] + ["eventsource", "recovery"] ], ) )) # this is used to simulate `required_if` of `AnsibleModule`, but only when state=present required_params = [ - ['type', 'email', ['smtp_email']], - ['type', 'script', ['script_name']], - ['type', 'sms', ['gsm_modem']], - ['type', 'jabber', ['username', 'password']], - ['type', 'ez_texting', ['username', 'password', 'message_text_limit']], - ['type', 'webhook', ['webhook_script']], - ['event_menu', True, ['event_menu_url', 'event_menu_name']], - ['smtp_authentication', True, ['username', 'password']] + ["type", "email", ["smtp_email"]], + ["type", "script", ["script_name"]], + ["type", "sms", ["gsm_modem"]], + ["type", "jabber", ["username", "password"]], + ["type", "ez_texting", ["username", "password", "message_text_limit"]], + ["type", "webhook", ["webhook_script"]], + ["event_menu", True, ["event_menu_url", "event_menu_name"]], + ["smtp_authentication", True, ["username", "password"]] ] module = AnsibleModule( @@ -778,24 +746,18 @@ def main(): supports_check_mode=True ) - zabbix_utils.require_creds_params(module) - - for p in ['server_url', 'login_user', 'login_password', 'timeout', 'validate_certs']: - if p in module.params and not module.params[p] is None: - module.warn('Option "%s" is deprecated with the move to httpapi connection and will be removed in the next release' % p) - - state = module.params['state'] - name = module.params['name'] + state = module.params["state"] + name = module.params["name"] mediatype = MediaTypeModule(module) - if module.params['state'] == 'present': + if module.params["state"] == "present": mediatype.validate_params(required_params) mediatype_exists, mediatype_id = mediatype.check_if_mediatype_exists(name) parameters = mediatype.construct_parameters() if mediatype_exists: - if state == 'absent': + if state == "absent": if module.check_mode: module.exit_json( changed=True, @@ -860,5 +822,5 @@ def main(): ) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/ansible_collections/community/zabbix/plugins/modules/zabbix_proxy.py b/ansible_collections/community/zabbix/plugins/modules/zabbix_proxy.py index 3fdfe42c1..a5ddbe137 100644 --- a/ansible_collections/community/zabbix/plugins/modules/zabbix_proxy.py +++ b/ansible_collections/community/zabbix/plugins/modules/zabbix_proxy.py @@ -22,7 +22,7 @@ from __future__ import (absolute_import, division, print_function) __metaclass__ = type -DOCUMENTATION = r''' +DOCUMENTATION = r""" --- module: zabbix_proxy short_description: Create/delete/get/update Zabbix proxies @@ -31,7 +31,7 @@ description: author: - "Alen Komic (@akomic)" requirements: - - "python >= 2.6" + - "python >= 3.9" options: proxy_name: description: @@ -42,7 +42,6 @@ options: description: - Comma-delimited list of IP/CIDR addresses or DNS names to accept active proxy requests from. - Requires I(status=active). - - Works only with >= Zabbix 4.0. ( remove option for <= 4.0 ) required: false type: str description: @@ -54,22 +53,22 @@ options: description: - Type of proxy. (4 - active, 5 - passive) required: false - choices: ['active', 'passive'] + choices: ["active", "passive"] default: "active" type: str tls_connect: description: - Connections to proxy. required: false - choices: ['no_encryption','PSK','certificate'] - default: 'no_encryption' + choices: ["no_encryption","PSK","certificate"] + default: "no_encryption" type: str tls_accept: description: - Connections from proxy. required: false - choices: ['no_encryption','PSK','certificate'] - default: 'no_encryption' + choices: ["no_encryption","PSK","certificate"] + default: "no_encryption" type: str ca_cert: description: @@ -98,14 +97,14 @@ options: - On C(present), it will create if proxy does not exist or update the proxy if the associated data is different. - On C(absent) will remove a proxy if it exists. required: false - choices: ['present', 'absent'] + choices: ["present", "absent"] default: "present" type: str interface: description: - Dictionary with params for the interface when proxy is in passive mode. - For more information, review proxy interface documentation at - - U(https://www.zabbix.com/documentation/4.0/manual/api/reference/proxy/object#proxy_interface). + - U(https://www.zabbix.com/documentation/current/en/manual/api/reference/proxy/object#proxy-interface). required: false suboptions: useip: @@ -120,53 +119,37 @@ options: description: - IP address used by proxy interface. - Required if I(useip=1). - default: '' + default: "" dns: type: str description: - DNS name of the proxy interface. - Required if I(useip=0). - default: '' + default: "" port: type: str description: - Port used by proxy interface. - default: '10051' - type: - type: int - description: - - Interface type to add. - - This suboption is currently ignored for Zabbix proxy. - - This suboption is deprecated since Ansible 2.10 and will eventually be removed in 2.14. - required: false - default: 0 - main: - type: int - description: - - Whether the interface is used as default. - - This suboption is currently ignored for Zabbix proxy. - - This suboption is deprecated since Ansible 2.10 and will eventually be removed in 2.14. - required: false - default: 0 + default: "10051" default: {} type: dict extends_documentation_fragment: - community.zabbix.zabbix -''' +""" -EXAMPLES = r''' +EXAMPLES = r""" # If you want to use Username and Password to be authenticated by Zabbix Server - name: Set credentials to access Zabbix Server API - set_fact: + ansible.builtin.set_fact: ansible_user: Admin ansible_httpapi_pass: zabbix # If you want to use API token to be authenticated by Zabbix Server # https://www.zabbix.com/documentation/current/en/manual/web_interface/frontend_sections/administration/general#api-tokens - name: Set API token - set_fact: + ansible.builtin.set_fact: ansible_zabbix_auth_key: 8ec0d52432c15c91fcafe9888500cf9a607f44091ab554dbee860f6b44fac895 - name: Create or update a proxy with proxy type active @@ -177,7 +160,7 @@ EXAMPLES = r''' ansible_httpapi_port: 443 ansible_httpapi_use_ssl: true ansible_httpapi_validate_certs: false - ansible_zabbix_url_path: 'zabbixeu' # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu + ansible_zabbix_url_path: "zabbixeu" # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu ansible_host: zabbix-example-fqdn.org community.zabbix.zabbix_proxy: proxy_name: ExampleProxy @@ -194,7 +177,7 @@ EXAMPLES = r''' ansible_httpapi_port: 443 ansible_httpapi_use_ssl: true ansible_httpapi_validate_certs: false - ansible_zabbix_url_path: 'zabbixeu' # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu + ansible_zabbix_url_path: "zabbixeu" # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu ansible_host: zabbix-example-fqdn.org community.zabbix.zabbix_proxy: proxy_name: ExampleProxy @@ -214,7 +197,7 @@ EXAMPLES = r''' ansible_httpapi_port: 443 ansible_httpapi_use_ssl: true ansible_httpapi_validate_certs: false - ansible_zabbix_url_path: 'zabbixeu' # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu + ansible_zabbix_url_path: "zabbixeu" # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu ansible_host: zabbix-example-fqdn.org community.zabbix.zabbix_proxy: proxy_name: ExampleProxy @@ -224,15 +207,14 @@ EXAMPLES = r''' interface: dns: proxy.example.com port: 10051 -''' +""" -RETURN = r''' # ''' +RETURN = r""" # """ from ansible.module_utils.basic import AnsibleModule from ansible_collections.community.zabbix.plugins.module_utils.base import ZabbixBase -from ansible.module_utils.compat.version import LooseVersion import ansible_collections.community.zabbix.plugins.module_utils.helpers as zabbix_utils @@ -243,13 +225,13 @@ class Proxy(ZabbixBase): self.existing_data = None def proxy_exists(self, proxy_name): - result = self._zapi.proxy.get({'output': 'extend', - 'selectInterface': 'extend', - 'filter': {'host': proxy_name}}) + result = self._zapi.proxy.get({"output": "extend", + "selectInterface": "extend", + "filter": {"host": proxy_name}}) - if len(result) > 0 and 'proxyid' in result[0]: + if len(result) > 0 and "proxyid" in result[0]: self.existing_data = result[0] - return result[0]['proxyid'] + return result[0]["proxyid"] else: return result @@ -263,23 +245,19 @@ class Proxy(ZabbixBase): if data[item]: parameters[item] = data[item] - if 'proxy_address' in data and data['status'] != '5': - parameters.pop('proxy_address', False) + if "proxy_address" in data and data["status"] != "5": + parameters.pop("proxy_address", False) - if 'interface' in data and data['status'] != '6': - parameters.pop('interface', False) - else: - if LooseVersion(self._zbx_api_version) >= LooseVersion('6.0'): - parameters['interface'].pop('type') - parameters['interface'].pop('main') + if "interface" in data and data["status"] != "6": + parameters.pop("interface", False) proxy_ids_list = self._zapi.proxy.create(parameters) self._module.exit_json(changed=True, - result="Successfully added proxy %s (%s)" % (data['host'], data['status'])) + result="Successfully added proxy %s (%s)" % (data["host"], data["status"])) if len(proxy_ids_list) >= 1: - return proxy_ids_list['proxyids'][0] + return proxy_ids_list["proxyids"][0] except Exception as e: - self._module.fail_json(msg="Failed to create proxy %s: %s" % (data['host'], e)) + self._module.fail_json(msg="Failed to create proxy %s: %s" % (data["host"], e)) def delete_proxy(self, proxy_id, proxy_name): try: @@ -299,33 +277,22 @@ class Proxy(ZabbixBase): for key in data: if data[key]: parameters[key] = data[key] - if 'interface' in parameters: - if parameters['status'] == '5': + if "interface" in parameters: + if parameters["status"] == "5": # Active proxy - parameters.pop('interface', False) + parameters.pop("interface", False) else: # Passive proxy - parameters['interface']['useip'] = str(parameters['interface']['useip']) - if LooseVersion(self._zbx_api_version) >= LooseVersion('6.0.0'): - parameters['interface'].pop('type', False) - parameters['interface'].pop('main', False) - else: - parameters['interface']['type'] = '0' - parameters['interface']['main'] = '1' - if ('interface' in self.existing_data - and isinstance(self.existing_data['interface'], dict)): - new_interface = self.existing_data['interface'].copy() - new_interface.update(parameters['interface']) - parameters['interface'] = new_interface - - if parameters['status'] == '5': + parameters["interface"]["useip"] = str(parameters["interface"]["useip"]) + + if parameters["status"] == "5": # Active proxy - parameters.pop('tls_connect', False) + parameters.pop("tls_connect", False) else: # Passive proxy - parameters.pop('tls_accept', False) + parameters.pop("tls_accept", False) - parameters['proxyid'] = proxy_id + parameters["proxyid"] = proxy_id change_parameters = {} difference = zabbix_utils.helper_cleanup_data(zabbix_utils.helper_compare_dictionaries(parameters, self.existing_data, change_parameters)) @@ -333,43 +300,41 @@ class Proxy(ZabbixBase): if difference == {}: self._module.exit_json(changed=False) else: - difference['proxyid'] = proxy_id + difference["proxyid"] = proxy_id self._zapi.proxy.update(parameters) self._module.exit_json( changed=True, result="Successfully updated proxy %s (%s)" % - (data['host'], proxy_id) + (data["host"], proxy_id) ) except Exception as e: self._module.fail_json(msg="Failed to update proxy %s: %s" % - (data['host'], e)) + (data["host"], e)) def main(): argument_spec = zabbix_utils.zabbix_common_argument_spec() argument_spec.update(dict( - proxy_name=dict(type='str', required=True), - proxy_address=dict(type='str', required=False), - status=dict(type='str', default="active", choices=['active', 'passive']), - state=dict(type='str', default="present", choices=['present', 'absent']), - description=dict(type='str', required=False), - tls_connect=dict(type='str', default='no_encryption', choices=['no_encryption', 'PSK', 'certificate']), - tls_accept=dict(type='str', default='no_encryption', choices=['no_encryption', 'PSK', 'certificate']), - ca_cert=dict(type='str', required=False, default=None, aliases=['tls_issuer']), - tls_subject=dict(type='str', required=False, default=None), - tls_psk_identity=dict(type='str', required=False, default=None), - tls_psk=dict(type='str', required=False, default=None, no_log=True), + proxy_name=dict(type="str", required=True), + proxy_address=dict(type="str", required=False), + status=dict(type="str", default="active", choices=["active", "passive"]), + state=dict(type="str", default="present", choices=["present", "absent"]), + description=dict(type="str", required=False), + tls_connect=dict(type="str", default="no_encryption", choices=["no_encryption", "PSK", "certificate"]), + tls_accept=dict(type="str", default="no_encryption", choices=["no_encryption", "PSK", "certificate"]), + ca_cert=dict(type="str", required=False, default=None, aliases=["tls_issuer"]), + tls_subject=dict(type="str", required=False, default=None), + tls_psk_identity=dict(type="str", required=False, default=None), + tls_psk=dict(type="str", required=False, default=None, no_log=True), interface=dict( - type='dict', + type="dict", required=False, default={}, options=dict( - useip=dict(type='int', choices=[0, 1], default=0), - ip=dict(type='str', default=''), - dns=dict(type='str', default=''), - port=dict(type='str', default='10051'), - type=dict(type='int', default=0, removed_in_version="3.0.0", removed_from_collection='community.zabbix'), # was Ansible 2.14 - main=dict(type='int', default=0, removed_in_version="3.0.0", removed_from_collection='community.zabbix'), # was Ansible 2.14 + useip=dict(type="int", choices=[0, 1], default=0), + ip=dict(type="str", default=""), + dns=dict(type="str", default=""), + port=dict(type="str", default="10051") ), ) )) @@ -378,38 +343,32 @@ def main(): supports_check_mode=True ) - zabbix_utils.require_creds_params(module) - - for p in ['server_url', 'login_user', 'login_password', 'timeout', 'validate_certs']: - if p in module.params and not module.params[p] is None: - module.warn('Option "%s" is deprecated with the move to httpapi connection and will be removed in the next release' % p) - - proxy_name = module.params['proxy_name'] - proxy_address = module.params['proxy_address'] - description = module.params['description'] - status = module.params['status'] - tls_connect = module.params['tls_connect'] - tls_accept = module.params['tls_accept'] - tls_issuer = module.params['ca_cert'] - tls_subject = module.params['tls_subject'] - tls_psk_identity = module.params['tls_psk_identity'] - tls_psk = module.params['tls_psk'] - state = module.params['state'] - interface = module.params['interface'] + proxy_name = module.params["proxy_name"] + proxy_address = module.params["proxy_address"] + description = module.params["description"] + status = module.params["status"] + tls_connect = module.params["tls_connect"] + tls_accept = module.params["tls_accept"] + tls_issuer = module.params["ca_cert"] + tls_subject = module.params["tls_subject"] + tls_psk_identity = module.params["tls_psk_identity"] + tls_psk = module.params["tls_psk"] + state = module.params["state"] + interface = module.params["interface"] # convert enabled to 0; disabled to 1 status = 6 if status == "passive" else 5 - if tls_connect == 'certificate': + if tls_connect == "certificate": tls_connect = 4 - elif tls_connect == 'PSK': + elif tls_connect == "PSK": tls_connect = 2 else: tls_connect = 1 - if tls_accept == 'certificate': + if tls_accept == "certificate": tls_accept = 4 - elif tls_accept == 'PSK': + elif tls_accept == "PSK": tls_accept = 2 else: tls_accept = 1 @@ -425,17 +384,17 @@ def main(): proxy.delete_proxy(proxy_id, proxy_name) else: proxy.update_proxy(proxy_id, { - 'host': proxy_name, - 'description': description, - 'status': str(status), - 'tls_connect': str(tls_connect), - 'tls_accept': str(tls_accept), - 'tls_issuer': tls_issuer, - 'tls_subject': tls_subject, - 'tls_psk_identity': tls_psk_identity, - 'tls_psk': tls_psk, - 'interface': interface, - 'proxy_address': proxy_address + "host": proxy_name, + "description": description, + "status": str(status), + "tls_connect": str(tls_connect), + "tls_accept": str(tls_accept), + "tls_issuer": tls_issuer, + "tls_subject": tls_subject, + "tls_psk_identity": tls_psk_identity, + "tls_psk": tls_psk, + "interface": interface, + "proxy_address": proxy_address }) else: if state == "absent": @@ -443,19 +402,19 @@ def main(): module.exit_json(changed=False) proxy_id = proxy.add_proxy(data={ - 'host': proxy_name, - 'description': description, - 'status': str(status), - 'tls_connect': str(tls_connect), - 'tls_accept': str(tls_accept), - 'tls_issuer': tls_issuer, - 'tls_subject': tls_subject, - 'tls_psk_identity': tls_psk_identity, - 'tls_psk': tls_psk, - 'interface': interface, - 'proxy_address': proxy_address + "host": proxy_name, + "description": description, + "status": str(status), + "tls_connect": str(tls_connect), + "tls_accept": str(tls_accept), + "tls_issuer": tls_issuer, + "tls_subject": tls_subject, + "tls_psk_identity": tls_psk_identity, + "tls_psk": tls_psk, + "interface": interface, + "proxy_address": proxy_address }) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/ansible_collections/community/zabbix/plugins/modules/zabbix_proxy_info.py b/ansible_collections/community/zabbix/plugins/modules/zabbix_proxy_info.py index b40022883..c82112609 100644 --- a/ansible_collections/community/zabbix/plugins/modules/zabbix_proxy_info.py +++ b/ansible_collections/community/zabbix/plugins/modules/zabbix_proxy_info.py @@ -8,7 +8,7 @@ from __future__ import absolute_import, division, print_function __metaclass__ = type -DOCUMENTATION = ''' +DOCUMENTATION = """ module: zabbix_proxy_info short_description: Gather information about Zabbix proxy version_added: 1.5.0 @@ -17,7 +17,7 @@ author: description: - This module allows you to obtain detailed information about configured zabbix proxies. requirements: - - "python >= 2.6" + - "python >= 3.9" options: proxy_name: description: @@ -33,19 +33,19 @@ options: extends_documentation_fragment: - community.zabbix.zabbix -''' +""" -EXAMPLES = ''' +EXAMPLES = """ # If you want to use Username and Password to be authenticated by Zabbix Server - name: Set credentials to access Zabbix Server API - set_fact: + ansible.builtin.set_fact: ansible_user: Admin ansible_httpapi_pass: zabbix # If you want to use API token to be authenticated by Zabbix Server # https://www.zabbix.com/documentation/current/en/manual/web_interface/frontend_sections/administration/general#api-tokens - name: Set API token - set_fact: + ansible.builtin.set_fact: ansible_zabbix_auth_key: 8ec0d52432c15c91fcafe9888500cf9a607f44091ab554dbee860f6b44fac895 - name: Get zabbix proxy info alongside the list of hosts monitored by the proxy @@ -56,17 +56,14 @@ EXAMPLES = ''' ansible_httpapi_port: 443 ansible_httpapi_use_ssl: true ansible_httpapi_validate_certs: false - ansible_zabbix_url_path: 'zabbixeu' # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu + ansible_zabbix_url_path: "zabbixeu" # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu ansible_host: zabbix-example-fqdn.org community.zabbix.zabbix_proxy_info: - server_url: "http://zabbix.example.com/zabbix/" - login_user: admin - login_password: secret proxy_name: zbx01.example.com proxy_hosts: true -''' +""" -RETURN = ''' +RETURN = """ zabbix_proxy: description: example returned: always @@ -120,7 +117,7 @@ zabbix_proxy: "tls_subject": "", "uuid": "" } -''' +""" from ansible.module_utils.basic import AnsibleModule @@ -134,15 +131,15 @@ class Proxy(ZabbixBase): def get_proxy(self, name, hosts=False): result = {} params = { - 'filter': { - 'host': name + "filter": { + "host": name }, - 'output': 'extend', - 'selectInterface': 'extend', + "output": "extend", + "selectInterface": "extend", } if hosts: - params['selectHosts'] = ['host', 'hostid'] + params["selectHosts"] = ["host", "hostid"] try: result = self._zapi.proxy.get(params) @@ -155,8 +152,8 @@ class Proxy(ZabbixBase): def main(): argument_spec = zabbix_utils.zabbix_common_argument_spec() argument_spec.update(dict( - proxy_name=dict(type='str', required=True), - proxy_hosts=dict(type='bool', required=False, default=False), + proxy_name=dict(type="str", required=True), + proxy_hosts=dict(type="bool", required=False, default=False), )) module = AnsibleModule( @@ -164,14 +161,8 @@ def main(): supports_check_mode=True ) - zabbix_utils.require_creds_params(module) - - for p in ['server_url', 'login_user', 'login_password', 'timeout', 'validate_certs']: - if p in module.params and not module.params[p] is None: - module.warn('Option "%s" is deprecated with the move to httpapi connection and will be removed in the next release' % p) - - name = module.params['proxy_name'] - hosts = module.params['proxy_hosts'] + name = module.params["proxy_name"] + hosts = module.params["proxy_hosts"] proxy = Proxy(module) result = proxy.get_proxy(name, hosts) diff --git a/ansible_collections/community/zabbix/plugins/modules/zabbix_regexp.py b/ansible_collections/community/zabbix/plugins/modules/zabbix_regexp.py new file mode 100644 index 000000000..fc3ccedee --- /dev/null +++ b/ansible_collections/community/zabbix/plugins/modules/zabbix_regexp.py @@ -0,0 +1,345 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# +# Copyright: (c) 2022, ONODERA Masaru <masaru-onodera@ieee.org> +# 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 + +__metaclass__ = type + + +DOCUMENTATION = """ +--- +module: zabbix_regexp + +short_description: Create/update/delete Zabbix regular expression + + +description: + - This module allows you to create, update and delete Zabbix regular expression. + +author: + - ONODERA Masaru(@masa-orca) + +requirements: + - "python >= 3.9" + +version_added: 2.1.0 + +options: + name: + description: + - Name of this regular expression + type: str + required: true + test_string: + description: + - A test string for this regular expression + type: str + expressions: + description: + - List of expressions. + - The regular expression returns true when all expressions return true. + - Required when C(state=present). + type: list + elements: dict + suboptions: + expression: + description: + - A expression string + type: str + required: true + expression_type: + description: + - A expression string + type: str + required: true + choices: + - "character_string_included" + - "any_character_string_included" + - "character_string_not_included" + - "result_is_true" + - "result_is_false" + exp_delimiter: + description: + - Delimiter for expression. + - Used if expression_type is C(any_character_string_included). + - Default values is C(,) + type: str + choices: [",", ".", "/"] + case_sensitive: + description: + - If true, the expression will be case sensitive. + type: bool + default: false + state: + description: + - State of the regular expression. + type: str + choices: ['present', 'absent'] + default: 'present' + + +notes: + - Only Zabbix >= 6.0 is supported. + +extends_documentation_fragment: + - community.zabbix.zabbix + +""" + +EXAMPLES = """ +# If you want to use Username and Password to be authenticated by Zabbix Server +- name: Set credentials to access Zabbix Server API + ansible.builtin.set_fact: + ansible_user: Admin + ansible_httpapi_pass: zabbix + +# If you want to use API token to be authenticated by Zabbix Server +# https://www.zabbix.com/documentation/current/en/manual/web_interface/frontend_sections/administration/general#api-tokens +- name: Set API token + ansible.builtin.set_fact: + ansible_zabbix_auth_key: 8ec0d52432c15c91fcafe9888500cf9a607f44091ab554dbee860f6b44fac895 + +- name: Update regexp of 'File systems for discovery' + # set task level variables as we change ansible_connection plugin here + vars: + ansible_network_os: community.zabbix.zabbix + ansible_connection: httpapi + ansible_httpapi_port: 443 + ansible_httpapi_use_ssl: true + ansible_httpapi_validate_certs: false + ansible_zabbix_url_path: 'zabbixeu' # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu + ansible_host: zabbix-example-fqdn.org + community.zabbix.zabbix_regexp: + name: File systems for discovery + test_string: ext2 + expressions: + - expression: "^(btrfs|ext2|ext3|ext4|reiser|xfs|ffs|ufs|jfs|jfs2|vxfs|hfs|apfs|refs|ntfs|fat32|zfs)$" + expression_type: result_is_true +""" + +RETURN = """ +msg: + description: The result of the operation + returned: success + type: str + sample: 'Successfully updated regular expression setting' +""" + + +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.community.zabbix.plugins.module_utils.base import ZabbixBase +from ansible.module_utils.compat.version import LooseVersion + +import ansible_collections.community.zabbix.plugins.module_utils.helpers as zabbix_utils + + +class RegularExpression(ZabbixBase): + def __init__(self, module, zbx=None, zapi_wrapper=None): + super(RegularExpression, self).__init__(module, zbx, zapi_wrapper) + if LooseVersion(self._zbx_api_version) < LooseVersion("6.0"): + module.fail_json( + msg="This module doesn't support Zabbix versions lower than 6.0" + ) + + def get_regexps(self, regexp_name): + try: + regexps = self._zapi.regexp.get( + { + "output": "extend", + "selectExpressions": [ + "expression", + "expression_type", + "exp_delimiter", + "case_sensitive", + ], + "filter": {"name": regexp_name}, + } + ) + if len(regexps) >= 2: + self._module.fail_json("Too many regexps are matched.") + return regexps + except Exception as e: + self._module.fail_json( + msg="Failed to get regular expression setting: %s" % e + ) + + def delete_regexp(self, regexp): + try: + parameter = [regexp["regexpid"]] + if self._module.check_mode: + self._module.exit_json(changed=True) + self._zapi.regexp.delete(parameter) + self._module.exit_json( + changed=True, msg="Successfully deleted regular expression setting." + ) + except Exception as e: + self._module.fail_json( + msg="Failed to delete regular expression setting: %s" % e + ) + + def _convert_expressions_to_json(self, expressions): + expression_type_values = [ + "character_string_included", + "any_character_string_included", + "character_string_not_included", + "result_is_true", + "result_is_false", + ] + + expression_jsons = [] + for expression in expressions: + expression_json = {} + + expression_json["expression"] = expression["expression"] + expression_type = zabbix_utils.helper_to_numeric_value( + expression_type_values, expression["expression_type"] + ) + expression_json["expression_type"] = str(expression_type) + if expression["expression_type"] == "any_character_string_included": + if expression["exp_delimiter"]: + expression_json["exp_delimiter"] = expression["exp_delimiter"] + else: + expression_json["exp_delimiter"] = "," + elif expression["exp_delimiter"]: + self._module.warn( + "A value of exp_delimiter will be ignored because expression_type is not 'any_character_string_included'." + ) + case_sensitive = "0" + if expression["case_sensitive"]: + case_sensitive = "1" + expression_json["case_sensitive"] = case_sensitive + + expression_jsons.append(expression_json) + return expression_jsons + + def create_regexp(self, name, test_string, expressions): + try: + if self._module.check_mode: + self._module.exit_json(changed=True) + self._zapi.regexp.create( + { + "name": name, + "test_string": test_string, + "expressions": self._convert_expressions_to_json(expressions), + } + ) + self._module.exit_json( + changed=True, msg="Successfully created regular expression setting." + ) + except Exception as e: + self._module.fail_json( + msg="Failed to create regular expression setting: %s" % e + ) + + def update_regexp(self, current_regexp, name, test_string, expressions): + try: + current_expressions = [] + for expression in current_regexp["expressions"]: + if expression["expression_type"] != "1": + expression = zabbix_utils.helper_normalize_data( + expression, del_keys=["exp_delimiter"] + )[0] + current_expressions.append(expression) + future_expressions = self._convert_expressions_to_json(expressions) + diff_expressions = [] + zabbix_utils.helper_compare_lists( + current_expressions, future_expressions, diff_expressions + ) + if ( + current_regexp["name"] == name + and current_regexp["test_string"] == test_string + and len(diff_expressions) == 0 + ): + self._module.exit_json(changed=False) + else: + if self._module.check_mode: + self._module.exit_json(changed=True) + self._zapi.regexp.update( + { + "regexpid": current_regexp["regexpid"], + "name": name, + "test_string": test_string, + "expressions": future_expressions, + } + ) + self._module.exit_json( + changed=True, msg="Successfully updated regular expression setting." + ) + except Exception as e: + self._module.fail_json( + msg="Failed to update regular expression setting: %s" % e + ) + + +def main(): + """Main ansible module function""" + + argument_spec = zabbix_utils.zabbix_common_argument_spec() + argument_spec.update( + dict( + name=dict(type="str", required=True), + test_string=dict( + type="str", + ), + expressions=dict( + type="list", + elements="dict", + options=dict( + expression=dict(type="str", required=True), + expression_type=dict( + type="str", + required=True, + choices=[ + "character_string_included", + "any_character_string_included", + "character_string_not_included", + "result_is_true", + "result_is_false", + ], + ), + exp_delimiter=dict(type="str", choices=[",", ".", "/"]), + case_sensitive=dict(type="bool", default=False), + ), + ), + state=dict( + type="str", + required=False, + default="present", + choices=["present", "absent"], + ), + ) + ) + + module = AnsibleModule( + argument_spec=argument_spec, + required_if=[["state", "present", ["expressions"]]], + supports_check_mode=True, + ) + + name = module.params["name"] + test_string = module.params["test_string"] + expressions = module.params["expressions"] + state = module.params["state"] + + regexp_class_obj = RegularExpression(module) + regexps = regexp_class_obj.get_regexps(name) + + if state == "absent": + if len(regexps) == 1: + regexp_class_obj.delete_regexp(regexps[0]) + else: + module.exit_json(changed=False) + else: + if len(regexps) == 1: + regexp_class_obj.update_regexp(regexps[0], name, test_string, expressions) + else: + regexp_class_obj.create_regexp(name, test_string, expressions) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/community/zabbix/plugins/modules/zabbix_screen.py b/ansible_collections/community/zabbix/plugins/modules/zabbix_screen.py deleted file mode 100644 index 5e350fc80..000000000 --- a/ansible_collections/community/zabbix/plugins/modules/zabbix_screen.py +++ /dev/null @@ -1,496 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - -# (c) 2013-2014, Epic Games, Inc. -# 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 -__metaclass__ = type - - -DOCUMENTATION = r''' ---- -module: zabbix_screen -short_description: Create/update/delete Zabbix screens -description: - - This module allows you to create, modify and delete Zabbix screens and associated graph data. -author: - - "Cove (@cove)" - - "Tony Minfei Ding (!UNKNOWN)" - - "Harrison Gu (@harrisongu)" -requirements: - - "python >= 2.6" - - "Zabbix <= 5.2" -options: - screens: - description: - - List of screens to be created/updated/deleted (see example). - type: list - elements: dict - required: true - suboptions: - screen_name: - description: - - Screen name will be used. - - If a screen has already been added, the screen name won't be updated. - type: str - required: true - host_group: - description: - - Host group(s) will be used for searching hosts. - - Required if I(state=present). - type: list - elements: str - aliases: [ 'host_groups' ] - state: - description: - - I(present) - Create a screen if it doesn't exist. If the screen already exists, the screen will be updated as needed. - - I(absent) - If a screen exists, the screen will be deleted. - type: str - default: present - choices: - - absent - - present - graph_names: - description: - - Graph names will be added to a screen. Case insensitive. - - Required if I(state=present). - type: list - elements: str - graph_width: - description: - - Graph width will be set in graph settings. - type: int - graph_height: - description: - - Graph height will be set in graph settings. - type: int - graphs_in_row: - description: - - Limit columns of a screen and make multiple rows. - type: int - default: 3 - sort: - description: - - Sort hosts alphabetically. - - If there are numbers in hostnames, leading zero should be used. - type: bool - default: no - -extends_documentation_fragment: -- community.zabbix.zabbix - - -notes: - - Too many concurrent updates to the same screen may cause Zabbix to return errors, see examples for a workaround if needed. - - Screens where removed from Zabbix with Version 5.4 -''' - -EXAMPLES = r''' -# If you want to use Username and Password to be authenticated by Zabbix Server -- name: Set credentials to access Zabbix Server API - set_fact: - ansible_user: Admin - ansible_httpapi_pass: zabbix - -# If you want to use API token to be authenticated by Zabbix Server -# https://www.zabbix.com/documentation/current/en/manual/web_interface/frontend_sections/administration/general#api-tokens -- name: Set API token - set_fact: - ansible_zabbix_auth_key: 8ec0d52432c15c91fcafe9888500cf9a607f44091ab554dbee860f6b44fac895 - -# Screens where removed from Zabbix with Version 5.4 - -# Create/update a screen. -- name: Create a new screen or update an existing screen's items 5 in a row - # set task level variables as we change ansible_connection plugin here - vars: - ansible_network_os: community.zabbix.zabbix - ansible_connection: httpapi - ansible_httpapi_port: 443 - ansible_httpapi_use_ssl: true - ansible_httpapi_validate_certs: false - ansible_zabbix_url_path: 'zabbixeu' # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu - ansible_host: zabbix-example-fqdn.org - community.zabbix.zabbix_screen: - screens: - - screen_name: ExampleScreen1 - host_group: Example group1 - state: present - graph_names: - - Example graph1 - - Example graph2 - graph_width: 200 - graph_height: 100 - graphs_in_row: 5 - -# Create/update multi-screen -- name: Create two of new screens or update the existing screens' items - # set task level variables as we change ansible_connection plugin here - vars: - ansible_network_os: community.zabbix.zabbix - ansible_connection: httpapi - ansible_httpapi_port: 443 - ansible_httpapi_use_ssl: true - ansible_httpapi_validate_certs: false - ansible_zabbix_url_path: 'zabbixeu' # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu - ansible_host: zabbix-example-fqdn.org - community.zabbix.zabbix_screen: - screens: - - screen_name: ExampleScreen1 - host_group: Example group1 - state: present - graph_names: - - Example graph1 - - Example graph2 - graph_width: 200 - graph_height: 100 - - screen_name: ExampleScreen2 - host_group: Example group2 - state: present - graph_names: - - Example graph1 - - Example graph2 - graph_width: 200 - graph_height: 100 - -# Limit the Zabbix screen creations to one host since Zabbix can return an error when doing concurrent updates -- name: Create a new screen or update an existing screen's items - # set task level variables as we change ansible_connection plugin here - vars: - ansible_network_os: community.zabbix.zabbix - ansible_connection: httpapi - ansible_httpapi_port: 443 - ansible_httpapi_use_ssl: true - ansible_httpapi_validate_certs: false - ansible_zabbix_url_path: 'zabbixeu' # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu - ansible_host: zabbix-example-fqdn.org - community.zabbix.zabbix_screen: - state: present - screens: - - screen_name: ExampleScreen - host_group: Example group - state: present - graph_names: - - Example graph1 - - Example graph2 - graph_width: 200 - graph_height: 100 - when: inventory_hostname==groups['group_name'][0] - -# Create/update using multiple hosts_groups. Hosts NOT present in all listed host_groups will be skipped. -- name: Create new screen or update the existing screen's items for hosts in both given groups - # set task level variables as we change ansible_connection plugin here - vars: - ansible_network_os: community.zabbix.zabbix - ansible_connection: httpapi - ansible_httpapi_port: 443 - ansible_httpapi_use_ssl: true - ansible_httpapi_validate_certs: false - ansible_zabbix_url_path: 'zabbixeu' # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu - ansible_host: zabbix-example-fqdn.org - community.zabbix.zabbix_screen: - screens: - - screen_name: ExampleScreen1 - host_group: - - Example group1 - - Example group2 - state: present - graph_names: - - Example graph1 - - Example graph2 - graph_width: 200 - graph_height: 100 -''' - - -from ansible.module_utils.basic import AnsibleModule - -from ansible_collections.community.zabbix.plugins.module_utils.base import ZabbixBase -from ansible_collections.community.zabbix.plugins.module_utils.wrappers import ScreenItem -from ansible.module_utils.compat.version import LooseVersion - -import ansible_collections.community.zabbix.plugins.module_utils.helpers as zabbix_utils - - -class Screen(ZabbixBase): - # get list of group ids by list of group names - def get_host_group_ids(self, group_names): - if not group_names: - self._module.fail_json(msg="group_name is required") - hostGroup_list = self._zapi.hostgroup.get({'output': 'extend', 'filter': {'name': group_names}}) - if not hostGroup_list: - self._module.fail_json(msg="Host group not found: {0}".format(group_names)) - else: - hostGroup_ids = [g['groupid'] for g in hostGroup_list] - return hostGroup_ids - - # get monitored host_ids by host_group_ids - # (the hosts belonging to all given groups) - def get_host_ids_by_group_ids(self, group_ids, sort): - host_list = self._zapi.host.get({'output': 'extend', 'selectGroups': 'groupid', 'groupids': group_ids, 'monitored_hosts': 1}) - if not host_list: - self._module.fail_json(msg="No hosts in the all group(s) with ids {0}".format(group_ids)) - else: - if sort: - host_list = sorted(host_list, key=lambda name: name['name']) - host_ids = [] - for host in host_list: - host_group_ids = [g['groupid'] for g in host['groups']] - # Check if all search group ids are in hosts group ids - if set(group_ids).issubset(host_group_ids): - host_id = host['hostid'] - host_ids.append(host_id) - return host_ids - - # get screen - def get_screen_id(self, screen_name): - if screen_name == "": - self._module.fail_json(msg="screen_name is required") - try: - screen_id_list = self._zapi.screen.get({'output': 'extend', 'search': {"name": screen_name}}) - if len(screen_id_list) >= 1: - screen_id = screen_id_list[0]['screenid'] - return screen_id - return None - except Exception as e: - self._module.fail_json(msg="Failed to get screen %s from Zabbix: %s" % (screen_name, e)) - - # create screen - def create_screen(self, screen_name, h_size, v_size): - try: - if self._module.check_mode: - self._module.exit_json(changed=True) - screen = self._zapi.screen.create({'name': screen_name, 'hsize': h_size, 'vsize': v_size}) - return screen['screenids'][0] - except Exception as e: - self._module.fail_json(msg="Failed to create screen %s: %s" % (screen_name, e)) - - # update screen - def update_screen(self, screen_id, screen_name, h_size, v_size): - try: - if self._module.check_mode: - self._module.exit_json(changed=True) - self._zapi.screen.update({'screenid': screen_id, 'hsize': h_size, 'vsize': v_size}) - except Exception as e: - self._module.fail_json(msg="Failed to update screen %s: %s" % (screen_name, e)) - - # delete screen - def delete_screen(self, screen_id, screen_name): - try: - if self._module.check_mode: - self._module.exit_json(changed=True) - self._zapi.screen.delete([screen_id]) - except Exception as e: - self._module.fail_json(msg="Failed to delete screen %s: %s" % (screen_name, e)) - - # get graph ids - def get_graph_ids(self, hosts, graph_name_list): - graph_id_lists = [] - vsize = 1 - for host in hosts: - graph_id_list = self.get_graphs_by_host_id(graph_name_list, host) - size = len(graph_id_list) - if size > 0: - graph_id_lists.extend(graph_id_list) - if vsize < size: - vsize = size - return graph_id_lists, vsize - - # getGraphs - def get_graphs_by_host_id(self, graph_name_list, host_id): - graph_ids = [] - for graph_name in graph_name_list: - graphs_list = self._zapi.graph.get({'output': 'extend', 'search': {'name': graph_name}, 'hostids': host_id}) - graph_id_list = [] - if len(graphs_list) > 0: - for graph in graphs_list: - graph_id = graph['graphid'] - graph_id_list.append(graph_id) - if len(graph_id_list) > 0: - graph_ids.extend(graph_id_list) - return graph_ids - - # get screen items - def get_screen_items(self, screen_id): - screen_item_list = self._zapi.screenitem.get({'output': 'extend', 'screenids': screen_id}) - return screen_item_list - - # delete screen items - def delete_screen_items(self, screen_id, screen_item_id_list): - if len(screen_item_id_list) == 0: - return True - screen_item_list = self.get_screen_items(screen_id) - if len(screen_item_list) > 0: - if self._module.check_mode: - self._module.exit_json(changed=True) - ScreenItem.delete(self, screen_item_id_list) - return True - return False - - # get screen's hsize and vsize - def get_hsize_vsize(self, hosts, v_size, graphs_in_row): - h_size = len(hosts) - # when there is only one host, put all graphs in a row - if h_size == 1: - if v_size <= graphs_in_row: - h_size = v_size - else: - h_size = graphs_in_row - v_size = (v_size - 1) // h_size + 1 - # when len(hosts) is more then graphs_in_row - elif len(hosts) > graphs_in_row: - h_size = graphs_in_row - v_size = (len(hosts) // graphs_in_row + 1) * v_size - - return h_size, v_size - - # create screen_items - def create_screen_items(self, screen_id, hosts, graph_name_list, width, height, h_size, graphs_in_row): - if len(hosts) < 4: - if width is None or width < 0: - width = 500 - else: - if width is None or width < 0: - width = 200 - if height is None or height < 0: - height = 100 - - # when there're only one host, only one row is not good. - if len(hosts) == 1: - graph_id_list = self.get_graphs_by_host_id(graph_name_list, hosts[0]) - for i, graph_id in enumerate(graph_id_list): - if graph_id is not None: - ScreenItem.create(self, ignoreExists=True, data={'screenid': screen_id, 'resourcetype': 0, 'resourceid': graph_id, - 'width': width, 'height': height, - 'x': i % h_size, 'y': i // h_size, 'colspan': 1, 'rowspan': 1, - 'elements': 0, 'valign': 0, 'halign': 0, - 'style': 0, 'dynamic': 0, 'sort_triggers': 0}) - else: - for i, host in enumerate(hosts): - graph_id_list = self.get_graphs_by_host_id(graph_name_list, host) - for j, graph_id in enumerate(graph_id_list): - if graph_id is not None: - ScreenItem.create(self, ignoreExists=True, data={'screenid': screen_id, 'resourcetype': 0, 'resourceid': graph_id, - 'width': width, 'height': height, - 'x': i % graphs_in_row, 'y': len(graph_id_list) * (i // graphs_in_row) + j, - 'colspan': 1, 'rowspan': 1, - 'elements': 0, 'valign': 0, 'halign': 0, - 'style': 0, 'dynamic': 0, 'sort_triggers': 0}) - - -def main(): - argument_spec = zabbix_utils.zabbix_common_argument_spec() - argument_spec.update(dict( - screens=dict( - type='list', - elements='dict', - required=True, - options=dict( - screen_name=dict(type='str', required=True), - host_group=dict(type='list', aliases=['host_groups'], elements='str'), - state=dict(type='str', default='present', choices=['absent', 'present']), - graph_names=dict(type='list', elements='str'), - graph_width=dict(type='int', default=None), - graph_height=dict(type='int', default=None), - graphs_in_row=dict(type='int', default=3), - sort=dict(default=False, type='bool'), - ), - required_if=[ - ['state', 'present', ['host_group']] - ] - ) - )) - module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True - ) - - zabbix_utils.require_creds_params(module) - - for p in ['server_url', 'login_user', 'login_password', 'timeout', 'validate_certs']: - if p in module.params and not module.params[p] is None: - module.warn('Option "%s" is deprecated with the move to httpapi connection and will be removed in the next release' % p) - - screens = module.params['screens'] - - screen = Screen(module) - if LooseVersion(screen._zbx_api_version) >= LooseVersion('5.4'): - module.fail_json(msg="Zabbix 5.4 removed the Screens feature see (%s)." % ( - "https://www.zabbix.com/documentation/current/en/manual/api/changes_5.2_-_5.4" - )) - - created_screens = [] - changed_screens = [] - deleted_screens = [] - - for zabbix_screen in screens: - screen_name = zabbix_screen['screen_name'] - screen_id = screen.get_screen_id(screen_name) - state = zabbix_screen['state'] - sort = zabbix_screen['sort'] - - if state == "absent": - if screen_id: - screen_item_list = screen.get_screen_items(screen_id) - screen_item_id_list = [] - for screen_item in screen_item_list: - screen_item_id = screen_item['screenitemid'] - screen_item_id_list.append(screen_item_id) - screen.delete_screen_items(screen_id, screen_item_id_list) - screen.delete_screen(screen_id, screen_name) - - deleted_screens.append(screen_name) - else: - host_group = zabbix_screen['host_group'] - graph_names = zabbix_screen['graph_names'] - graphs_in_row = zabbix_screen['graphs_in_row'] - graph_width = zabbix_screen['graph_width'] - graph_height = zabbix_screen['graph_height'] - host_group_ids = screen.get_host_group_ids(host_group) - hosts = screen.get_host_ids_by_group_ids(host_group_ids, sort) - if not hosts: - module.fail_json(msg="No hosts found belongin to all given groups: %s" % host_group) - screen_item_id_list = [] - resource_id_list = [] - - graph_ids, v_size = screen.get_graph_ids(hosts, graph_names) - h_size, v_size = screen.get_hsize_vsize(hosts, v_size, graphs_in_row) - - if not screen_id: - # create screen - screen_id = screen.create_screen(screen_name, h_size, v_size) - screen.create_screen_items(screen_id, hosts, graph_names, graph_width, graph_height, h_size, graphs_in_row) - created_screens.append(screen_name) - else: - screen_item_list = screen.get_screen_items(screen_id) - - for screen_item in screen_item_list: - screen_item_id = screen_item['screenitemid'] - resource_id = screen_item['resourceid'] - screen_item_id_list.append(screen_item_id) - resource_id_list.append(resource_id) - - # when the screen items changed, then update - if graph_ids != resource_id_list: - deleted = screen.delete_screen_items(screen_id, screen_item_id_list) - if deleted: - screen.update_screen(screen_id, screen_name, h_size, v_size) - screen.create_screen_items(screen_id, hosts, graph_names, graph_width, graph_height, h_size, graphs_in_row) - changed_screens.append(screen_name) - - if created_screens and changed_screens: - module.exit_json(changed=True, result="Successfully created screen(s): %s, and updated screen(s): %s" % (",".join(created_screens), - ",".join(changed_screens))) - elif created_screens: - module.exit_json(changed=True, result="Successfully created screen(s): %s" % ",".join(created_screens)) - elif changed_screens: - module.exit_json(changed=True, result="Successfully updated screen(s): %s" % ",".join(changed_screens)) - elif deleted_screens: - module.exit_json(changed=True, result="Successfully deleted screen(s): %s" % ",".join(deleted_screens)) - else: - module.exit_json(changed=False) - - -if __name__ == '__main__': - main() diff --git a/ansible_collections/community/zabbix/plugins/modules/zabbix_script.py b/ansible_collections/community/zabbix/plugins/modules/zabbix_script.py index 15277b0fa..643851885 100644 --- a/ansible_collections/community/zabbix/plugins/modules/zabbix_script.py +++ b/ansible_collections/community/zabbix/plugins/modules/zabbix_script.py @@ -8,7 +8,7 @@ from __future__ import absolute_import, division, print_function __metaclass__ = type -DOCUMENTATION = ''' +DOCUMENTATION = """ module: zabbix_script short_description: Create/update/delete Zabbix scripts version_added: 1.7.0 @@ -17,7 +17,7 @@ author: description: - This module allows you to create, update and delete scripts. requirements: - - "python >= 2.6" + - "python >= 3.9" options: name: description: @@ -27,10 +27,9 @@ options: script_type: description: - Script type. - - Types C(ssh), C(telnet) and C(webhook) works only with Zabbix >= 5.4. type: str required: true - choices: ['script', 'ipmi', 'ssh', 'telnet', 'webhook'] + choices: ["script", "ipmi", "ssh", "telnet", "webhook"] command: description: - Command to run. @@ -39,24 +38,22 @@ options: scope: description: - Script scope. - - Works only with Zabbix >= 5.4. For lower versions is silently ignored which is equivalent of C(manual_host_action). type: str required: false - choices: ['action_operation', 'manual_host_action', 'manual_event_action'] - default: 'action_operation' + choices: ["action_operation", "manual_host_action", "manual_event_action"] + default: "action_operation" execute_on: description: - Where to run the script. - Used if type is C(script). type: str required: false - choices: ['zabbix_agent', 'zabbix_server', 'zabbix_server_proxy'] - default: 'zabbix_server_proxy' + choices: ["zabbix_agent", "zabbix_server", "zabbix_server_proxy"] + default: "zabbix_server_proxy" menu_path: description: - Folders separated by slash that form a menu like navigation in frontend when clicked on host or event. - Used if scope is C(manual_host_action) or C(manual_event_action). - - Works only with Zabbix >= 5.4. For lower versions is silently ignored. Prepend menu path to name instead. type: str required: false authtype: @@ -65,7 +62,7 @@ options: - Used if type is C(ssh). type: str required: false - choices: ['password', 'public_key'] + choices: ["password", "public_key"] username: description: - User name used for authentication. @@ -98,25 +95,25 @@ options: required: false host_group: description: - - host group name that the script can be run on. If set to 'all', the script will be available on all host groups. + - host group name that the script can be run on. If set to "all", the script will be available on all host groups. type: str required: false - default: 'all' + default: "all" user_group: description: - - user group name that will be allowed to run the script. If set to 'all', the script will be available for all user groups. + - user group name that will be allowed to run the script. If set to "all", the script will be available for all user groups. - Used if scope is C(manual_host_action) or C(manual_event_action). type: str required: false - default: 'all' + default: "all" host_access: description: - Host permissions needed to run the script. - Used if scope is C(manual_host_action) or C(manual_event_action). type: str required: false - choices: ['read', 'write'] - default: 'read' + choices: ["read", "write"] + default: "read" confirmation: description: - Confirmation pop up text. The pop up will appear when trying to run the script from the Zabbix frontend. @@ -127,9 +124,9 @@ options: description: - Webhook script execution timeout in seconds. Time suffixes are supported, e.g. 30s, 1m. - Required if type is C(webhook). - - 'Possible values: 1-60s.' + - "Possible values: 1-60s." type: str - default: '30s' + default: "30s" required: false parameters: description: @@ -148,7 +145,7 @@ options: - Parameter value. Supports macros. type: str required: false - default: '' + default: "" description: description: - Description of the script. @@ -159,24 +156,24 @@ options: - State of the script. type: str required: false - choices: ['present', 'absent'] - default: 'present' + choices: ["present", "absent"] + default: "present" extends_documentation_fragment: - community.zabbix.zabbix -''' +""" -EXAMPLES = ''' +EXAMPLES = """ # If you want to use Username and Password to be authenticated by Zabbix Server - name: Set credentials to access Zabbix Server API - set_fact: + ansible.builtin.set_fact: ansible_user: Admin ansible_httpapi_pass: zabbix # If you want to use API token to be authenticated by Zabbix Server # https://www.zabbix.com/documentation/current/en/manual/web_interface/frontend_sections/administration/general#api-tokens - name: Set API token - set_fact: + ansible.builtin.set_fact: ansible_zabbix_auth_key: 8ec0d52432c15c91fcafe9888500cf9a607f44091ab554dbee860f6b44fac895 - name: test - Create new action operation script to execute webhook @@ -187,34 +184,33 @@ EXAMPLES = ''' ansible_httpapi_port: 443 ansible_httpapi_use_ssl: true ansible_httpapi_validate_certs: false - ansible_zabbix_url_path: 'zabbixeu' # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu + ansible_zabbix_url_path: "zabbixeu" # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu ansible_host: zabbix-example-fqdn.org - zabbix_script: + community.zabbix.zabbix_script: name: Test action operation script scope: action_operation script_type: webhook - command: 'return 0' + command: "return 0" description: "Test action operation script" state: present -''' +""" -RETURN = ''' -''' +RETURN = """ +""" from ansible.module_utils.basic import AnsibleModule from ansible_collections.community.zabbix.plugins.module_utils.base import ZabbixBase -from ansible.module_utils.compat.version import LooseVersion import ansible_collections.community.zabbix.plugins.module_utils.helpers as zabbix_utils class Script(ZabbixBase): def get_script_ids(self, script_name): script_ids = [] - scripts = self._zapi.script.get({'filter': {'name': script_name}}) + scripts = self._zapi.script.get({"filter": {"name": script_name}}) for script in scripts: - script_ids.append(script['scriptid']) + script_ids.append(script["scriptid"]) return script_ids def create_script(self, name, script_type, command, scope, execute_on, menu_path, authtype, username, password, @@ -233,103 +229,95 @@ class Script(ZabbixBase): def generate_script_config(self, name, script_type, command, scope, execute_on, menu_path, authtype, username, password, publickey, privatekey, port, host_group, user_group, host_access, confirmation, script_timeout, parameters, description): - if host_group == 'all': - groupid = '0' + if host_group == "all": + groupid = "0" else: - groups = self._zapi.hostgroup.get({'filter': {'name': host_group}}) + groups = self._zapi.hostgroup.get({"filter": {"name": host_group}}) if not groups: - self._module.fail_json(changed=False, msg='Host group "%s" not found' % host_group) - groupid = groups[0]['groupid'] + self._module.fail_json(changed=False, msg="Host group '%s' not found" % host_group) + groupid = groups[0]["groupid"] - if user_group == 'all': - usrgrpid = '0' + if user_group == "all": + usrgrpid = "0" else: - user_groups = self._zapi.usergroup.get({'filter': {'name': user_group}}) + user_groups = self._zapi.usergroup.get({"filter": {"name": user_group}}) if not user_groups: - self._module.fail_json(changed=False, msg='User group "%s" not found' % user_group) - usrgrpid = user_groups[0]['usrgrpid'] + self._module.fail_json(changed=False, msg="User group '%s' not found" % user_group) + usrgrpid = user_groups[0]["usrgrpid"] request = { - 'name': name, - 'type': str(zabbix_utils.helper_to_numeric_value([ - 'script', - 'ipmi', - 'ssh', - 'telnet', - '', - 'webhook'], script_type)), - 'command': command, - 'scope': str(zabbix_utils.helper_to_numeric_value([ - '', - 'action_operation', - 'manual_host_action', - '', - 'manual_event_action'], scope)), - 'groupid': groupid + "name": name, + "type": str(zabbix_utils.helper_to_numeric_value([ + "script", + "ipmi", + "ssh", + "telnet", + "", + "webhook"], script_type)), + "command": command, + "scope": str(zabbix_utils.helper_to_numeric_value([ + "", + "action_operation", + "manual_host_action", + "", + "manual_event_action"], scope)), + "groupid": groupid } if description is not None: - request['description'] = description + request["description"] = description - if script_type == 'script': + if script_type == "script": if execute_on is None: - execute_on = 'zabbix_server_proxy' - request['execute_on'] = str(zabbix_utils.helper_to_numeric_value([ - 'zabbix_agent', - 'zabbix_server', - 'zabbix_server_proxy'], execute_on)) + execute_on = "zabbix_server_proxy" + request["execute_on"] = str(zabbix_utils.helper_to_numeric_value([ + "zabbix_agent", + "zabbix_server", + "zabbix_server_proxy"], execute_on)) - if scope in ['manual_host_action', 'manual_event_action']: + if scope in ["manual_host_action", "manual_event_action"]: if menu_path is None: - request['menu_path'] = '' + request["menu_path"] = "" else: - request['menu_path'] = menu_path - request['usrgrpid'] = usrgrpid - request['host_access'] = str(zabbix_utils.helper_to_numeric_value([ - '', - '', - 'read', - 'write'], host_access)) + request["menu_path"] = menu_path + request["usrgrpid"] = usrgrpid + request["host_access"] = str(zabbix_utils.helper_to_numeric_value([ + "", + "", + "read", + "write"], host_access)) if confirmation is None: - request['confirmation'] = '' + request["confirmation"] = "" else: - request['confirmation'] = confirmation + request["confirmation"] = confirmation - if script_type == 'ssh': + if script_type == "ssh": if authtype is None: - self._module.fail_json(changed=False, msg='authtype must be provided for ssh script type') - request['authtype'] = str(zabbix_utils.helper_to_numeric_value([ - 'password', - 'public_key'], authtype)) - if authtype == 'public_key': + self._module.fail_json(changed=False, msg="authtype must be provided for ssh script type") + request["authtype"] = str(zabbix_utils.helper_to_numeric_value([ + "password", + "public_key"], authtype)) + if authtype == "public_key": if publickey is None or privatekey is None: - self._module.fail_json(changed=False, msg='publickey and privatekey must be provided for ssh script type with publickey authtype') - request['publickey'] = publickey - request['privatekey'] = privatekey + self._module.fail_json(changed=False, msg="publickey and privatekey must be provided for ssh script type with publickey authtype") + request["publickey"] = publickey + request["privatekey"] = privatekey - if script_type in ['ssh', 'telnet']: + if script_type in ["ssh", "telnet"]: if username is None: - self._module.fail_json(changed=False, msg='username must be provided for "ssh" and "telnet" script types') - request['username'] = username - if (script_type == 'ssh' and authtype == 'password') or script_type == 'telnet': + self._module.fail_json(changed=False, msg="username must be provided for 'ssh' and 'telnet' script types") + request["username"] = username + if (script_type == "ssh" and authtype == "password") or script_type == "telnet": if password is None: - self._module.fail_json(changed=False, msg='password must be provided for telnet script type or ssh script type with password autheype') - request['password'] = password + self._module.fail_json(changed=False, msg="password must be provided for telnet script type or ssh script type with password autheype") + request["password"] = password if port is not None: - request['port'] = port + request["port"] = port - if script_type == 'webhook': - request['timeout'] = script_timeout + if script_type == "webhook": + request["timeout"] = script_timeout if parameters: - request['parameters'] = parameters - - if LooseVersion(self._zbx_api_version) < LooseVersion('5.4'): - if script_type not in ['script', 'ipmi']: - self._module.fail_json(changed=False, msg='script_type must be script or ipmi in version <5.4') - if 'scope' in request: - del request['scope'] - if 'menu_path' in request: - del request['menu_path'] + request["parameters"] = parameters return request @@ -338,7 +326,7 @@ class Script(ZabbixBase): generated_config = self.generate_script_config(name, script_type, command, scope, execute_on, menu_path, authtype, username, password, publickey, privatekey, port, host_group, user_group, host_access, confirmation, script_timeout, parameters, description) - live_config = self._zapi.script.get({'filter': {'name': name}})[0] + live_config = self._zapi.script.get({"filter": {"name": name}})[0] change_parameters = {} difference = zabbix_utils.helper_cleanup_data(zabbix_utils.helper_compare_dictionaries(generated_config, live_config, change_parameters)) @@ -348,7 +336,7 @@ class Script(ZabbixBase): if self._module.check_mode: self._module.exit_json(changed=True) - generated_config['scriptid'] = live_config['scriptid'] + generated_config["scriptid"] = live_config["scriptid"] self._zapi.script.update(generated_config) self._module.exit_json(changed=True, msg="Script %s updated" % name) @@ -356,55 +344,55 @@ class Script(ZabbixBase): def main(): argument_spec = zabbix_utils.zabbix_common_argument_spec() argument_spec.update(dict( - name=dict(type='str', required=True), + name=dict(type="str", required=True), script_type=dict( - type='str', + type="str", required=True, - choices=['script', 'ipmi', 'ssh', 'telnet', 'webhook']), - command=dict(type='str', required=True), + choices=["script", "ipmi", "ssh", "telnet", "webhook"]), + command=dict(type="str", required=True), scope=dict( - type='str', + type="str", required=False, - choices=['action_operation', 'manual_host_action', 'manual_event_action'], - default='action_operation'), + choices=["action_operation", "manual_host_action", "manual_event_action"], + default="action_operation"), execute_on=dict( - type='str', + type="str", required=False, - choices=['zabbix_agent', 'zabbix_server', 'zabbix_server_proxy'], - default='zabbix_server_proxy'), - menu_path=dict(type='str', required=False), + choices=["zabbix_agent", "zabbix_server", "zabbix_server_proxy"], + default="zabbix_server_proxy"), + menu_path=dict(type="str", required=False), authtype=dict( - type='str', + type="str", required=False, - choices=['password', 'public_key']), - username=dict(type='str', required=False), - password=dict(type='str', required=False, no_log=True), - publickey=dict(type='str', required=False), - privatekey=dict(type='str', required=False, no_log=True), - port=dict(type='str', required=False), - host_group=dict(type='str', required=False, default='all'), - user_group=dict(type='str', required=False, default='all'), + choices=["password", "public_key"]), + username=dict(type="str", required=False), + password=dict(type="str", required=False, no_log=True), + publickey=dict(type="str", required=False), + privatekey=dict(type="str", required=False, no_log=True), + port=dict(type="str", required=False), + host_group=dict(type="str", required=False, default="all"), + user_group=dict(type="str", required=False, default="all"), host_access=dict( - type='str', + type="str", required=False, - choices=['read', 'write'], - default='read'), - confirmation=dict(type='str', required=False), - script_timeout=dict(type='str', default='30s', required=False), + choices=["read", "write"], + default="read"), + confirmation=dict(type="str", required=False), + script_timeout=dict(type="str", default="30s", required=False), parameters=dict( - type='list', - elements='dict', + type="list", + elements="dict", options=dict( - name=dict(type='str', required=True), - value=dict(type='str', required=False, default='') + name=dict(type="str", required=True), + value=dict(type="str", required=False, default="") ) ), - description=dict(type='str', required=False), + description=dict(type="str", required=False), state=dict( - type='str', + type="str", required=False, - default='present', - choices=['present', 'absent']) + default="present", + choices=["present", "absent"]) )) module = AnsibleModule( @@ -412,32 +400,26 @@ def main(): supports_check_mode=True ) - zabbix_utils.require_creds_params(module) - - for p in ['server_url', 'login_user', 'login_password', 'timeout', 'validate_certs']: - if p in module.params and not module.params[p] is None: - module.warn('Option "%s" is deprecated with the move to httpapi connection and will be removed in the next release' % p) - - name = module.params['name'] - script_type = module.params['script_type'] - command = module.params['command'] - scope = module.params['scope'] - execute_on = module.params['execute_on'] - menu_path = module.params['menu_path'] - authtype = module.params['authtype'] - username = module.params['username'] - password = module.params['password'] - publickey = module.params['publickey'] - privatekey = module.params['privatekey'] - port = module.params['port'] - host_group = module.params['host_group'] - user_group = module.params['user_group'] - host_access = module.params['host_access'] - confirmation = module.params['confirmation'] - script_timeout = module.params['script_timeout'] - parameters = module.params['parameters'] - description = module.params['description'] - state = module.params['state'] + name = module.params["name"] + script_type = module.params["script_type"] + command = module.params["command"] + scope = module.params["scope"] + execute_on = module.params["execute_on"] + menu_path = module.params["menu_path"] + authtype = module.params["authtype"] + username = module.params["username"] + password = module.params["password"] + publickey = module.params["publickey"] + privatekey = module.params["privatekey"] + port = module.params["port"] + host_group = module.params["host_group"] + user_group = module.params["user_group"] + host_access = module.params["host_access"] + confirmation = module.params["confirmation"] + script_timeout = module.params["script_timeout"] + parameters = module.params["parameters"] + description = module.params["description"] + state = module.params["state"] script = Script(module) script_ids = script.get_script_ids(name) diff --git a/ansible_collections/community/zabbix/plugins/modules/zabbix_service.py b/ansible_collections/community/zabbix/plugins/modules/zabbix_service.py index 9468ee49a..8a7b9b605 100644 --- a/ansible_collections/community/zabbix/plugins/modules/zabbix_service.py +++ b/ansible_collections/community/zabbix/plugins/modules/zabbix_service.py @@ -8,7 +8,7 @@ from __future__ import absolute_import, division, print_function __metaclass__ = type -DOCUMENTATION = ''' +DOCUMENTATION = """ --- module: zabbix_service short_description: Create/update/delete Zabbix service @@ -18,61 +18,26 @@ author: - "Emmanuel Riviere (@emriver)" - "Evgeny Yurchenko (@BGmot)" requirements: - - "python >= 2.7" + - "python >= 3.9" options: name: description: - Name of Zabbix service required: true type: str - parent: - description: - - Name of Zabbix service parent - - With >= Zabbix 6.0 this field is removed from the API and is dropped silently by module. - required: false - type: str - sla: - description: - - Sla value (i.e 99.99), goodsla in Zabbix API - - With >= Zabbix 6.0 this field is removed from the API and is dropped silently by module. - required: false - type: float - calculate_sla: - description: - - If yes, calculate the SLA value for this service, showsla in Zabbix API - - With >= Zabbix 6.0 this field is removed from the API and is dropped silently by module. - required: false - default: false - type: bool algorithm: description: - - Algorithm used to calculate the sla with < Zabbix 6.0 - - ' - C(no), sla is not calculated' - - ' - C(one_child), problem if at least one child has a problem' - - ' - C(all_children), problem if all children have problems' - - Status calculation rule. Only applicable if child services exists with >= Zabbix 6.0 - - ' - C(status_to_ok), set status to OK with' - - ' - C(most_crit_if_all_children), most critical if all children have problems' - - ' - C(most_crit_of_child_serv), most critical of child services with' - required: false - type: str - choices: ["no", "one_child", "all_children", "status_to_ok", "most_crit_if_all_children", "most_crit_of_child_serv"] - default: one_child - trigger_name: - description: - - Name of trigger linked to the service. - - With >= Zabbix 6.0 this field is removed from the API and is dropped silently by module. - required: false - type: str - trigger_host: - description: - - Name of host linked to the service. - - With >= Zabbix 6.0 this field is removed from the API and is dropped silently by module. + - Status calculation rule. Only applicable if child services exists. + - " - C(status_to_ok), set status to OK with" + - " - C(most_crit_if_all_children), most critical if all children have problems" + - " - C(most_crit_of_child_serv), most critical of child services with" required: false type: str + choices: ["status_to_ok", "most_crit_if_all_children", "most_crit_of_child_serv"] + default: status_to_ok state: description: - - 'State: present - create/update service; absent - delete service.' + - "State: present - create/update service; absent - delete service." required: false choices: [present, absent] default: "present" @@ -85,20 +50,17 @@ options: weight: description: - Service weight. - - New field with >= Zabbix 6.0. required: false - default: '0' + default: "0" type: str description: description: - Description of the service. - - New field with >= Zabbix 6.0. required: false type: str tags: description: - Service tags to be created for the service. - - New field with >= Zabbix 6.0. required: false type: list elements: dict @@ -116,7 +78,6 @@ options: problem_tags: description: - Problem tags to be created for the service. - - New field with >= Zabbix 6.0. required: false type: list elements: dict @@ -131,7 +92,7 @@ options: - Mapping condition operator. - C(equals) - C(like) - choices: ['equals', 'like'] + choices: ["equals", "like"] required: false default: "equals" type: str @@ -144,21 +105,18 @@ options: parents: description: - Parent services to be linked to the service. - - New field with >= Zabbix 6.0. required: false type: list elements: str children: description: - Child services to be linked to the service. - - New field with >= Zabbix 6.0. required: false type: list elements: str propagation_rule: description: - Status propagation value. Must be set together with propagation_rule. - - New field with >= Zabbix 6.0. - C(as_is) propagate service status as is - without any changes - C(increase) increase the propagated status by a given propagation_value (by 1 to 5 severities) - C(decrease) decrease the propagated status by a given propagation_value (by 1 to 5 severities) @@ -171,30 +129,28 @@ options: propagation_value: description: - Status propagation value. Must be set together with propagation_rule. - - New field with >= Zabbix 6.0. - - 'Possible values when I(propagation_rule=as_is or ignore):' - - ' - C(not_classified)' - - 'Possible values when I(propagation_rule=increase or decrease):' - - ' - C(information)' - - ' - C(warning)' - - ' - C(average)' - - ' - C(high)' - - ' - C(disaster)' - - 'Possible values when I(propagation_rule=fixed):' - - ' - C(ok)' - - ' - C(not_classified)' - - ' - C(information)' - - ' - C(warning)' - - ' - C(average)' - - ' - C(high)' - - ' - C(disaster)' + - "Possible values when I(propagation_rule=as_is or ignore):" + - " - C(not_classified)" + - "Possible values when I(propagation_rule=increase or decrease):" + - " - C(information)" + - " - C(warning)" + - " - C(average)" + - " - C(high)" + - " - C(disaster)" + - "Possible values when I(propagation_rule=fixed):" + - " - C(ok)" + - " - C(not_classified)" + - " - C(information)" + - " - C(warning)" + - " - C(average)" + - " - C(high)" + - " - C(disaster)" - Required with C(propagation_rule) required: false type: str status_rules: description: - Status rules for the service. - - New field with >= Zabbix 6.0. required: false type: list elements: dict @@ -214,8 +170,8 @@ options: type: str limit_value: description: - - 'Limit value: N, N% or W' - - 'Possible values: 1-100000 for N and W, 1-100 for N%' + - "Limit value: N, N% or W" + - "Possible values: 1-100000 for N and W, 1-100 for N%" required: true type: int limit_status: @@ -245,43 +201,23 @@ options: extends_documentation_fragment: - community.zabbix.zabbix -''' +""" -EXAMPLES = ''' +EXAMPLES = """ --- # If you want to use Username and Password to be authenticated by Zabbix Server - name: Set credentials to access Zabbix Server API - set_fact: + ansible.builtin.set_fact: ansible_user: Admin ansible_httpapi_pass: zabbix # If you want to use API token to be authenticated by Zabbix Server # https://www.zabbix.com/documentation/current/en/manual/web_interface/frontend_sections/administration/general#api-tokens - name: Set API token - set_fact: + ansible.builtin.set_fact: ansible_zabbix_auth_key: 8ec0d52432c15c91fcafe9888500cf9a607f44091ab554dbee860f6b44fac895 -# Creates a new Zabbix service with Zabbix < 6.0 -- name: Manage services - # set task level variables - vars: - ansible_network_os: community.zabbix.zabbix - ansible_connection: httpapi - ansible_httpapi_port: 443 - ansible_httpapi_use_ssl: true - ansible_httpapi_validate_certs: false - ansible_zabbix_url_path: 'zabbixeu' # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu - ansible_host: zabbix-example-fqdn.org - community.zabbix.zabbix_service: - name: apache2 service - sla: 99.99 - calculate_sla: yes - algorithm: one_child - trigger_name: apache2 service status - trigger_host: webserver01 - state: present - -# Creates a new Zabbix service with Zabbix >= 6.0 +# Creates a new Zabbix service - name: Create Zabbix service monitoring Apache2 in DCs in Toronto area # set task level variables vars: @@ -290,10 +226,10 @@ EXAMPLES = ''' ansible_httpapi_port: 443 ansible_httpapi_use_ssl: true ansible_httpapi_validate_certs: false - ansible_zabbix_url_path: 'zabbixeu' # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu + ansible_zabbix_url_path: "zabbixeu" # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu ansible_host: zabbix-example-fqdn.org community.zabbix.zabbix_service: - name: 'apache2 service Toronto' + name: "apache2 service Toronto" description: Apache2 services in Toronto area sortorder: 0 propagation_rule: increase @@ -325,7 +261,7 @@ EXAMPLES = ''' ansible_httpapi_port: 443 ansible_httpapi_use_ssl: true ansible_httpapi_validate_certs: false - ansible_zabbix_url_path: 'zabbixeu' # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu + ansible_zabbix_url_path: "zabbixeu" # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu ansible_host: zabbix-example-fqdn.org community.zabbix.zabbix_service: name: apache2 service @@ -336,27 +272,26 @@ EXAMPLES = ''' - tag: area value: global children: - - 'apache2 service Toronto' -''' + - "apache2 service Toronto" +""" -RETURN = ''' +RETURN = """ --- -''' +""" from ansible.module_utils.basic import AnsibleModule from ansible_collections.community.zabbix.plugins.module_utils.base import ZabbixBase import ansible_collections.community.zabbix.plugins.module_utils.helpers as zabbix_utils -from ansible.module_utils.compat.version import LooseVersion class Service(ZabbixBase): def get_service_ids(self, service_name): service_ids = [] - services = self._zapi.service.get({'filter': {'name': service_name}}) + services = self._zapi.service.get({"filter": {"name": service_name}}) for service in services: - service_ids.append(service['serviceid']) + service_ids.append(service["serviceid"]) return service_ids def delete_service(self, service_ids): @@ -365,208 +300,160 @@ class Service(ZabbixBase): self._zapi.service.delete(service_ids) def dump_services(self, service_ids): - if LooseVersion(self._zbx_api_version) < LooseVersion('6.0'): - services = self._zapi.service.get({'output': 'extend', 'filter': {'serviceid': service_ids}, 'selectParent': '1'}) - else: - services = self._zapi.service.get({'output': 'extend', 'filter': {'serviceid': service_ids}, 'selectParents': 'extend', - 'selectTags': 'extend', 'selectProblemTags': 'extend', 'selectChildren': 'extend', - 'selectStatusRules': 'extend'}) + services = self._zapi.service.get({"output": "extend", "filter": {"serviceid": service_ids}, "selectParents": "extend", + "selectTags": "extend", "selectProblemTags": "extend", "selectChildren": "extend", + "selectStatusRules": "extend"}) return services - def generate_service_config(self, name, parent, sla, calculate_sla, trigger_name, trigger_host, sortorder, weight, + def generate_service_config(self, name, sortorder, weight, algorithm, description, tags, problem_tags, parents, children, propagation_rule, propagation_value, status_rules): - algorithms = {'no': '0', 'one_child': '1', 'all_children': '2', - 'status_to_ok': '0', 'most_crit_if_all_children': '1', 'most_crit_of_child_serv': '2'} + algorithms = {"status_to_ok": "0", "most_crit_if_all_children": "1", "most_crit_of_child_serv": "2"} algorithm = algorithms[algorithm] - if LooseVersion(self._zbx_api_version) < LooseVersion('6.0'): - if calculate_sla: - calculate_sla = 1 - else: - calculate_sla = 0 - else: - sla = 0 # Parameter does not exist in >= 6.0 but we needed for format() function constructing request - - # Zabbix api return when no trigger - trigger_id = 0 - if trigger_host and trigger_name: - # Retrieving the host to get the trigger - hosts = self._zapi.host.get({'filter': {'host': trigger_host}}) - if not hosts: - self._module.fail_json(msg="Target host %s not found" % trigger_host) - host_id = hosts[0]['hostid'] - - triggers = self._zapi.trigger.get({'filter': {'description': trigger_name}, 'hostids': [host_id]}) - if not triggers: - self._module.fail_json(msg="Trigger %s not found on host %s" % (trigger_name, trigger_host)) - trigger_id = triggers[0]['triggerid'] - request = { - 'name': name, - 'algorithm': algorithm, - 'showsla': str(calculate_sla), - 'sortorder': sortorder, - 'goodsla': format(sla, '.4f'), # Sla has 4 decimals - 'triggerid': str(trigger_id) + "name": name, + "algorithm": algorithm, + "sortorder": sortorder, + "description": description, + "weight": weight } - if LooseVersion(self._zbx_api_version) >= LooseVersion('6.0'): - request.pop('showsla') - request.pop('triggerid') - request.pop('goodsla') - request['description'] = description - request['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 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']] + 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: - 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.") + 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: - 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.") + 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: - request['propagation_value'] = pv_map[propagation_value] - else: - if parent: - parent_ids = self.get_service_ids(parent) - if not parent_ids: - self._module.fail_json(msg="Parent %s not found" % parent) - request['parentid'] = parent_ids[0] + 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.") + 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.") + else: + request["propagation_value"] = pv_map[propagation_value] + return request - def create_service(self, name, parent, sla, calculate_sla, trigger_name, trigger_host, sortorder, weight, algorithm, + 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) - self._zapi.service.create(self.generate_service_config(name, parent, sla, calculate_sla, trigger_name, trigger_host, sortorder, weight, + self._zapi.service.create(self.generate_service_config(name, sortorder, weight, algorithm, description, tags, problem_tags, parents, children, propagation_rule, propagation_value, status_rules)) - def update_service(self, service_id, name, parent, sla, calculate_sla, trigger_name, trigger_host, sortorder, weight, algorithm, + def update_service(self, service_id, name, sortorder, weight, algorithm, description, tags, problem_tags, parents, children, propagation_rule, propagation_value, status_rules): - generated_config = self.generate_service_config(name, parent, sla, calculate_sla, trigger_name, trigger_host, sortorder, weight, algorithm, + 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 LooseVersion(self._zbx_api_version) >= LooseVersion('6.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 - - else: - if 'goodsla' in live_config: - live_config['goodsla'] = format(float(live_config['goodsla']), '.4f') + 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 'parentid' in generated_config: - if 'serviceid' in live_config['parent']: - live_config['parentid'] = live_config['parent']['serviceid'] + 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)) @@ -576,7 +463,7 @@ class Service(ZabbixBase): if self._module.check_mode: self._module.exit_json(changed=True) - generated_config['serviceid'] = service_id + generated_config["serviceid"] = service_id self._zapi.service.update(generated_config) self._module.exit_json(changed=True, msg="Service %s updated" % name) @@ -584,81 +471,75 @@ class Service(ZabbixBase): def main(): argument_spec = zabbix_utils.zabbix_common_argument_spec() argument_spec.update(dict( - name=dict(type='str', required=True), - parent=dict(type='str', required=False), - sla=dict(type='float', required=False), - calculate_sla=dict(type='bool', required=False, default=False), - algorithm=dict(default='one_child', required=False, choices=['no', 'one_child', 'all_children', - 'status_to_ok', 'most_crit_if_all_children', 'most_crit_of_child_serv']), - trigger_name=dict(type='str', required=False), - trigger_host=dict(type='str', required=False), - sortorder=dict(type='str', required=True), - weight=dict(default='0', type='str', required=False), - state=dict(default="present", choices=['present', 'absent']), - description=dict(type='str', required=False), + name=dict(type="str", required=True), + algorithm=dict(default="status_to_ok", required=False, choices=["status_to_ok", "most_crit_if_all_children", "most_crit_of_child_serv"]), + sortorder=dict(type="str", required=True), + weight=dict(default="0", type="str", required=False), + state=dict(default="present", choices=["present", "absent"]), + description=dict(type="str", required=False), tags=dict( - type='list', + type="list", required=False, - elements='dict', + elements="dict", options=dict( tag=dict( - type='str', + type="str", required=True ), value=dict( - type='str', + type="str", required=False ) ) ), problem_tags=dict( - type='list', + type="list", required=False, - elements='dict', + elements="dict", options=dict( tag=dict( - type='str', + type="str", required=True ), operator=dict( - type='str', + type="str", required=False, choices=[ - 'equals', - 'like' + "equals", + "like" ], - default='equals' + default="equals" ), value=dict( - type='str', + type="str", required=False, - default='' + default="" ) ) ), - 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_value=dict(type='str', required=False), + 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_value=dict(type="str", required=False), status_rules=dict( - type='list', + type="list", required=False, - elements='dict', + elements="dict", options=dict( type=dict( - type='str', + type="str", required=True ), limit_value=dict( - type='int', + type="int", required=True ), limit_status=dict( - type='str', + type="str", required=True ), new_status=dict( - type='str', + type="str", required=True ) ) @@ -669,30 +550,19 @@ def main(): supports_check_mode=True ) - zabbix_utils.require_creds_params(module) - - for p in ['server_url', 'login_user', 'login_password', 'timeout', 'validate_certs']: - if p in module.params and not module.params[p] is None: - module.warn('Option "%s" is deprecated with the move to httpapi connection and will be removed in the next release' % p) - - name = module.params['name'] - parent = module.params['parent'] - sla = module.params['sla'] - calculate_sla = module.params['calculate_sla'] - algorithm = module.params['algorithm'] - trigger_name = module.params['trigger_name'] - trigger_host = module.params['trigger_host'] - sortorder = module.params['sortorder'] - weight = module.params['weight'] - state = module.params['state'] - description = module.params['description'] - tags = module.params['tags'] - problem_tags = module.params['problem_tags'] - parents = module.params['parents'] - children = module.params['children'] - propagation_rule = module.params['propagation_rule'] - propagation_value = module.params['propagation_value'] - status_rules = module.params['status_rules'] + name = module.params["name"] + algorithm = module.params["algorithm"] + sortorder = module.params["sortorder"] + weight = module.params["weight"] + state = module.params["state"] + description = module.params["description"] + tags = module.params["tags"] + problem_tags = module.params["problem_tags"] + parents = module.params["parents"] + children = module.params["children"] + propagation_rule = module.params["propagation_rule"] + propagation_value = module.params["propagation_value"] + status_rules = module.params["status_rules"] # Load service module service = Service(module) @@ -706,18 +576,16 @@ def main(): module.exit_json(changed=True, result="Successfully deleted service(s) %s" % name) elif state == "present": - if (trigger_name and not trigger_host) or (trigger_host and not trigger_name): - module.fail_json(msg="Specify either both trigger_host and trigger_name or none to create or update a service") # Does not exists going to create it if not service_ids: - service.create_service(name, parent, sla, calculate_sla, trigger_name, trigger_host, sortorder, weight, algorithm, description, + 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: - service.update_service(service_ids[0], name, parent, sla, calculate_sla, trigger_name, trigger_host, sortorder, weight, + service.update_service(service_ids[0], name, sortorder, weight, algorithm, description, tags, problem_tags, parents, children, propagation_rule, propagation_value, status_rules) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/ansible_collections/community/zabbix/plugins/modules/zabbix_settings.py b/ansible_collections/community/zabbix/plugins/modules/zabbix_settings.py new file mode 100644 index 000000000..de8ec6886 --- /dev/null +++ b/ansible_collections/community/zabbix/plugins/modules/zabbix_settings.py @@ -0,0 +1,1112 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# +# Copyright: (c) 2023, ONODERA Masaru <masaru-onodera@ieee.org> +# 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 + +__metaclass__ = type + + +DOCUMENTATION = """ +--- +module: zabbix_settings + +short_description: Update Zabbix global settings. + +description: + - This module allows you to update Zabbix global settings. + +author: + - ONODERA Masaru(@masa-orca) + +requirements: + - "python >= 3.9" + +version_added: 2.1.0 + +options: + default_lang: + description: + - Default language for users. + required: false + type: str + default_timezone: + description: + - Default time zone for users. + - Please set C(system) if you want to use system time zone. + required: false + type: str + default_theme: + description: + - Default theme for users. + required: false + type: str + choices: + - blue-theme + - dark-theme + - hc-light + - hc-dark + search_limit: + description: + - A number of search and filter results limit. + required: false + type: int + max_overview_table_size: + description: + - Max number of columns and rows in overview tables + required: false + type: int + max_in_table: + description: + - Max count of elements to show inside table cell + required: false + type: int + server_check_interval: + description: + - The Zabbix shows "Zabbix server is down" warning if C(true). + required: false + type: bool + work_period: + description: + - Working time setting. + - U(https://www.zabbix.com/documentation/current/en/manual/appendix/time_period) + required: false + type: str + show_technical_errors: + description: + - The Zabbix shows PHP and SQL technical errors to users who are non-Super admin or belong to user groups with debug mode not enabled if C(true). + required: false + type: bool + history_period: + description: + - Max period of displaying history data. + - Accepts seconds and time unit with suffix (e.g. 24h). + required: false + type: str + period_default: + description: + - Default period value for time filter. + - Accepts seconds and time unit with suffix (e.g. 1h). + required: false + type: str + max_period: + description: + - Max period for time filter. + - Accepts seconds and time unit with suffix (e.g. 1y). + required: false + type: str + severity_color_0: + description: + - A custom color for not classified severity. + - Please set hexadecimal color code (e.g. 00FF00). + required: false + type: str + severity_color_1: + description: + - A custom color for information severity. + - Please set hexadecimal color code (e.g. 00FF00). + required: false + type: str + severity_color_2: + description: + - A custom color for warning severity. + - Please set hexadecimal color code (e.g. 00FF00). + required: false + type: str + severity_color_3: + description: + - A custom color for average severity. + - Please set hexadecimal color code (e.g. 00FF00). + required: false + type: str + severity_color_4: + description: + - A custom color for high severity. + - Please set hexadecimal color code (e.g. 00FF00). + required: false + type: str + severity_color_5: + description: + - A custom color for disaster severity. + - Please set hexadecimal color code (e.g. 00FF00). + required: false + type: str + severity_name_0: + description: + - A custom name for not classified severity. + required: false + type: str + severity_name_1: + description: + - A custom name for information severity. + required: false + type: str + severity_name_2: + description: + - A custom name for warning severity. + required: false + type: str + severity_name_3: + description: + - A custom name for average severity. + required: false + type: str + severity_name_4: + description: + - A custom name for high severity. + required: false + type: str + severity_name_5: + description: + - A custom name for disaster severity. + required: false + type: str + custom_color: + description: + - Custom event color settings will be activated if C(true). + required: false + type: bool + ok_period: + description: + - A time of period for displaying OK triggers. + - Accepts seconds and time unit with suffix (e.g. 5m). + required: false + type: str + blink_period: + description: + - A time of period for blinking status changed triggers. + - Accepts seconds and time unit with suffix (e.g. 5m). + required: false + type: str + problem_unack_color: + description: + - A custom color for unacknowledged PROBLEM events. + - This setting will be activated if I(custom_color=true). + - Please set hexadecimal color code (e.g. 00FF00). + required: false + type: str + problem_ack_color: + description: + - A custom color for acknowledged PROBLEM events. + - This setting will be activated if I(custom_color=true). + - Please set hexadecimal color code (e.g. 00FF00). + required: false + type: str + ok_unack_color: + description: + - A custom color for unacknowledged RESOLVED events. + - This setting will be activated if I(custom_color=true). + - Please set hexadecimal color code (e.g. 00FF00). + required: false + type: str + ok_ack_color: + description: + - A custom color for acknowledged RESOLVED events. + - This setting will be activated if I(custom_color=true). + - Please set hexadecimal color code (e.g. 00FF00). + required: false + type: str + problem_unack_style: + description: + - Unacknowledged PROBLEM events blink if C(true). + required: false + type: bool + problem_ack_style: + description: + - Acknowledged PROBLEM events blink if C(true). + required: false + type: bool + ok_unack_style: + description: + - Unacknowledged RESOLVED events blink if C(true). + required: false + type: bool + ok_ack_style: + description: + - Acknowledged RESOLVED events blink if C(true). + required: false + type: bool + frontend_url: + description: + - A URL of frontend. + - This parameter is used for url parameter of settings API. + required: false + type: str + discovery_group: + description: + - A hostgroup which discovered hosts will belong to. + required: false + type: str + default_inventory_mode: + description: + - A default value for host inventory mode. + required: false + type: str + choices: + - disabled + - manual + - automatic + alert_usrgrp: + description: + - A name of user group which user belongs to receive an alerm message when database down. + required: false + type: str + snmptrap_logging: + description: + - Logging unmatched SNMP traps will be ebabled if C(true). + required: false + type: bool + login_attempts: + description: + - A number of login attempts you can try with non blocked. + required: false + type: int + login_block: + description: + - A time of interval to reset login attempts when the user is blocked. + - Accepts seconds and time unit with suffix (e.g. 5m). + required: false + type: str + validate_uri_schemes: + description: + - Validate URI schemes if C(true). + required: false + type: bool + uri_valid_schemes: + description: + - A list of valid URI scheme. + required: false + type: list + elements: str + x_frame_options: + description: + - A text of X-Frame-Options of HTTP header. + required: false + type: str + iframe_sandboxing_enabled: + description: + - The Zabbix uses iframe sandboxing if C(true). + required: false + type: bool + iframe_sandboxing_exceptions: + description: + - A text of iframe sandboxing exceptions. + required: false + type: str + connect_timeout: + description: + - A time of connection timeout with Zabbix server. + required: false + type: str + socket_timeout: + description: + - A time of network default timeout. + required: false + type: str + media_type_test_timeout: + description: + - A time of network timeout for media type test. + required: false + type: str + item_test_timeout: + description: + - A time of network timeout for item tests. + required: false + type: str + script_timeout: + description: + - A time of network timeout for script execution. + required: false + type: str + report_test_timeout: + description: + - A time of network timeout for scheduled report test. + required: false + type: str + auditlog_enabled: + description: + - Enable audit logging if C(true). + required: false + type: bool + geomaps_tile_provider: + description: + - A provider of Geomap tile. + - Please set C(another) if you want use non default provider + required: false + type: str + choices: + - OpenStreetMap.Mapnik + - OpenTopoMap + - Stamen.TonerLite + - Stamen.Terrain + - USGS.USTopo + - USGS.USImagery + - another + geomaps_tile_url: + description: + - A URL of geomap tile. + required: false + type: str + geomaps_max_zoom: + description: + - Max zoom level of geomap. + required: false + type: str + geomaps_attribution: + description: + - A text of Geomap attribution. + required: false + type: str + vault_provider: + description: + - A name of vault provider. + - This parameter is available since Zabbix 6.2. + required: false + type: str + choices: + - HashiCorp_Vault + - CyberArk_Vault + +notes: + - This module manages settings related with settings api except ha_failover_delay. + +extends_documentation_fragment: + - community.zabbix.zabbix +""" + +EXAMPLES = """ +# If you want to use Username and Password to be authenticated by Zabbix Server +- name: Set credentials to access Zabbix Server API + ansible.builtin.set_fact: + ansible_user: Admin + ansible_httpapi_pass: zabbix + +# If you want to use API token to be authenticated by Zabbix Server +# https://www.zabbix.com/documentation/current/en/manual/web_interface/frontend_sections/administration/general#api-tokens +- name: Set API token + ansible.builtin.set_fact: + ansible_zabbix_auth_key: 8ec0d52432c15c91fcafe9888500cf9a607f44091ab554dbee860f6b44fac895 + +- name: Update settings + # set task level variables as we change ansible_connection plugin here + vars: + ansible_network_os: community.zabbix.zabbix + ansible_connection: httpapi + ansible_httpapi_port: 443 + ansible_httpapi_use_ssl: true + ansible_httpapi_validate_certs: false + ansible_zabbix_url_path: "zabbixeu" # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu + ansible_host: zabbix-example-fqdn.org + community.zabbix.zabbix_settings: + alert_usrgrp: "0" + auditlog_enabled: false + blink_period: "10m" + connect_timeout: "30s" + custom_color: false + default_inventory_mode: automatic +""" + +RETURN = """ +msg: + description: The result of the operation + returned: success + type: str + sample: "Successfully update global settings" +""" + +import re + +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.community.zabbix.plugins.module_utils.base import ZabbixBase +from ansible.module_utils.compat.version import LooseVersion +import ansible_collections.community.zabbix.plugins.module_utils.helpers as zabbix_utils + + +class Settings(ZabbixBase): + # get setting setting + def get_settings(self): + try: + return self._zapi.settings.get({"output": "extend"}) + except Exception as e: + self._module.fail_json(msg="Failed to get settings: %s" % e) + + def _is_time(self, time): + pattern = re.compile(r"^(\d+)([smhdwMy]?)$") + search_result = pattern.search(time) + if search_result is None: + self._module.fail_json(msg="{0} is invalid value.".format(time)) + return True + + def _is_color(self, color): + pattern = re.compile(r"^[0-9a-fA-F]{6}$") + search_result = pattern.search(color) + if search_result is None: + self._module.fail_json(msg="{0} is invalid value.".format(color)) + return True + + def get_usrgrpid_from_name(self, usrgrp): + usrgrpids = self._zapi.usergroup.get({"filter": {"name": usrgrp}}) + if not usrgrpids or len(usrgrpids) > 1: + self._module.fail_json("User group '%s' cannot be found" % usrgrp) + return usrgrpids[0]["usrgrpid"] + + def get_groupid_from_name(self, hostgroup): + groupid = self._zapi.hostgroup.get({"filter": {"name": hostgroup}}) + if not groupid or len(groupid) > 1: + self._module.fail_json("Host group '%s' cannot be found" % hostgroup) + return groupid[0]["groupid"] + + def update_settings( + self, + current_settings, + default_lang, + default_timezone, + default_theme, + search_limit, + max_overview_table_size, + max_in_table, + server_check_interval, + work_period, + show_technical_errors, + history_period, + period_default, + max_period, + severity_color_0, + severity_color_1, + severity_color_2, + severity_color_3, + severity_color_4, + severity_color_5, + severity_name_0, + severity_name_1, + severity_name_2, + severity_name_3, + severity_name_4, + severity_name_5, + custom_color, + ok_period, + blink_period, + problem_unack_color, + problem_ack_color, + ok_unack_color, + ok_ack_color, + problem_unack_style, + problem_ack_style, + ok_unack_style, + ok_ack_style, + frontend_url, + discovery_group, + default_inventory_mode, + alert_usrgrp, + snmptrap_logging, + login_attempts, + login_block, + validate_uri_schemes, + uri_valid_schemes, + x_frame_options, + iframe_sandboxing_enabled, + iframe_sandboxing_exceptions, + connect_timeout, + socket_timeout, + media_type_test_timeout, + item_test_timeout, + script_timeout, + report_test_timeout, + auditlog_enabled, + geomaps_tile_provider, + geomaps_tile_url, + geomaps_max_zoom, + geomaps_attribution, + vault_provider, + ): + try: + params = {} + + if isinstance(default_lang, str): + if default_lang != current_settings["default_lang"]: + params["default_lang"] = default_lang + + if isinstance(default_timezone, str): + if default_timezone != current_settings["default_timezone"]: + params["default_timezone"] = default_timezone + + if isinstance(default_theme, str): + if default_theme != current_settings["default_theme"]: + params["default_theme"] = default_theme + + if isinstance(search_limit, int): + if str(search_limit) != current_settings["search_limit"]: + params["search_limit"] = str(search_limit) + + if isinstance(max_overview_table_size, int): + if ( + str(max_overview_table_size) + != current_settings["max_overview_table_size"] + ): + params["max_overview_table_size"] = str(max_overview_table_size) + + if isinstance(max_in_table, int): + if str(max_in_table) != current_settings["max_in_table"]: + params["max_in_table"] = str(max_in_table) + + if isinstance(server_check_interval, bool): + if server_check_interval: + if current_settings["server_check_interval"] != "10": + params["server_check_interval"] = "10" + else: + if current_settings["server_check_interval"] != "0": + params["server_check_interval"] = "0" + + if isinstance(work_period, str): + if work_period != current_settings["work_period"]: + params["work_period"] = work_period + + if isinstance(show_technical_errors, bool): + if show_technical_errors: + if current_settings["show_technical_errors"] != "1": + params["show_technical_errors"] = "1" + else: + if current_settings["show_technical_errors"] != "0": + params["show_technical_errors"] = "0" + + if isinstance(history_period, str): + if self._is_time(history_period): + if history_period != current_settings["history_period"]: + params["history_period"] = history_period + + if isinstance(period_default, str): + if self._is_time(period_default): + if period_default != current_settings["period_default"]: + params["period_default"] = period_default + + if isinstance(max_period, str): + if self._is_time(max_period): + if max_period != current_settings["max_period"]: + params["max_period"] = max_period + + if isinstance(severity_color_0, str): + if self._is_color(severity_color_0): + if severity_color_0 != current_settings["severity_color_0"]: + params["severity_color_0"] = severity_color_0 + + if isinstance(severity_color_1, str): + if self._is_color(severity_color_1): + if severity_color_1 != current_settings["severity_color_1"]: + params["severity_color_1"] = severity_color_1 + + if isinstance(severity_color_2, str): + if self._is_color(severity_color_2): + if severity_color_2 != current_settings["severity_color_2"]: + params["severity_color_2"] = severity_color_2 + + if isinstance(severity_color_3, str): + if self._is_color(severity_color_3): + if severity_color_3 != current_settings["severity_color_3"]: + params["severity_color_3"] = severity_color_3 + + if isinstance(severity_color_4, str): + if self._is_color(severity_color_4): + if severity_color_4 != current_settings["severity_color_4"]: + params["severity_color_4"] = severity_color_4 + + if isinstance(severity_color_5, str): + if self._is_color(severity_color_5): + if severity_color_5 != current_settings["severity_color_5"]: + params["severity_color_5"] = severity_color_5 + + if isinstance(severity_name_0, str): + if severity_name_0 != current_settings["severity_name_0"]: + params["severity_name_0"] = severity_name_0 + + if isinstance(severity_name_1, str): + if severity_name_1 != current_settings["severity_name_1"]: + params["severity_name_1"] = severity_name_1 + + if isinstance(severity_name_2, str): + if severity_name_2 != current_settings["severity_name_2"]: + params["severity_name_2"] = severity_name_2 + + if isinstance(severity_name_3, str): + if severity_name_3 != current_settings["severity_name_3"]: + params["severity_name_3"] = severity_name_3 + + if isinstance(severity_name_4, str): + if severity_name_4 != current_settings["severity_name_4"]: + params["severity_name_4"] = severity_name_4 + + if isinstance(severity_name_5, str): + if severity_name_5 != current_settings["severity_name_5"]: + params["severity_name_5"] = severity_name_5 + + if isinstance(custom_color, bool): + if custom_color: + if current_settings["custom_color"] != "1": + params["custom_color"] = "1" + else: + if current_settings["custom_color"] != "0": + params["custom_color"] = "0" + + if isinstance(ok_period, str): + if self._is_time(ok_period): + if ok_period != current_settings["ok_period"]: + params["ok_period"] = ok_period + + if isinstance(blink_period, str): + if self._is_time(blink_period): + if blink_period != current_settings["blink_period"]: + params["blink_period"] = blink_period + + if isinstance(problem_unack_color, str): + if self._is_color(problem_unack_color): + if problem_unack_color != current_settings["problem_unack_color"]: + params["problem_unack_color"] = problem_unack_color + + if isinstance(problem_ack_color, str): + if self._is_color(problem_ack_color): + if problem_ack_color != current_settings["problem_ack_color"]: + params["problem_ack_color"] = problem_ack_color + + if isinstance(ok_unack_color, str): + if self._is_color(ok_unack_color): + if ok_unack_color != current_settings["ok_unack_color"]: + params["ok_unack_color"] = ok_unack_color + + if isinstance(ok_ack_color, str): + if self._is_color(ok_ack_color): + if ok_ack_color != current_settings["ok_ack_color"]: + params["ok_ack_color"] = ok_ack_color + + if isinstance(problem_unack_style, bool): + if problem_unack_style: + if current_settings["problem_unack_style"] != "1": + params["problem_unack_style"] = "1" + else: + if current_settings["problem_unack_style"] != "0": + params["problem_unack_style"] = "0" + + if isinstance(problem_ack_style, bool): + if problem_ack_style: + if current_settings["problem_ack_style"] != "1": + params["problem_ack_style"] = "1" + else: + if current_settings["problem_ack_style"] != "0": + params["problem_ack_style"] = "0" + + if isinstance(ok_unack_style, bool): + if ok_unack_style: + if current_settings["ok_unack_style"] != "1": + params["ok_unack_style"] = "1" + else: + if current_settings["ok_unack_style"] != "0": + params["ok_unack_style"] = "0" + + if isinstance(ok_ack_style, bool): + if ok_ack_style: + if current_settings["ok_ack_style"] != "1": + params["ok_ack_style"] = "1" + else: + if current_settings["ok_ack_style"] != "0": + params["ok_ack_style"] = "0" + + if isinstance(frontend_url, str): + if frontend_url != current_settings["url"]: + params["url"] = frontend_url + + if isinstance(discovery_group, str): + discovery_groupid = self.get_groupid_from_name(discovery_group) + if current_settings["discovery_groupid"] != discovery_groupid: + params["discovery_groupid"] = discovery_groupid + + if isinstance(default_inventory_mode, str): + _default_inventory_mode = str( + zabbix_utils.helper_to_numeric_value( + ["disabled", "manual", "automatic"], default_inventory_mode + ) + - 1 + ) + if ( + _default_inventory_mode + != current_settings["default_inventory_mode"] + ): + params["default_inventory_mode"] = _default_inventory_mode + + if isinstance(alert_usrgrp, str): + if alert_usrgrp != "0": + alert_usrgrpid = self.get_usrgrpid_from_name(alert_usrgrp) + else: + alert_usrgrpid = alert_usrgrp + if current_settings["alert_usrgrpid"] != alert_usrgrpid: + params["alert_usrgrpid"] = alert_usrgrpid + + if isinstance(snmptrap_logging, bool): + if snmptrap_logging: + if current_settings["snmptrap_logging"] != "1": + params["snmptrap_logging"] = "1" + else: + if current_settings["snmptrap_logging"] != "0": + params["snmptrap_logging"] = "0" + + if isinstance(login_attempts, int): + if str(login_attempts) != current_settings["login_attempts"]: + params["login_attempts"] = str(login_attempts) + + if isinstance(login_block, str): + if self._is_time(login_block): + if login_block != current_settings["login_block"]: + params["login_block"] = login_block + + if isinstance(validate_uri_schemes, bool): + if validate_uri_schemes: + if current_settings["validate_uri_schemes"] != "1": + params["validate_uri_schemes"] = "1" + else: + if current_settings["validate_uri_schemes"] != "0": + params["validate_uri_schemes"] = "0" + + if isinstance(uri_valid_schemes, list): + current_uri_valid_schemes = current_settings["uri_valid_schemes"].split( + "," + ) + uri_valid_schemes.sort() + current_uri_valid_schemes.sort() + compare_result = [] + zabbix_utils.helper_compare_lists( + uri_valid_schemes, current_uri_valid_schemes, compare_result + ) + if len(compare_result) != 0: + params["uri_valid_schemes"] = ",".join(uri_valid_schemes) + + if isinstance(x_frame_options, str): + if x_frame_options != current_settings["x_frame_options"]: + params["x_frame_options"] = x_frame_options + + if isinstance(iframe_sandboxing_enabled, bool): + if iframe_sandboxing_enabled: + if current_settings["iframe_sandboxing_enabled"] != "1": + params["iframe_sandboxing_enabled"] = "1" + else: + if current_settings["iframe_sandboxing_enabled"] != "0": + params["iframe_sandboxing_enabled"] = "0" + + if isinstance(iframe_sandboxing_exceptions, str): + if ( + iframe_sandboxing_exceptions + != current_settings["iframe_sandboxing_exceptions"] + ): + params[ + "iframe_sandboxing_exceptions" + ] = iframe_sandboxing_exceptions + + if isinstance(connect_timeout, str): + if self._is_time(connect_timeout): + if connect_timeout != current_settings["connect_timeout"]: + params["connect_timeout"] = connect_timeout + + if isinstance(socket_timeout, str): + if self._is_time(socket_timeout): + if socket_timeout != current_settings["socket_timeout"]: + params["socket_timeout"] = socket_timeout + + if isinstance(media_type_test_timeout, str): + if self._is_time(media_type_test_timeout): + if ( + media_type_test_timeout + != current_settings["media_type_test_timeout"] + ): + params["media_type_test_timeout"] = media_type_test_timeout + + if isinstance(item_test_timeout, str): + if self._is_time(item_test_timeout): + if item_test_timeout != current_settings["item_test_timeout"]: + params["item_test_timeout"] = item_test_timeout + + if isinstance(script_timeout, str): + if self._is_time(script_timeout): + if script_timeout != current_settings["script_timeout"]: + params["script_timeout"] = script_timeout + + if isinstance(report_test_timeout, str): + if self._is_time(report_test_timeout): + if report_test_timeout != current_settings["report_test_timeout"]: + params["report_test_timeout"] = report_test_timeout + + if isinstance(auditlog_enabled, bool): + if auditlog_enabled: + if current_settings["auditlog_enabled"] != "1": + params["auditlog_enabled"] = "1" + else: + if current_settings["auditlog_enabled"] != "0": + params["auditlog_enabled"] = "0" + + if isinstance(geomaps_tile_provider, str): + _geomaps_tile_provider = geomaps_tile_provider + if geomaps_tile_provider == "another": + _geomaps_tile_provider = "" + if _geomaps_tile_provider != current_settings["geomaps_tile_provider"]: + params["geomaps_tile_provider"] = _geomaps_tile_provider + + if isinstance(geomaps_tile_url, str): + if geomaps_tile_url != current_settings["geomaps_tile_url"]: + params["geomaps_tile_url"] = geomaps_tile_url + + if isinstance(geomaps_max_zoom, int): + if str(geomaps_max_zoom) != current_settings["geomaps_max_zoom"]: + params["geomaps_max_zoom"] = str(geomaps_max_zoom) + + if isinstance(geomaps_attribution, str): + if geomaps_attribution != current_settings["geomaps_attribution"]: + params["geomaps_attribution"] = geomaps_attribution + + if LooseVersion("6.2") <= LooseVersion(self._zbx_api_version): + if isinstance(vault_provider, str): + _vault_provider = str( + zabbix_utils.helper_to_numeric_value( + ["HashiCorp_Vault", "CyberArk_Vault"], vault_provider + ) + ) + if _vault_provider != current_settings["vault_provider"]: + params["vault_provider"] = _vault_provider + + if params != {}: + if self._module.check_mode: + self._module.exit_json(changed=True) + self._zapi.settings.update(params) + self._module.exit_json( + changed=True, result="Successfully updated global settings" + ) + else: + if self._module.check_mode: + self._module.exit_json(changed=False) + self._module.exit_json( + changed=False, result="Settings are already up to date" + ) + except Exception as e: + self._module.fail_json(msg="Failed to update global settings: %s" % e) + + +def main(): + argument_spec = zabbix_utils.zabbix_common_argument_spec() + argument_spec.update( + dict( + default_lang=dict(type="str"), + default_timezone=dict(type="str"), + default_theme=dict( + type="str", choices=["blue-theme", "dark-theme", "hc-light", "hc-dark"] + ), + search_limit=dict(type="int"), + max_overview_table_size=dict(type="int"), + max_in_table=dict(type="int"), + server_check_interval=dict(type="bool"), + work_period=dict(type="str"), + show_technical_errors=dict(type="bool"), + history_period=dict(type="str"), + period_default=dict(type="str"), + max_period=dict(type="str"), + severity_color_0=dict(type="str"), + severity_color_1=dict(type="str"), + severity_color_2=dict(type="str"), + severity_color_3=dict(type="str"), + severity_color_4=dict(type="str"), + severity_color_5=dict(type="str"), + severity_name_0=dict(type="str"), + severity_name_1=dict(type="str"), + severity_name_2=dict(type="str"), + severity_name_3=dict(type="str"), + severity_name_4=dict(type="str"), + severity_name_5=dict(type="str"), + custom_color=dict(type="bool"), + ok_period=dict(type="str"), + blink_period=dict(type="str"), + problem_unack_color=dict(type="str"), + problem_ack_color=dict(type="str"), + ok_unack_color=dict(type="str"), + ok_ack_color=dict(type="str"), + problem_unack_style=dict(type="bool"), + problem_ack_style=dict(type="bool"), + ok_unack_style=dict(type="bool"), + ok_ack_style=dict(type="bool"), + frontend_url=dict(type="str"), + discovery_group=dict(type="str"), + default_inventory_mode=dict( + type="str", choices=["disabled", "manual", "automatic"] + ), + alert_usrgrp=dict(type="str"), + snmptrap_logging=dict(type="bool"), + login_attempts=dict(type="int"), + login_block=dict(type="str"), + validate_uri_schemes=dict(type="bool"), + uri_valid_schemes=dict(type="list", elements="str"), + x_frame_options=dict(type="str"), + iframe_sandboxing_enabled=dict(type="bool"), + iframe_sandboxing_exceptions=dict(type="str"), + connect_timeout=dict(type="str"), + socket_timeout=dict(type="str"), + media_type_test_timeout=dict(type="str"), + item_test_timeout=dict(type="str"), + script_timeout=dict(type="str"), + report_test_timeout=dict(type="str"), + auditlog_enabled=dict(type="bool"), + geomaps_tile_provider=dict( + type="str", + choices=[ + "OpenStreetMap.Mapnik", + "OpenTopoMap", + "Stamen.TonerLite", + "Stamen.Terrain", + "USGS.USTopo", + "USGS.USImagery", + "another", + ], + ), + geomaps_tile_url=dict(type="str"), + geomaps_max_zoom=dict(type="str"), + geomaps_attribution=dict(type="str"), + vault_provider=dict( + type="str", choices=["HashiCorp_Vault", "CyberArk_Vault"] + ), + ) + ) + module = AnsibleModule( + argument_spec=argument_spec, + required_if=[ + [ + "geomaps_tile_provider", + "another", + ["geomaps_tile_url", "geomaps_max_zoom", "geomaps_attribution"], + ], + ], + supports_check_mode=True, + ) + + default_lang = module.params["default_lang"] + default_timezone = module.params["default_timezone"] + default_theme = module.params["default_theme"] + search_limit = module.params["search_limit"] + max_overview_table_size = module.params["max_overview_table_size"] + max_in_table = module.params["max_in_table"] + server_check_interval = module.params["server_check_interval"] + work_period = module.params["work_period"] + show_technical_errors = module.params["show_technical_errors"] + history_period = module.params["history_period"] + period_default = module.params["period_default"] + max_period = module.params["max_period"] + severity_color_0 = module.params["severity_color_0"] + severity_color_1 = module.params["severity_color_1"] + severity_color_2 = module.params["severity_color_2"] + severity_color_3 = module.params["severity_color_3"] + severity_color_4 = module.params["severity_color_4"] + severity_color_5 = module.params["severity_color_5"] + severity_name_0 = module.params["severity_name_0"] + severity_name_1 = module.params["severity_name_1"] + severity_name_2 = module.params["severity_name_2"] + severity_name_3 = module.params["severity_name_3"] + severity_name_4 = module.params["severity_name_4"] + severity_name_5 = module.params["severity_name_5"] + custom_color = module.params["custom_color"] + ok_period = module.params["ok_period"] + blink_period = module.params["blink_period"] + problem_unack_color = module.params["problem_unack_color"] + problem_ack_color = module.params["problem_ack_color"] + ok_unack_color = module.params["ok_unack_color"] + ok_ack_color = module.params["ok_ack_color"] + problem_unack_style = module.params["problem_unack_style"] + problem_ack_style = module.params["problem_ack_style"] + ok_unack_style = module.params["ok_unack_style"] + ok_ack_style = module.params["ok_ack_style"] + frontend_url = module.params["frontend_url"] + discovery_group = module.params["discovery_group"] + default_inventory_mode = module.params["default_inventory_mode"] + alert_usrgrp = module.params["alert_usrgrp"] + snmptrap_logging = module.params["snmptrap_logging"] + login_attempts = module.params["login_attempts"] + login_block = module.params["login_block"] + validate_uri_schemes = module.params["validate_uri_schemes"] + uri_valid_schemes = module.params["uri_valid_schemes"] + x_frame_options = module.params["x_frame_options"] + iframe_sandboxing_enabled = module.params["iframe_sandboxing_enabled"] + iframe_sandboxing_exceptions = module.params["iframe_sandboxing_exceptions"] + connect_timeout = module.params["connect_timeout"] + socket_timeout = module.params["socket_timeout"] + media_type_test_timeout = module.params["media_type_test_timeout"] + item_test_timeout = module.params["item_test_timeout"] + script_timeout = module.params["script_timeout"] + report_test_timeout = module.params["report_test_timeout"] + auditlog_enabled = module.params["auditlog_enabled"] + geomaps_tile_provider = module.params["geomaps_tile_provider"] + geomaps_tile_url = module.params["geomaps_tile_url"] + geomaps_max_zoom = module.params["geomaps_max_zoom"] + geomaps_attribution = module.params["geomaps_attribution"] + vault_provider = module.params["vault_provider"] + + settings = Settings(module) + + current_settings = settings.get_settings() + settings.update_settings( + current_settings, + default_lang, + default_timezone, + default_theme, + search_limit, + max_overview_table_size, + max_in_table, + server_check_interval, + work_period, + show_technical_errors, + history_period, + period_default, + max_period, + severity_color_0, + severity_color_1, + severity_color_2, + severity_color_3, + severity_color_4, + severity_color_5, + severity_name_0, + severity_name_1, + severity_name_2, + severity_name_3, + severity_name_4, + severity_name_5, + custom_color, + ok_period, + blink_period, + problem_unack_color, + problem_ack_color, + ok_unack_color, + ok_ack_color, + problem_unack_style, + problem_ack_style, + ok_unack_style, + ok_ack_style, + frontend_url, + discovery_group, + default_inventory_mode, + alert_usrgrp, + snmptrap_logging, + login_attempts, + login_block, + validate_uri_schemes, + uri_valid_schemes, + x_frame_options, + iframe_sandboxing_enabled, + iframe_sandboxing_exceptions, + connect_timeout, + socket_timeout, + media_type_test_timeout, + item_test_timeout, + script_timeout, + report_test_timeout, + auditlog_enabled, + geomaps_tile_provider, + geomaps_tile_url, + geomaps_max_zoom, + geomaps_attribution, + vault_provider, + ) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/community/zabbix/plugins/modules/zabbix_template.py b/ansible_collections/community/zabbix/plugins/modules/zabbix_template.py index 7c4c098af..a773790aa 100644 --- a/ansible_collections/community/zabbix/plugins/modules/zabbix_template.py +++ b/ansible_collections/community/zabbix/plugins/modules/zabbix_template.py @@ -8,19 +8,19 @@ from __future__ import absolute_import, division, print_function __metaclass__ = type -DOCUMENTATION = r''' +DOCUMENTATION = r""" --- module: zabbix_template -short_description: Create/update/delete/dump Zabbix template +short_description: Create/update/delete Zabbix template description: - - This module allows you to create, modify, delete and dump Zabbix templates. + - This module allows you to create, modify and delete Zabbix templates. - Multiple templates can be created or modified at once if passing JSON or XML to module. author: - "sookido (@sookido)" - "Logan Vig (@logan2211)" - "Dusan Matejka (@D3DeFi)" requirements: - - "python >= 2.6" + - "python >= 3.9" options: template_name: description: @@ -93,7 +93,6 @@ options: tags: description: - List of tags to assign to the template. - - Works only with >= Zabbix 4.2. - Providing I(tags=[]) with I(force=yes) will clean all of the tags from the template. required: false type: list @@ -108,54 +107,34 @@ options: description: - Value of the template tag. type: str - default: '' - dump_format: - description: - - Format to use when dumping template with C(state=dump). - - This option is deprecated and will eventually be removed in 2.14. - required: false - choices: [json, xml] - default: "json" - type: str - omit_date: - description: - - Removes the date field for the exported/dumped template - - Requires C(state=dump) - required: false - type: bool - default: false + default: "" state: description: - Required state of the template. - On C(state=present) template will be created/imported or updated depending if it is already present. - - On C(state=dump) template content will get dumped into required format specified in I(dump_format). - On C(state=absent) template will be deleted. - - The C(state=dump) is deprecated and will be removed in 2.14. The M(community.zabbix.zabbix_template_info) module should be used instead. required: false - choices: [present, absent, dump] + choices: [present, absent] default: "present" type: str extends_documentation_fragment: - community.zabbix.zabbix +""" -notes: -- there where breaking changes in the Zabbix API with version 5.4 onwards (especially UUIDs) which may - require you to export the templates again (see version tag >= 5.4 in the resulting file/data). -''' -EXAMPLES = r''' +EXAMPLES = r""" --- # If you want to use Username and Password to be authenticated by Zabbix Server - name: Set credentials to access Zabbix Server API - set_fact: + ansible.builtin.set_fact: ansible_user: Admin ansible_httpapi_pass: zabbix # If you want to use API token to be authenticated by Zabbix Server # https://www.zabbix.com/documentation/current/en/manual/web_interface/frontend_sections/administration/general#api-tokens - name: Set API token - set_fact: + ansible.builtin.set_fact: ansible_zabbix_auth_key: 8ec0d52432c15c91fcafe9888500cf9a607f44091ab554dbee860f6b44fac895 - name: Create a new Zabbix template linked to groups, macros and templates @@ -166,7 +145,7 @@ EXAMPLES = r''' ansible_httpapi_port: 443 ansible_httpapi_use_ssl: true ansible_httpapi_validate_certs: false - ansible_zabbix_url_path: 'zabbixeu' # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu + ansible_zabbix_url_path: "zabbixeu" # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu ansible_host: zabbix-example-fqdn.org community.zabbix.zabbix_template: template_name: ExampleHost @@ -177,12 +156,12 @@ EXAMPLES = r''' - Example template1 - Example template2 macros: - - macro: '{$EXAMPLE_MACRO1}' + - macro: "{$EXAMPLE_MACRO1}" value: 30000 - - macro: '{$EXAMPLE_MACRO2}' + - macro: "{$EXAMPLE_MACRO2}" value: 3 - - macro: '{$EXAMPLE_MACRO3}' - value: 'Example' + - macro: "{$EXAMPLE_MACRO3}" + value: "Example" state: present - name: Unlink and clear templates from the existing Zabbix template @@ -193,7 +172,7 @@ EXAMPLES = r''' ansible_httpapi_port: 443 ansible_httpapi_use_ssl: true ansible_httpapi_validate_certs: false - ansible_zabbix_url_path: 'zabbixeu' # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu + ansible_zabbix_url_path: "zabbixeu" # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu ansible_host: zabbix-example-fqdn.org community.zabbix.zabbix_template: template_name: ExampleHost @@ -210,7 +189,7 @@ EXAMPLES = r''' ansible_httpapi_port: 443 ansible_httpapi_use_ssl: true ansible_httpapi_validate_certs: false - ansible_zabbix_url_path: 'zabbixeu' # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu + ansible_zabbix_url_path: "zabbixeu" # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu ansible_host: zabbix-example-fqdn.org community.zabbix.zabbix_template: template_json: "{{ lookup('file', 'zabbix_apache2.json') }}" @@ -224,7 +203,7 @@ EXAMPLES = r''' ansible_httpapi_port: 443 ansible_httpapi_use_ssl: true ansible_httpapi_validate_certs: false - ansible_zabbix_url_path: 'zabbixeu' # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu + ansible_zabbix_url_path: "zabbixeu" # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu ansible_host: zabbix-example-fqdn.org community.zabbix.zabbix_template: template_xml: "{{ lookup('file', 'zabbix_apache2.xml') }}" @@ -238,20 +217,18 @@ EXAMPLES = r''' ansible_httpapi_port: 443 ansible_httpapi_use_ssl: true ansible_httpapi_validate_certs: false - ansible_zabbix_url_path: 'zabbixeu' # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu + ansible_zabbix_url_path: "zabbixeu" # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu ansible_host: zabbix-example-fqdn.org community.zabbix.zabbix_template: template_json: zabbix_export: - version: '3.2' + version: "3.2" templates: - name: Template for Testing - description: 'Testing template import' + description: "Testing template import" template: Test Template groups: - name: Templates - applications: - - name: Test Application state: present - name: Configure macros on the existing Zabbix template @@ -262,13 +239,13 @@ EXAMPLES = r''' ansible_httpapi_port: 443 ansible_httpapi_use_ssl: true ansible_httpapi_validate_certs: false - ansible_zabbix_url_path: 'zabbixeu' # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu + ansible_zabbix_url_path: "zabbixeu" # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu ansible_host: zabbix-example-fqdn.org community.zabbix.zabbix_template: template_name: Template macros: - - macro: '{$TEST_MACRO}' - value: 'Example' + - macro: "{$TEST_MACRO}" + value: "Example" state: present - name: Add tags to the existing Zabbix template @@ -279,7 +256,7 @@ EXAMPLES = r''' ansible_httpapi_port: 443 ansible_httpapi_use_ssl: true ansible_httpapi_validate_certs: false - ansible_zabbix_url_path: 'zabbixeu' # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu + ansible_zabbix_url_path: "zabbixeu" # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu ansible_host: zabbix-example-fqdn.org community.zabbix.zabbix_template: template_name: Template @@ -296,110 +273,16 @@ EXAMPLES = r''' ansible_httpapi_port: 443 ansible_httpapi_use_ssl: true ansible_httpapi_validate_certs: false - ansible_zabbix_url_path: 'zabbixeu' # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu + ansible_zabbix_url_path: "zabbixeu" # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu ansible_host: zabbix-example-fqdn.org community.zabbix.zabbix_template: template_name: Template state: absent +""" -- name: Dump Zabbix template as JSON - # set task level variables as we change ansible_connection plugin here - vars: - ansible_network_os: community.zabbix.zabbix - ansible_connection: httpapi - ansible_httpapi_port: 443 - ansible_httpapi_use_ssl: true - ansible_httpapi_validate_certs: false - ansible_zabbix_url_path: 'zabbixeu' # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu - ansible_host: zabbix-example-fqdn.org - community.zabbix.zabbix_template: - template_name: Template - omit_date: yes - state: dump - register: template_dump - -- name: Dump Zabbix template as XML - # set task level variables as we change ansible_connection plugin here - vars: - ansible_network_os: community.zabbix.zabbix - ansible_connection: httpapi - ansible_httpapi_port: 443 - ansible_httpapi_use_ssl: true - ansible_httpapi_validate_certs: false - ansible_zabbix_url_path: 'zabbixeu' # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu - ansible_host: zabbix-example-fqdn.org - community.zabbix.zabbix_template: - template_name: Template - dump_format: xml - omit_date: false - state: dump - register: template_dump -''' - -RETURN = r''' +RETURN = r""" --- -template_json: - description: The JSON dump of the template - returned: when state is dump and omit_date is no - type: str - sample: { - "zabbix_export":{ - "date":"2017-11-29T16:37:24Z", - "templates":[{ - "templates":[], - "description":"", - "httptests":[], - "screens":[], - "applications":[], - "discovery_rules":[], - "groups":[{"name":"Templates"}], - "name":"Test Template", - "items":[], - "macros":[], - "template":"test" - }], - "version":"3.2", - "groups":[{ - "name":"Templates" - }] - } - } - -template_xml: - description: dump of the template in XML representation - returned: when state is dump, dump_format is xml and omit_date is yes - type: str - sample: |- - <?xml version="1.0" ?> - <zabbix_export> - <version>4.2</version> - <groups> - <group> - <name>Templates</name> - </group> - </groups> - <templates> - <template> - <template>test</template> - <name>Test Template</name> - <description/> - <groups> - <group> - <name>Templates</name> - </group> - </groups> - <applications/> - <items/> - <discovery_rules/> - <httptests/> - <macros/> - <templates/> - <screens/> - <tags/> - </template> - </templates> - </zabbix_export> -''' +""" import json @@ -422,12 +305,12 @@ class Template(ZabbixBase): def get_group_ids_by_group_names(self, group_names): group_ids = [] for group_name in group_names: - if LooseVersion(self._zbx_api_version) >= LooseVersion('6.2'): - group = self._zapi.templategroup.get({'output': ['groupid'], 'filter': {'name': group_name}}) + if LooseVersion(self._zbx_api_version) >= LooseVersion("6.2"): + group = self._zapi.templategroup.get({"output": ["groupid"], "filter": {"name": group_name}}) else: - group = self._zapi.hostgroup.get({'output': ['groupid'], 'filter': {'name': group_name}}) + group = self._zapi.hostgroup.get({"output": ["groupid"], "filter": {"name": group_name}}) if group: - group_ids.append({'groupid': group[0]['groupid']}) + group_ids.append({"groupid": group[0]["groupid"]}) else: self._module.fail_json(msg="Template group not found: %s" % group_name) return group_ids @@ -437,25 +320,25 @@ class Template(ZabbixBase): if template_list is None or len(template_list) == 0: return template_ids for template in template_list: - template_list = self._zapi.template.get({'output': 'extend', 'filter': {'host': template}}) + template_list = self._zapi.template.get({"output": "extend", "filter": {"host": template}}) if len(template_list) < 1: continue else: - template_id = template_list[0]['templateid'] - template_ids.append({'templateid': template_id}) + template_id = template_list[0]["templateid"] + template_ids.append({"templateid": template_id}) return template_ids def add_template(self, template_name, group_ids, link_template_ids, macros, tags): if self._module.check_mode: self._module.exit_json(changed=True) - new_template = {'host': template_name, 'groups': group_ids, 'templates': link_template_ids, 'macros': macros, 'tags': tags} + new_template = {"host": template_name, "groups": group_ids, "templates": link_template_ids, "macros": macros, "tags": tags} if macros is None: - new_template.update({'macros': []}) + new_template.update({"macros": []}) if tags is None: - new_template.update({'tags': []}) + new_template.update({"tags": []}) if link_template_ids is None: - new_template.update({'templates': []}) + new_template.update({"templates": []}) self._zapi.template.create(new_template) @@ -470,17 +353,17 @@ class Template(ZabbixBase): changed = False # Compare filtered and formatted XMLs strings for any changes. It is expected that provided # XML has same structure as Zabbix uses (e.g. it was optimally exported via Zabbix GUI or API) - if template_content is not None and template_type == 'xml': - existing_template = self.dump_template(template_ids, template_type='xml') + if template_content is not None and template_type == "xml": + existing_template = self.dump_template(template_ids, template_type="xml") if self.filter_xml_template(template_content) != self.filter_xml_template(existing_template): changed = True return changed - existing_template = self.dump_template(template_ids, template_type='json') + existing_template = self.dump_template(template_ids, template_type="json") # Compare JSON objects as deep sorted python dictionaries - if template_content is not None and template_type == 'json': + if template_content is not None and template_type == "json": parsed_template_json = self.load_json_template(template_content) if self.diff_template(parsed_template_json, existing_template): changed = True @@ -489,19 +372,19 @@ class Template(ZabbixBase): # If neither template_json or template_xml were used, user provided all parameters via module options if template_groups is not None: - if LooseVersion(self._zbx_api_version) >= LooseVersion('6.2'): - existing_groups = [g['name'] for g in existing_template['zabbix_export']['template_groups']] + if LooseVersion(self._zbx_api_version) >= LooseVersion("6.2"): + existing_groups = [g["name"] for g in existing_template["zabbix_export"]["template_groups"]] else: - existing_groups = [g['name'] for g in existing_template['zabbix_export']['groups']] + existing_groups = [g["name"] for g in existing_template["zabbix_export"]["groups"]] if set(template_groups) != set(existing_groups): changed = True - if 'templates' not in existing_template['zabbix_export']['templates'][0]: - existing_template['zabbix_export']['templates'][0]['templates'] = [] + if "templates" not in existing_template["zabbix_export"]["templates"][0]: + existing_template["zabbix_export"]["templates"][0]["templates"] = [] # Check if any new templates would be linked or any existing would be unlinked - exist_child_templates = [t['name'] for t in existing_template['zabbix_export']['templates'][0]['templates']] + exist_child_templates = [t["name"] for t in existing_template["zabbix_export"]["templates"][0]["templates"]] if link_templates is not None: if set(link_templates) != set(exist_child_templates): changed = True @@ -516,46 +399,45 @@ class Template(ZabbixBase): changed = True break - if 'macros' not in existing_template['zabbix_export']['templates'][0]: - existing_template['zabbix_export']['templates'][0]['macros'] = [] + if "macros" not in existing_template["zabbix_export"]["templates"][0]: + existing_template["zabbix_export"]["templates"][0]["macros"] = [] if template_macros is not None: - existing_macros = existing_template['zabbix_export']['templates'][0]['macros'] + existing_macros = existing_template["zabbix_export"]["templates"][0]["macros"] if template_macros != existing_macros: changed = True - if LooseVersion(self._zbx_api_version) >= LooseVersion('4.2'): - if 'tags' not in existing_template['zabbix_export']['templates'][0]: - existing_template['zabbix_export']['templates'][0]['tags'] = [] - if template_tags is not None: - existing_tags = existing_template['zabbix_export']['templates'][0]['tags'] - if template_tags != existing_tags: - changed = True + if "tags" not in existing_template["zabbix_export"]["templates"][0]: + existing_template["zabbix_export"]["templates"][0]["tags"] = [] + if template_tags is not None: + existing_tags = existing_template["zabbix_export"]["templates"][0]["tags"] + if template_tags != existing_tags: + changed = True return changed def update_template(self, template_ids, group_ids, link_template_ids, clear_template_ids, template_macros, template_tags): template_changes = {} if group_ids is not None: - template_changes.update({'groups': group_ids}) + template_changes.update({"groups": group_ids}) if link_template_ids is not None: - template_changes.update({'templates': link_template_ids}) + template_changes.update({"templates": link_template_ids}) else: - template_changes.update({'templates': []}) + template_changes.update({"templates": []}) if clear_template_ids is not None: - template_changes.update({'templates_clear': clear_template_ids}) + template_changes.update({"templates_clear": clear_template_ids}) if template_macros is not None: - template_changes.update({'macros': template_macros}) + template_changes.update({"macros": template_macros}) else: - template_changes.update({'macros': []}) + template_changes.update({"macros": []}) if template_tags is not None: - template_changes.update({'tags': template_tags}) + template_changes.update({"tags": template_tags}) else: - template_changes.update({'tags': []}) + template_changes.update({"tags": []}) if template_changes: # If we got here we know that only one template was provided via template_name @@ -566,7 +448,7 @@ class Template(ZabbixBase): if self._module.check_mode: self._module.exit_json(changed=True) - templateids_list = [t.get('templateid') for t in templateids] + templateids_list = [t.get("templateid") for t in templateids] self._zapi.template.delete(templateids_list) def ordered_json(self, obj): @@ -578,26 +460,21 @@ class Template(ZabbixBase): else: return obj - def dump_template(self, template_ids, template_type='json', omit_date=False): - template_ids_list = [t.get('templateid') for t in template_ids] + def dump_template(self, template_ids, template_type="json"): + template_ids_list = [t.get("templateid") for t in template_ids] try: - dump = self._zapi.configuration.export({'format': template_type, 'options': {'templates': template_ids_list}}) - if template_type == 'xml': - xmlroot = ET.fromstring(dump.encode('utf-8')) - # remove date field if requested - if omit_date: - date = xmlroot.find(".date") - if date is not None: - xmlroot.remove(date) + dump = self._zapi.configuration.export({"format": template_type, "options": {"templates": template_ids_list}}) + if template_type == "xml": + xmlroot = ET.fromstring(dump.encode("utf-8")) if PY2: - return str(ET.tostring(xmlroot, encoding='utf-8')) + return str(ET.tostring(xmlroot, encoding="utf-8")) else: - return str(ET.tostring(xmlroot, encoding='utf-8').decode('utf-8')) + return str(ET.tostring(xmlroot, encoding="utf-8").decode("utf-8")) else: - return self.load_json_template(dump, omit_date=omit_date) + return self.load_json_template(dump) except Exception as e: - self._module.fail_json(msg='Unable to export template: %s' % e) + self._module.fail_json(msg="Unable to export template: %s" % e) def diff_template(self, template_json_a, template_json_b): # Compare 2 zabbix templates and return True if they differ. @@ -609,20 +486,18 @@ class Template(ZabbixBase): def filter_template(self, template_json): # Filter the template json to contain only the keys we will update - keep_keys = set(['graphs', 'templates', 'triggers', 'value_maps']) - unwanted_keys = set(template_json['zabbix_export']) - keep_keys + keep_keys = set(["graphs", "templates", "triggers", "value_maps"]) + unwanted_keys = set(template_json["zabbix_export"]) - keep_keys for unwanted_key in unwanted_keys: - del template_json['zabbix_export'][unwanted_key] + del template_json["zabbix_export"][unwanted_key] # Versions older than 2.4 do not support description field within template desc_not_supported = False - if LooseVersion(self._zbx_api_version) < LooseVersion('2.4'): - desc_not_supported = True # Filter empty attributes from template object to allow accurate comparison - for template in template_json['zabbix_export']['templates']: + for template in template_json["zabbix_export"]["templates"]: for key in list(template.keys()): - if not template[key] or (key == 'description' and desc_not_supported): + if not template[key] or (key == "description" and desc_not_supported): template.pop(key) return template_json @@ -634,7 +509,7 @@ class Template(ZabbixBase): It is advised that provided XML template exactly matches XML structure used by Zabbix""" # Strip last new line and convert string to ElementTree parsed_xml_root = self.load_xml_template(template_xml.strip()) - keep_keys = ['graphs', 'templates', 'triggers', 'value_maps'] + keep_keys = ["graphs", "templates", "triggers", "value_maps"] # Remove unwanted XML nodes for node in list(parsed_xml_root): @@ -642,209 +517,174 @@ class Template(ZabbixBase): parsed_xml_root.remove(node) # Filter empty attributes from template objects to allow accurate comparison - for template in list(parsed_xml_root.find('templates')): + for template in list(parsed_xml_root.find("templates")): for element in list(template): if element.text is None and len(list(element)) == 0: template.remove(element) # Filter new lines and indentation - xml_root_text = list(line.strip() for line in ET.tostring(parsed_xml_root, encoding='utf8', method='xml').decode().split('\n')) - return ''.join(xml_root_text) + xml_root_text = list(line.strip() for line in ET.tostring(parsed_xml_root, encoding="utf8", method="xml").decode().split("\n")) + return "".join(xml_root_text) - def load_json_template(self, template_json, omit_date=False): + def load_json_template(self, template_json): try: jsondoc = json.loads(template_json) - if omit_date and 'date' in jsondoc['zabbix_export']: - del jsondoc['zabbix_export']['date'] return jsondoc except ValueError as e: - self._module.fail_json(msg='Invalid JSON provided', details=to_native(e), exception=traceback.format_exc()) + self._module.fail_json(msg="Invalid JSON provided", details=to_native(e), exception=traceback.format_exc()) def load_xml_template(self, template_xml): try: return ET.fromstring(template_xml) except ET.ParseError as e: - self._module.fail_json(msg='Invalid XML provided', details=to_native(e), exception=traceback.format_exc()) + self._module.fail_json(msg="Invalid XML provided", details=to_native(e), exception=traceback.format_exc()) - def import_template(self, template_content, template_type='json'): + def import_template(self, template_content, template_type="json"): if self._module.check_mode: self._module.exit_json(changed=True) # rules schema latest version update_rules = { - 'applications': { - 'createMissing': True, - 'deleteMissing': True + "discoveryRules": { + "createMissing": True, + "updateExisting": True, + "deleteMissing": True }, - 'discoveryRules': { - 'createMissing': True, - 'updateExisting': True, - 'deleteMissing': True + "graphs": { + "createMissing": True, + "updateExisting": True, + "deleteMissing": True }, - 'graphs': { - 'createMissing': True, - 'updateExisting': True, - 'deleteMissing': True + "host_groups": { + "createMissing": True }, - 'host_groups': { - 'createMissing': True + "httptests": { + "createMissing": True, + "updateExisting": True, + "deleteMissing": True }, - 'httptests': { - 'createMissing': True, - 'updateExisting': True, - 'deleteMissing': True + "items": { + "createMissing": True, + "updateExisting": True, + "deleteMissing": True }, - 'items': { - 'createMissing': True, - 'updateExisting': True, - 'deleteMissing': True + "templates": { + "createMissing": True, + "updateExisting": True }, - 'templates': { - 'createMissing': True, - 'updateExisting': True + "template_groups": { + "createMissing": True }, - 'template_groups': { - 'createMissing': True + "templateLinkage": { + "createMissing": True }, - 'templateLinkage': { - 'createMissing': True + "templateScreens": { + "createMissing": True, + "updateExisting": True, + "deleteMissing": True }, - 'templateScreens': { - 'createMissing': True, - 'updateExisting': True, - 'deleteMissing': True + "triggers": { + "createMissing": True, + "updateExisting": True, + "deleteMissing": True }, - 'triggers': { - 'createMissing': True, - 'updateExisting': True, - 'deleteMissing': True - }, - 'valueMaps': { - 'createMissing': True, - 'updateExisting': True + "valueMaps": { + "createMissing": True, + "updateExisting": True } } try: - # updateExisting for application removed from zabbix api after 3.2 - if LooseVersion(self._zbx_api_version) <= LooseVersion('3.2'): - update_rules['applications']['updateExisting'] = True - - # templateLinkage.deleteMissing only available in 4.0 branch higher .16 and higher 4.4.4 - # it's not available in 4.2 branches or lower 4.0.16 - if LooseVersion(self._zbx_api_version).version[:2] == LooseVersion('4.0').version and \ - LooseVersion(self._zbx_api_version).version[:3] >= LooseVersion('4.0.16').version: - update_rules['templateLinkage']['deleteMissing'] = True - if LooseVersion(self._zbx_api_version) >= LooseVersion('4.4.4'): - update_rules['templateLinkage']['deleteMissing'] = True - - # templateScreens is named templateDashboards in zabbix >= 5.2 - # https://support.zabbix.com/browse/ZBX-18677 - if LooseVersion(self._zbx_api_version) >= LooseVersion('5.2'): - update_rules["templateDashboards"] = update_rules.pop("templateScreens") - - # Zabbix 5.4 no longer supports applications - if LooseVersion(self._zbx_api_version) >= LooseVersion('5.4'): - update_rules.pop('applications', None) + update_rules["templateLinkage"]["deleteMissing"] = True + update_rules["templateDashboards"] = update_rules.pop("templateScreens") # before Zabbix 6.2 host_groups and template_group are joined into groups parameter - if LooseVersion(self._zbx_api_version) < LooseVersion('6.2'): - update_rules['groups'] = {'createMissing': True} - update_rules.pop('host_groups', None) - update_rules.pop('template_groups', None) + if LooseVersion(self._zbx_api_version) < LooseVersion("6.2"): + update_rules["groups"] = {"createMissing": True} + update_rules.pop("host_groups", None) + update_rules.pop("template_groups", None) # The loaded unicode slash of multibyte as a string is escaped when parsing JSON by json.loads in Python2. # So, it is imported in the unicode string into Zabbix. # The following processing is removing the unnecessary slash in escaped for decoding correctly to the multibyte string. # https://github.com/ansible-collections/community.zabbix/issues/314 if PY2: - template_content = re.sub(r'\\\\u([0-9a-z]{,4})', r'\\u\1', template_content) + template_content = re.sub(r"\\\\u([0-9a-z]{,4})", r"\\u\1", template_content) - import_data = {'format': template_type, 'source': template_content, 'rules': update_rules} + import_data = {"format": template_type, "source": template_content, "rules": update_rules} self._zapi.configuration.import_(import_data) except Exception as e: - self._module.fail_json(msg='Unable to import template', details=to_native(e), + self._module.fail_json(msg="Unable to import template", details=to_native(e), exception=traceback.format_exc()) def main(): argument_spec = zabbix_utils.zabbix_common_argument_spec() argument_spec.update(dict( - template_name=dict(type='str', required=False), - template_json=dict(type='json', required=False), - template_xml=dict(type='str', required=False), - template_groups=dict(type='list', required=False), - link_templates=dict(type='list', required=False), - clear_templates=dict(type='list', required=False), + template_name=dict(type="str", required=False), + template_json=dict(type="json", required=False), + template_xml=dict(type="str", required=False), + template_groups=dict(type="list", required=False, elements="str"), + link_templates=dict(type="list", required=False, elements="str"), + clear_templates=dict(type="list", required=False, elements="str"), macros=dict( - type='list', - elements='dict', + type="list", + elements="dict", options=dict( - macro=dict(type='str', required=True), - value=dict(type='str', required=True) + macro=dict(type="str", required=True), + value=dict(type="str", required=True) ) ), tags=dict( - type='list', - elements='dict', + type="list", + elements="dict", options=dict( - tag=dict(type='str', required=True), - value=dict(type='str', default='') + tag=dict(type="str", required=True), + value=dict(type="str", default="") ) ), - omit_date=dict(type='bool', required=False, default=False), - dump_format=dict(type='str', required=False, default='json', choices=['json', 'xml']), - state=dict(type='str', default="present", choices=['present', 'absent', 'dump']), + state=dict(type="str", default="present", choices=["present", "absent"]), )) module = AnsibleModule( argument_spec=argument_spec, required_one_of=[ - ['template_name', 'template_json', 'template_xml'] + ["template_name", "template_json", "template_xml"] ], mutually_exclusive=[ - ['template_name', 'template_json', 'template_xml'] + ["template_name", "template_json", "template_xml"] ], required_if=[ - ['state', 'absent', ['template_name']], - ['state', 'dump', ['template_name']] + ["state", "absent", ["template_name"]] ], supports_check_mode=True ) - zabbix_utils.require_creds_params(module) - - for p in ['server_url', 'login_user', 'login_password', 'timeout', 'validate_certs']: - if p in module.params and not module.params[p] is None: - module.warn('Option "%s" is deprecated with the move to httpapi connection and will be removed in the next release' % p) - - template_name = module.params['template_name'] - template_json = module.params['template_json'] - template_xml = module.params['template_xml'] - template_groups = module.params['template_groups'] - link_templates = module.params['link_templates'] - clear_templates = module.params['clear_templates'] - template_macros = module.params['macros'] - template_tags = module.params['tags'] - omit_date = module.params['omit_date'] - dump_format = module.params['dump_format'] - state = module.params['state'] + template_name = module.params["template_name"] + template_json = module.params["template_json"] + template_xml = module.params["template_xml"] + template_groups = module.params["template_groups"] + link_templates = module.params["link_templates"] + clear_templates = module.params["clear_templates"] + template_macros = module.params["macros"] + template_tags = module.params["tags"] + state = module.params["state"] template = Template(module) # Identify template names for IDs retrieval - # Template names are expected to reside in ['zabbix_export']['templates'][*]['template'] for both data types + # Template names are expected to reside in ["zabbix_export"]["templates"][*]["template"] for both data types template_content, template_type = None, None if template_json is not None: - template_type = 'json' + template_type = "json" template_content = template_json json_parsed = template.load_json_template(template_content) - template_names = list(t['template'] for t in json_parsed['zabbix_export']['templates']) + template_names = list(t["template"] for t in json_parsed["zabbix_export"]["templates"]) elif template_xml is not None: - template_type = 'xml' + template_type = "xml" template_content = template_xml xml_parsed = template.load_xml_template(template_content) - template_names = list(t.find('template').text for t in list(xml_parsed.find('templates'))) + template_names = list(t.find("template").text for t in list(xml_parsed.find("templates"))) else: template_names = [template_name] @@ -858,17 +698,6 @@ def main(): template.delete_template(template_ids) module.exit_json(changed=True, result="Successfully deleted template %s" % template_name) - elif state == "dump": - module.deprecate("The 'dump' state has been deprecated and will be removed, use 'zabbix_template_info' module instead.", - collection_name="community.zabbix", version='3.0.0') # was 2.14 - if not template_ids: - module.fail_json(msg='Template not found: %s' % template_name) - - if dump_format == 'json': - module.exit_json(changed=False, template_json=template.dump_template(template_ids, template_type='json', omit_date=omit_date)) - elif dump_format == 'xml': - module.exit_json(changed=False, template_xml=template.dump_template(template_ids, template_type='xml', omit_date=omit_date)) - elif state == "present": # Load all subelements for template that were provided by user group_ids = None @@ -895,14 +724,14 @@ def main(): tagitem[key] = str(tagitem[key]) if not template_ids: - # Assume new templates are being added when no ID's were found + # Assume new templates are being added when no ID"s were found if template_content is not None: template.import_template(template_content, template_type) module.exit_json(changed=True, result="Template import successful") else: if group_ids is None: - module.fail_json(msg='template_groups are required when creating a new Zabbix template') + module.fail_json(msg="template_groups are required when creating a new Zabbix template") template.add_template(template_name, group_ids, link_template_ids, template_macros, template_tags) module.exit_json(changed=True, result="Successfully added template: %s" % template_name) @@ -924,5 +753,5 @@ def main(): module.exit_json(changed=changed, result="Template successfully updated") -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/ansible_collections/community/zabbix/plugins/modules/zabbix_template_info.py b/ansible_collections/community/zabbix/plugins/modules/zabbix_template_info.py index acd5b5055..140233e1a 100644 --- a/ansible_collections/community/zabbix/plugins/modules/zabbix_template_info.py +++ b/ansible_collections/community/zabbix/plugins/modules/zabbix_template_info.py @@ -8,7 +8,7 @@ from __future__ import absolute_import, division, print_function __metaclass__ = type -DOCUMENTATION = ''' +DOCUMENTATION = """ module: zabbix_template_info short_description: Gather information about Zabbix template author: @@ -16,7 +16,7 @@ author: description: - This module allows you to search for Zabbix template. requirements: - - "python >= 2.6" + - "python >= 3.9" options: template_name: description: @@ -26,8 +26,7 @@ options: format: description: - Format to use when dumping template. - - C(yaml) works only with Zabbix >= 5.2. - choices: ['json', 'xml', 'yaml', 'none'] + choices: ["json", "xml", "yaml", "none"] default: json type: str omit_date: @@ -38,23 +37,19 @@ options: default: false extends_documentation_fragment: - community.zabbix.zabbix +""" -notes: -- there where breaking changes in the Zabbix API with version 5.4 onwards (especially UUIDs) which may - require you to export the templates again (see version tag >= 5.4 in the resulting file/data). -''' - -EXAMPLES = ''' +EXAMPLES = """ # If you want to use Username and Password to be authenticated by Zabbix Server - name: Set credentials to access Zabbix Server API - set_fact: + ansible.builtin.set_fact: ansible_user: Admin ansible_httpapi_pass: zabbix # If you want to use API token to be authenticated by Zabbix Server # https://www.zabbix.com/documentation/current/en/manual/web_interface/frontend_sections/administration/general#api-tokens - name: Set API token - set_fact: + ansible.builtin.set_fact: ansible_zabbix_auth_key: 8ec0d52432c15c91fcafe9888500cf9a607f44091ab554dbee860f6b44fac895 - name: Get Zabbix template as JSON @@ -65,7 +60,7 @@ EXAMPLES = ''' ansible_httpapi_port: 443 ansible_httpapi_use_ssl: true ansible_httpapi_validate_certs: false - ansible_zabbix_url_path: 'zabbixeu' # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu + ansible_zabbix_url_path: "zabbixeu" # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu ansible_host: zabbix-example-fqdn.org community.zabbix.zabbix_template_info: template_name: Template @@ -81,7 +76,7 @@ EXAMPLES = ''' ansible_httpapi_port: 443 ansible_httpapi_use_ssl: true ansible_httpapi_validate_certs: false - ansible_zabbix_url_path: 'zabbixeu' # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu + ansible_zabbix_url_path: "zabbixeu" # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu ansible_host: zabbix-example-fqdn.org community.zabbix.zabbix_template_info: template_name: Template @@ -97,7 +92,7 @@ EXAMPLES = ''' ansible_httpapi_port: 443 ansible_httpapi_use_ssl: true ansible_httpapi_validate_certs: false - ansible_zabbix_url_path: 'zabbixeu' # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu + ansible_zabbix_url_path: "zabbixeu" # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu ansible_host: zabbix-example-fqdn.org community.zabbix.zabbix_template_info: template_name: Template @@ -113,15 +108,15 @@ EXAMPLES = ''' ansible_httpapi_port: 443 ansible_httpapi_use_ssl: true ansible_httpapi_validate_certs: false - ansible_zabbix_url_path: 'zabbixeu' # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu + ansible_zabbix_url_path: "zabbixeu" # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu ansible_host: zabbix-example-fqdn.org community.zabbix.zabbix_template_info: template_name: Template format: none register: template -''' +""" -RETURN = ''' +RETURN = """ --- template_id: description: The ID of the template @@ -133,38 +128,33 @@ template_json: returned: when format is json and omit_date is true type: str sample: { + "changed": false, + "failed": false, + "template_id": "10529", + "template_json": { "zabbix_export": { - "version": "4.0", - "groups": [ - { - "name": "Templates" - } - ], - "templates": [ - { - "template": "Test Template", - "name": "Template for Testing", - "description": "Testing template import", - "groups": [ + "groups": [ { - "name": "Templates" + "name": "Templates", + "uuid": "7df96b18c230490a9a0a9e2307226338" } - ], - "applications": [ + ], + "templates": [ { - "name": "Test Application" + "groups": [ + { + "name": "Templates" + } + ], + "name": "ExampleTemplateForTempleteInfoModule", + "template": "ExampleTemplateForTempleteInfoModule", + "uuid": "615e9b0662bb4399a2503a9aaa743517" } - ], - "items": [], - "discovery_rules": [], - "httptests": [], - "macros": [], - "templates": [], - "screens": [] - } - ] + ], + "version": "6.0" } - } + } + } template_xml: description: The XML of the template @@ -172,34 +162,23 @@ template_xml: type: str sample: >- <zabbix_export> - <version>4.0</version> - <date>2019-10-27T14:49:57Z</date> + <version>6.0</version> <groups> <group> + <uuid>7df96b18c230490a9a0a9e2307226338</uuid> <name>Templates</name> </group> </groups> <templates> <template> - <template>Test Template</template> - <name>Template for Testing</name> - <description>Testing template import</description> + <uuid>9a83162273f74032a1005fdb13943038</uuid> + <template>ExampleTemplateForTempleteInfoModule</template> + <name>ExampleTemplateForTempleteInfoModule</name> <groups> <group> <name>Templates</name> </group> </groups> - <applications> - <application> - <name>Test Application</name> - </application> - </applications> - <items /> - <discovery_rules /> - <httptests /> - <macros /> - <templates /> - <screens /> </template> </templates> </zabbix_export> @@ -210,22 +189,20 @@ template_yaml: type: str sample: >- zabbix_export: - version: '6.0' - date: '2022-07-09T13:25:18Z' + version: "6.0" groups: - uuid: 7df96b18c230490a9a0a9e2307226338 name: Templates - templates: - - - uuid: 88a9ad240f924f669eb7d4eed736320c - template: 'Test Template' - name: 'Template for Testing' - description: 'Testing template import' - groups: + templates: - - name: Templates -''' + uuid: 67b075276bf047d3aeb8f7d5c2121c6a + template: ExampleTemplateForTempleteInfoModule + name: ExampleTemplateForTempleteInfoModule + groups: + - + name: Templatesn +""" import traceback @@ -244,13 +221,13 @@ class TemplateInfo(ZabbixBase): def get_template_id(self, template_name): template_id = [] try: - template_list = self._zapi.template.get({'output': ['templateid'], - 'filter': {'host': template_name}}) + template_list = self._zapi.template.get({"output": ["templateid"], + "filter": {"host": template_name}}) except Exception as e: - self._module.fail_json(msg='Failed to get template: %s' % e) + self._module.fail_json(msg="Failed to get template: %s" % e) if template_list: - template_id.append(template_list[0]['templateid']) + template_id.append(template_list[0]["templateid"]) return template_id @@ -258,92 +235,86 @@ class TemplateInfo(ZabbixBase): try: jsondoc = json.loads(template_json) # remove date field if requested - if omit_date and 'date' in jsondoc['zabbix_export']: - del jsondoc['zabbix_export']['date'] + if omit_date and "date" in jsondoc["zabbix_export"]: + del jsondoc["zabbix_export"]["date"] return jsondoc except ValueError as e: - self._module.fail_json(msg='Invalid JSON provided', details=to_native(e), exception=traceback.format_exc()) + self._module.fail_json(msg="Invalid JSON provided", details=to_native(e), exception=traceback.format_exc()) def load_yaml_template(self, template_yaml, omit_date=False): if omit_date: yaml_lines = template_yaml.splitlines(True) for index, line in enumerate(yaml_lines): - if 'date:' in line: + if "date:" in line: del yaml_lines[index] - return ''.join(yaml_lines) + return "".join(yaml_lines) else: return template_yaml - def dump_template(self, template_id, template_type='json', omit_date=False): + def dump_template(self, template_id, template_type="json", omit_date=False): try: - dump = self._zapi.configuration.export({'format': template_type, 'options': {'templates': template_id}}) - if template_type == 'xml': - xmlroot = ET.fromstring(dump.encode('utf-8')) + dump = self._zapi.configuration.export({"format": template_type, "options": {"templates": template_id}}) + if template_type == "xml": + xmlroot = ET.fromstring(dump.encode("utf-8")) # remove date field if requested if omit_date: date = xmlroot.find(".date") if date is not None: xmlroot.remove(date) if PY2: - return str(ET.tostring(xmlroot, encoding='utf-8')) + return str(ET.tostring(xmlroot, encoding="utf-8")) else: - return str(ET.tostring(xmlroot, encoding='utf-8').decode('utf-8')) - elif template_type == 'yaml': + return str(ET.tostring(xmlroot, encoding="utf-8").decode("utf-8")) + elif template_type == "yaml": return self.load_yaml_template(dump, omit_date) else: return self.load_json_template(dump, omit_date) except Exception as e: - self._module.fail_json(msg='Unable to export template: %s' % e) + self._module.fail_json(msg="Unable to export template: %s" % e) def main(): argument_spec = zabbix_utils.zabbix_common_argument_spec() argument_spec.update(dict( - template_name=dict(type='str', required=True), - omit_date=dict(type='bool', required=False, default=False), - format=dict(type='str', choices=['json', 'xml', 'yaml', 'none'], default='json') + template_name=dict(type="str", required=True), + omit_date=dict(type="bool", required=False, default=False), + format=dict(type="str", choices=["json", "xml", "yaml", "none"], default="json") )) module = AnsibleModule( argument_spec=argument_spec, supports_check_mode=True ) - zabbix_utils.require_creds_params(module) - - for p in ['server_url', 'login_user', 'login_password', 'timeout', 'validate_certs']: - if p in module.params and not module.params[p] is None: - module.warn('Option "%s" is deprecated with the move to httpapi connection and will be removed in the next release' % p) - - template_name = module.params['template_name'] - omit_date = module.params['omit_date'] - format = module.params['format'] + template_name = module.params["template_name"] + omit_date = module.params["omit_date"] + format = module.params["format"] template_info = TemplateInfo(module) template_id = template_info.get_template_id(template_name) if not template_id: - module.fail_json(msg='Template not found: %s' % template_name) + module.fail_json(msg="Template not found: %s" % template_name) - if format == 'json': + if format == "json": module.exit_json( changed=False, template_id=template_id[0], - template_json=template_info.dump_template(template_id, template_type='json', omit_date=omit_date) + template_json=template_info.dump_template(template_id, template_type="json", omit_date=omit_date) ) - elif format == 'xml': + elif format == "xml": module.exit_json( changed=False, template_id=template_id[0], - template_xml=template_info.dump_template(template_id, template_type='xml', omit_date=omit_date) + template_xml=template_info.dump_template(template_id, template_type="xml", omit_date=omit_date) ) - elif format == 'yaml': + elif format == "yaml": module.exit_json( changed=False, template_id=template_id[0], - template_yaml=template_info.dump_template(template_id, template_type='yaml', omit_date=omit_date) + template_yaml=template_info.dump_template(template_id, template_type="yaml", omit_date=omit_date) ) - elif format == 'none': + elif format == "none": module.exit_json(changed=False, template_id=template_id[0]) diff --git a/ansible_collections/community/zabbix/plugins/modules/zabbix_templategroup.py b/ansible_collections/community/zabbix/plugins/modules/zabbix_templategroup.py new file mode 100644 index 000000000..7ab39d8fc --- /dev/null +++ b/ansible_collections/community/zabbix/plugins/modules/zabbix_templategroup.py @@ -0,0 +1,180 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# +# 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 +__metaclass__ = type + + +DOCUMENTATION = r''' +--- +module: zabbix_templategroup +short_description: Create/delete Zabbix template groups +description: + - Create template groups if they do not exist. + - Delete existing template groups if they exist. +author: + - "Cove (@cove)" + - "Tony Minfei Ding (!UNKNOWN)" + - "Harrison Gu (@harrisongu)" +requirements: + - "python >= 2.6" +options: + state: + description: + - Create or delete template group. + required: false + type: str + default: "present" + choices: [ "present", "absent" ] + template_groups: + description: + - List of template groups to create or delete. + required: true + type: list + elements: str + aliases: [ "template_group" ] + +extends_documentation_fragment: +- community.zabbix.zabbix + +notes: + - Too many concurrent updates to the same group may cause Zabbix to return errors, see examples for a workaround if needed. +''' + +EXAMPLES = r''' +# If you want to use Username and Password to be authenticated by Zabbix Server +- name: Set credentials to access Zabbix Server API + ansible.builtin.set_fact: + ansible_user: Admin + ansible_httpapi_pass: zabbix + +# If you want to use API token to be authenticated by Zabbix Server +# https://www.zabbix.com/documentation/current/en/manual/web_interface/frontend_sections/administration/general#api-tokens +- name: Set API token + ansible.builtin.set_fact: + ansible_zabbix_auth_key: 8ec0d52432c15c91fcafe9888500cf9a607f44091ab554dbee860f6b44fac895 + +# Base create template groups example +- name: Create template groups + # set task level variables as we change ansible_connection plugin here + vars: + ansible_network_os: community.zabbix.zabbix + ansible_connection: httpapi + ansible_httpapi_port: 443 + ansible_httpapi_use_ssl: true + ansible_httpapi_validate_certs: false + ansible_zabbix_url_path: 'zabbixeu' # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu + ansible_host: zabbix-example-fqdn.org + community.zabbix.zabbix_group: + state: present + template_groups: + - Example group1 + - Example group2 + +# Limit the Zabbix group creations to one template since Zabbix can return an error when doing concurrent updates +- name: Create template groups + # set task level variables as we change ansible_connection plugin here + vars: + ansible_network_os: community.zabbix.zabbix + ansible_connection: httpapi + ansible_httpapi_port: 443 + ansible_httpapi_use_ssl: true + ansible_httpapi_validate_certs: false + ansible_zabbix_url_path: 'zabbixeu' # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu + ansible_host: zabbix-example-fqdn.org + community.zabbix.zabbix_group: + state: present + template_groups: + - Example group1 + - Example group2 + when: inventory_hostname==groups['group_name'][0] +''' + + +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.community.zabbix.plugins.module_utils.base import ZabbixBase +import ansible_collections.community.zabbix.plugins.module_utils.helpers as zabbix_utils + + +class TemplateGroup(ZabbixBase): + # create template group(s) if not exists + def create_template_group(self, group_names): + try: + group_add_list = [] + for group_name in group_names: + result = self._zapi.templategroup.get({'filter': {'name': group_name}}) + if not result: + if self._module.check_mode: + self._module.exit_json(changed=True) + self._zapi.templategroup.create({'name': group_name}) + group_add_list.append(group_name) + return group_add_list + except Exception as e: + self._module.fail_json(msg="Failed to create template group(s): %s" % e) + + # delete template group(s) + def delete_template_group(self, group_ids): + try: + if self._module.check_mode: + self._module.exit_json(changed=True) + self._zapi.templategroup.delete(group_ids) + except Exception as e: + self._module.fail_json(msg="Failed to delete template group(s), Exception: %s" % e) + + # get group ids by name + def get_group_ids(self, template_groups): + group_ids = [] + + group_list = self._zapi.templategroup.get({'output': 'extend', 'filter': {'name': template_groups}}) + for group in group_list: + group_id = group['groupid'] + group_ids.append(group_id) + return group_ids, group_list + + +def main(): + argument_spec = zabbix_utils.zabbix_common_argument_spec() + argument_spec.update(dict( + template_groups=dict(type='list', required=True, aliases=['template_group'], elements='str'), + state=dict(type='str', default="present", choices=['present', 'absent']), + )) + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True + ) + + template_groups = module.params['template_groups'] + state = module.params['state'] + + templateGroup = TemplateGroup(module) + + group_ids = [] + group_list = [] + if template_groups: + group_ids, group_list = templateGroup.get_group_ids(template_groups) + + if state == "absent": + # delete template groups + if group_ids: + delete_group_names = [] + templateGroup.delete_template_group(group_ids) + for group in group_list: + delete_group_names.append(group['name']) + module.exit_json(changed=True, + result="Successfully deleted template group(s): %s." % ",".join(delete_group_names)) + else: + module.exit_json(changed=False, result="No template group(s) to delete.") + else: + # create template groups + group_add_list = templateGroup.create_template_group(template_groups) + if len(group_add_list) > 0: + module.exit_json(changed=True, result="Successfully created template group(s): %s" % group_add_list) + else: + module.exit_json(changed=False) + + +if __name__ == '__main__': + main() diff --git a/ansible_collections/community/zabbix/plugins/modules/zabbix_token.py b/ansible_collections/community/zabbix/plugins/modules/zabbix_token.py new file mode 100644 index 000000000..fc4d4c791 --- /dev/null +++ b/ansible_collections/community/zabbix/plugins/modules/zabbix_token.py @@ -0,0 +1,288 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +# +# Copyright: (c) 2023, ONODERA Masaru <masaru-onodera@ieee.org> +# 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 + +__metaclass__ = type + + +DOCUMENTATION = """ +--- +module: zabbix_token + +short_description: Create/Update/Generate/Delete Zabbix token. + +description: + - This module allows you to create, update, generate and delete Zabbix token. + +author: + - ONODERA Masaru(@masa-orca) + +requirements: + - "python >= 3.9" + +version_added: 2.1.0 + +options: + name: + description: + - Name of the token. + required: true + type: str + description: + description: + - Description of the token. + required: false + type: str + username: + description: + - Name of user who is the token assinged to. + required: true + type: str + status: + description: + - Status of the token. + required: false + type: bool + expires_at: + description: + - A timestamp of the token will be expired. + - The token will never expire if C(0) + required: false + type: int + generate_token: + description: + - New token string will be generated if C(true). + required: false + type: bool + default: false + state: + description: + - Create or delete token. + type: str + default: present + choices: + - present + - absent + +extends_documentation_fragment: + - community.zabbix.zabbix +""" + +EXAMPLES = """ +# If you want to use Username and Password to be authenticated by Zabbix Server +- name: Set credentials to access Zabbix Server API + ansible.builtin.set_fact: + ansible_user: Admin + ansible_httpapi_pass: zabbix + +# If you want to use API token to be authenticated by Zabbix Server +# https://www.zabbix.com/documentation/current/en/manual/web_interface/frontend_sections/administration/general#api-tokens +- name: Set API token + ansible.builtin.set_fact: + ansible_zabbix_auth_key: 8ec0d52432c15c91fcafe9888500cf9a607f44091ab554dbee860f6b44fac895 + +- name: Create Zabbix token and generate token string + # set task level variables as we change ansible_connection plugin here + vars: + ansible_network_os: community.zabbix.zabbix + ansible_connection: httpapi + ansible_httpapi_port: 443 + ansible_httpapi_use_ssl: true + ansible_httpapi_validate_certs: false + ansible_zabbix_url_path: "zabbixeu" # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu + ansible_host: zabbix-example-fqdn.org + community.zabbix.zabbix_token: + name: test token + description: Admin test token + username: Admin + status: true + expires_at: 1700000000 + generate_token: true + state: present +""" + +RETURN = """ +msg: + description: The result of the operation + returned: success + type: str + sample: "Successfully created token" +token: + description: Generated token string + returned: I(generate_token=true) + type: str + sample: "8ec0d52432c15c91fcafe9888500cf9a607f44091ab554dbee860f6b44fac895" +""" + +from ansible.module_utils.basic import AnsibleModule + +from ansible_collections.community.zabbix.plugins.module_utils.base import ZabbixBase +import ansible_collections.community.zabbix.plugins.module_utils.helpers as zabbix_utils + + +class Token(ZabbixBase): + def get_userid_from_name(self, username): + try: + userids = self._zapi.user.get( + {"output": "userid", "filter": {"username": username}} + ) + if not userids or len(userids) > 1: + self._module.fail_json("User '%s' cannot be found" % username) + return userids[0]["userid"] + except Exception as e: + self._module.fail_json(msg="Failed to get userid: %s" % e) + + # get token + def get_token(self, name, userid): + try: + return self._zapi.token.get( + {"output": "extend", "userids": [userid], "filter": {"name": name}} + ) + except Exception as e: + self._module.fail_json(msg="Failed to get token: %s" % e) + + def create_token( + self, name, description, userid, status, expires_at, generate_token + ): + try: + params = {} + params["name"] = name + if isinstance(description, str): + params["description"] = description + + params["userid"] = userid + + if isinstance(status, bool): + if status: + params["status"] = "1" + else: + params["status"] = "0" + + if isinstance(expires_at, str): + params["expires_at"] = str(expires_at) + + if self._module.check_mode: + self._module.exit_json(changed=True) + result = self._zapi.token.create(params) + + if generate_token: + generated_tokens = self._zapi.token.generate(result["tokenids"]) + self._module.exit_json( + changed=True, + msg="Successfully created token.", + token=generated_tokens[0]["token"], + ) + else: + self._module.exit_json(changed=True, msg="Successfully created token.") + + except Exception as e: + self._module.fail_json(msg="Failed to create token: %s" % e) + + def update_token( + self, token, name, description, status, expires_at, generate_token + ): + try: + params = {} + params["tokenid"] = token["tokenid"] + params["name"] = name + if isinstance(description, str) and description != token["description"]: + params["description"] = description + + if isinstance(status, bool): + if status: + if token["status"] != "0": + params["status"] = "0" + else: + if token["status"] != "1": + params["status"] = "1" + + if isinstance(expires_at, int) and str(expires_at) != token["expires_at"]: + params["expires_at"] = str(expires_at) + + # If params does not have any parameter except tokenid and name, no need to update. + if len(params.keys()) == 2: + if not generate_token: + self._module.exit_json(changed=False) + elif self._module.check_mode: + self._module.exit_json(changed=True) + else: + if self._module.check_mode: + self._module.exit_json(changed=True) + self._zapi.token.update(params) + + if generate_token: + generated_tokens = self._zapi.token.generate([token["tokenid"]]) + self._module.exit_json( + changed=True, + msg="Successfully updated token.", + token=generated_tokens[0]["token"], + ) + else: + self._module.exit_json(changed=True, msg="Successfully updated token.") + + except Exception as e: + self._module.fail_json(msg="Failed to update token: %s" % e) + + # delete token + def delete_token(self, token): + try: + tokenid = token["tokenid"] + if self._module.check_mode: + self._module.exit_json(changed=True) + self._zapi.token.delete([tokenid]) + self._module.exit_json(changed=True, msg="Successfully deleted token.") + except Exception as e: + self._module.fail_json(msg="Failed to delete token: %s" % e) + + +def main(): + argument_spec = zabbix_utils.zabbix_common_argument_spec() + argument_spec.update( + dict( + name=dict(type="str", required=True), + description=dict(type="str"), + username=dict(type="str", required=True), + status=dict(type="bool"), + expires_at=dict(type="int"), + generate_token=dict(type="bool", default=False), + state=dict(type="str", choices=["present", "absent"], default="present"), + ) + ) + module = AnsibleModule( + argument_spec=argument_spec, + supports_check_mode=True, + ) + + name = module.params["name"] + description = module.params["description"] + username = module.params["username"] + status = module.params["status"] + expires_at = module.params["expires_at"] + generate_token = module.params["generate_token"] + state = module.params["state"] + + token_class_obj = Token(module) + userid = token_class_obj.get_userid_from_name(username) + tokens = token_class_obj.get_token(name, userid) + if state == "absent": + if len(tokens) == 1: + token_class_obj.delete_token(tokens[0]) + else: + module.exit_json(changed=False) + else: + if len(tokens) == 1: + token_class_obj.update_token( + tokens[0], name, description, status, expires_at, generate_token + ) + else: + token_class_obj.create_token( + name, description, userid, status, expires_at, generate_token + ) + + +if __name__ == "__main__": + main() diff --git a/ansible_collections/community/zabbix/plugins/modules/zabbix_user.py b/ansible_collections/community/zabbix/plugins/modules/zabbix_user.py index 3c655ffe8..92f04b989 100644 --- a/ansible_collections/community/zabbix/plugins/modules/zabbix_user.py +++ b/ansible_collections/community/zabbix/plugins/modules/zabbix_user.py @@ -6,9 +6,10 @@ from __future__ import absolute_import, division, print_function + __metaclass__ = type -DOCUMENTATION = r''' +DOCUMENTATION = r""" module: zabbix_user short_description: Create/update/delete Zabbix users author: @@ -16,14 +17,12 @@ author: description: - This module allows you to create, modify and delete Zabbix users. requirements: - - "python >= 2.6" + - "python >= 3.9" options: username: description: - - Name of the user alias in Zabbix. + - Username. - username is the unique identifier used and cannot be updated using this module. - - alias must be replaced with username since Zabbix 6.4. - aliases: [ alias ] required: true type: str name: @@ -45,7 +44,6 @@ options: description: - User's password. - Required unless all of the I(usrgrps) are set to use LDAP as frontend access. - - Always required for Zabbix versions lower than 4.0. required: false type: str override_passwd: @@ -54,37 +52,43 @@ options: - Password will not be updated on subsequent runs without setting this value to yes. default: no type: bool + current_passwd: + description: + - Current password for the user when overriding its password. + - Required when overriding the logged in user's password. + - https://www.zabbix.com/documentation/6.4/en/manual/api/reference/user/update + required: false + type: str lang: description: - Language code of the user's language. - - C(default) can be used with Zabbix version 5.2 or higher. choices: - - 'en_GB' - - 'en_US' - - 'zh_CN' - - 'cs_CZ' - - 'fr_FR' - - 'he_IL' - - 'it_IT' - - 'ko_KR' - - 'ja_JP' - - 'nb_NO' - - 'pl_PL' - - 'pt_BR' - - 'pt_PT' - - 'ru_RU' - - 'sk_SK' - - 'tr_TR' - - 'uk_UA' - - 'default' + - "en_GB" + - "en_US" + - "zh_CN" + - "cs_CZ" + - "fr_FR" + - "he_IL" + - "it_IT" + - "ko_KR" + - "ja_JP" + - "nb_NO" + - "pl_PL" + - "pt_BR" + - "pt_PT" + - "ru_RU" + - "sk_SK" + - "tr_TR" + - "uk_UA" + - "default" type: str theme: description: - User's theme. choices: - - 'default' - - 'blue-theme' - - 'dark-theme' + - "default" + - "blue-theme" + - "dark-theme" type: str autologin: description: @@ -116,7 +120,7 @@ options: mediatype: description: - Media type name to set. - default: 'Email' + default: "Email" type: str sendto: description: @@ -128,8 +132,8 @@ options: description: - Time when the notifications can be sent as a time period or user macros separated by a semicolon. - Please review the documentation for more information on the supported time period. - - https://www.zabbix.com/documentation/4.0/manual/appendix/time_period - default: '1-7,00:00-24:00' + - https://www.zabbix.com/documentation/current/en/manual/appendix/time_period + default: "1-7,00:00-24:00" type: str severity: description: @@ -180,26 +184,15 @@ options: type: bool type: list elements: dict - type: - description: - - Type of the user. - - I(type) can be used when Zabbix version is 5.0 or lower. - choices: - - 'Zabbix user' - - 'Zabbix admin' - - 'Zabbix super admin' - type: str timezone: description: - User's time zone. - - I(timezone) can be used with Zabbix version 5.2 or higher. - For the full list of supported time zones please refer to U(https://www.php.net/manual/en/timezones.php) type: str version_added: 1.2.0 role_name: description: - User's role. - - I(role_name) can be used when Zabbix version is 5.2 or higher. - Default is C(User role) when creating a new user. - The default value will be removed at the version 2.0.0. type: str @@ -209,25 +202,25 @@ options: - State of the user. - On C(present), it will create if user does not exist or update the user if the associated data is different. - On C(absent) will remove a user if it exists. - default: 'present' - choices: ['present', 'absent'] + default: "present" + choices: ["present", "absent"] type: str extends_documentation_fragment: - community.zabbix.zabbix -''' +""" -EXAMPLES = r''' +EXAMPLES = r""" # If you want to use Username and Password to be authenticated by Zabbix Server - name: Set credentials to access Zabbix Server API - set_fact: + ansible.builtin.set_fact: ansible_user: Admin ansible_httpapi_pass: zabbix # If you want to use API token to be authenticated by Zabbix Server # https://www.zabbix.com/documentation/current/en/manual/web_interface/frontend_sections/administration/general#api-tokens - name: Set API token - set_fact: + ansible.builtin.set_fact: ansible_zabbix_auth_key: 8ec0d52432c15c91fcafe9888500cf9a607f44091ab554dbee860f6b44fac895 - name: create a new zabbix user. @@ -238,7 +231,7 @@ EXAMPLES = r''' ansible_httpapi_port: 443 ansible_httpapi_use_ssl: true ansible_httpapi_validate_certs: false - ansible_zabbix_url_path: 'zabbixeu' # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu + ansible_zabbix_url_path: "zabbixeu" # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu ansible_host: zabbix-example-fqdn.org community.zabbix.zabbix_user: username: example @@ -251,10 +244,10 @@ EXAMPLES = r''' lang: en_GB theme: blue-theme autologin: no - autologout: '0' - refresh: '30' - rows_per_page: '200' - after_login_url: '' + autologout: "0" + refresh: "30" + rows_per_page: "200" + after_login_url: "" user_medias: - mediatype: Email sendto: @@ -269,7 +262,6 @@ EXAMPLES = r''' high: yes disaster: yes active: no - type: Zabbix super admin state: present - name: delete existing zabbix user. @@ -280,20 +272,20 @@ EXAMPLES = r''' ansible_httpapi_port: 443 ansible_httpapi_use_ssl: true ansible_httpapi_validate_certs: false - ansible_zabbix_url_path: 'zabbixeu' # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu + ansible_zabbix_url_path: "zabbixeu" # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu ansible_host: zabbix-example-fqdn.org community.zabbix.zabbix_user: username: example state: absent -''' +""" -RETURN = r''' +RETURN = r""" user_ids: description: User id created or changed returned: success type: dict sample: { "userids": [ "5" ] } -''' +""" import copy @@ -301,44 +293,40 @@ import copy from ansible.module_utils.basic import AnsibleModule from ansible_collections.community.zabbix.plugins.module_utils.base import ZabbixBase -from ansible_collections.community.zabbix.plugins.module_utils.helpers import helper_normalize_data +from ansible_collections.community.zabbix.plugins.module_utils.helpers import ( + helper_normalize_data, +) from ansible.module_utils.compat.version import LooseVersion import ansible_collections.community.zabbix.plugins.module_utils.helpers as zabbix_utils class User(ZabbixBase): - - def username_key(self): - """ Returns the key name for 'username', which was 'alias' - before Zabbix 5.4. - """ - if LooseVersion(self._zbx_api_version) < LooseVersion('5.4'): - return 'alias' - return 'username' - def get_default_authentication(self): - auth = self._zapi.authentication.get({'output': 'extend'}) + auth = self._zapi.authentication.get({"output": "extend"}) try: if auth["authentication_type"] == "0": return "internal" elif auth["authentication_type"] == "1": return "LDAP" else: - self._module.fail_json(msg="Failed to query authentication type. Unknown authentication type %s" % auth) + self._module.fail_json( + msg="Failed to query authentication type. Unknown authentication type %s" + % auth + ) except Exception as e: - self._module.fail_json(msg="Unhandled error while querying authentication type. %s" % (e)) + self._module.fail_json( + msg="Unhandled error while querying authentication type. %s" % (e) + ) def get_usergroups_by_name(self, usrgrps): params = { - 'output': ['usrgrpid', 'name', 'gui_access'], - 'filter': { - 'name': usrgrps - } + "output": ["usrgrpid", "name", "gui_access"], + "filter": {"name": usrgrps}, } res = self._zapi.usergroup.get(params) if res: - ids = [{'usrgrpid': g['usrgrpid']} for g in res] + ids = [{"usrgrpid": g["usrgrpid"]} for g in res] # User can be created password-less only when all groups are of non-internal # authentication types # 0 = use system default authentication method @@ -346,199 +334,248 @@ class User(ZabbixBase): # 2 = use LDAP authentication # 3 = disable access to the frontend - if bool([g for g in res if g['gui_access'] == '1']): + if bool([g for g in res if g["gui_access"] == "1"]): require_password = True - elif bool([g for g in res if g['gui_access'] == '2' or g['gui_access'] == '3']): + elif bool( + [g for g in res if g["gui_access"] == "2" or g["gui_access"] == "3"] + ): require_password = False - elif bool([g for g in res if g['gui_access'] == '0']): + elif bool([g for g in res if g["gui_access"] == "0"]): # Zabbix API for versions < 5.2 does not have a way to query the default auth type # so we must assume its set to internal - if LooseVersion(self._zbx_api_version) >= LooseVersion('5.2'): - default_authentication = self.get_default_authentication() - require_password = True if default_authentication == 'internal' else False - else: - default_authentication = "internal" - require_password = True + default_authentication = self.get_default_authentication() + require_password = ( + True if default_authentication == "internal" else False + ) - not_found_groups = set(usrgrps) - set([g['name'] for g in res]) + not_found_groups = set(usrgrps) - set([g["name"] for g in res]) if not_found_groups: - self._module.fail_json(msg='User groups not found: %s' % not_found_groups) + self._module.fail_json( + msg="User groups not found: %s" % not_found_groups + ) return ids, require_password else: - self._module.fail_json(msg='No user groups found') + self._module.fail_json(msg="No user groups found") def check_user_exist(self, username): - zbx_user = self._zapi.user.get({'output': 'extend', 'filter': {self.username_key(): username}, - 'getAccess': True, 'selectMedias': 'extend', - 'selectUsrgrps': 'extend'}) + zbx_user = self._zapi.user.get( + { + "output": "extend", + "filter": {"username": username}, + "getAccess": True, + "selectMedias": "extend", + "selectUsrgrps": "extend", + } + ) return zbx_user def convert_user_medias_parameter_types(self, user_medias): copy_user_medias = copy.deepcopy(user_medias) for user_media in copy_user_medias: - media_types = self._zapi.mediatype.get({'output': 'extend'}) + media_types = self._zapi.mediatype.get({"output": "extend"}) for media_type in media_types: - if LooseVersion(self._zbx_api_version) < LooseVersion('4.4'): - if media_type['description'] == user_media['mediatype']: - user_media['mediatypeid'] = media_type['mediatypeid'] - user_media['mediatype'] = media_type['type'] - break - else: - if media_type['name'] == user_media['mediatype']: - user_media['mediatypeid'] = media_type['mediatypeid'] - user_media['mediatype'] = media_type['type'] - break - if 'mediatypeid' not in user_media: - self._module.fail_json(msg="Media type not found: %s" % user_media['mediatype']) + if media_type["name"] == user_media["mediatype"]: + user_media["mediatypeid"] = media_type["mediatypeid"] + user_media["mediatype"] = media_type["type"] + break + if "mediatypeid" not in user_media: + self._module.fail_json( + msg="Media type not found: %s" % user_media["mediatype"] + ) else: - if user_media['mediatype'] == '0': # E-Mail + if user_media["mediatype"] == "0": # E-Mail # Because user media sendto parameter is raw in parameters specs perform explicit check on type - if not (isinstance(user_media['sendto'], list) or isinstance(user_media['sendto'], str)): - self._module.fail_json('For Email media type sendto parameter must be of type list or str.') - if isinstance(user_media['sendto'], str): + if not ( + isinstance(user_media["sendto"], list) + or isinstance(user_media["sendto"], str) + ): + self._module.fail_json( + "For Email media type sendto parameter must be of type list or str." + ) + if isinstance(user_media["sendto"], str): # sendto should be a list for Email media type - user_media['sendto'] = [user_media['sendto']] + user_media["sendto"] = [user_media["sendto"]] else: - if not isinstance(user_media['sendto'], str): - self._module.fail_json(user_media) - self._module.fail_json('For any other than Email media type sendto parameter must be of type str.') - del user_media['mediatype'] - - severity_binary_number = '' - for severity_key in 'disaster', 'high', 'average', 'warning', 'information', 'not_classified': - if user_media['severity'][severity_key]: - severity_binary_number = severity_binary_number + '1' + if not isinstance(user_media["sendto"], str): + self._module.fail_json( + "For any other than Email media type sendto parameter must be of type str." + ) + del user_media["mediatype"] + + severity_binary_number = "" + for severity_key in ( + "disaster", + "high", + "average", + "warning", + "information", + "not_classified", + ): + if user_media["severity"][severity_key]: + severity_binary_number = severity_binary_number + "1" else: - severity_binary_number = severity_binary_number + '0' - user_media['severity'] = str(int(severity_binary_number, 2)) + severity_binary_number = severity_binary_number + "0" + user_media["severity"] = str(int(severity_binary_number, 2)) - if user_media['active']: - user_media['active'] = '0' + if user_media["active"]: + user_media["active"] = "0" else: - user_media['active'] = '1' + user_media["active"] = "1" return copy_user_medias def get_roleid_by_name(self, role_name): - roles = self._zapi.role.get({'output': 'extend'}) + roles = self._zapi.role.get({"output": "extend"}) for role in roles: - if role['name'] == role_name: - return role['roleid'] + if role["name"] == role_name: + return role["roleid"] self._module.fail_json(msg="Role not found: %s" % role_name) - def user_parameter_difference_check(self, zbx_user, username, name, surname, user_group_ids, passwd, lang, theme, - autologin, autologout, refresh, rows_per_page, url, user_medias, user_type, - timezone, role_name, override_passwd): + def user_parameter_difference_check( + self, + zbx_user, + username, + name, + surname, + user_group_ids, + passwd, + lang, + theme, + autologin, + autologout, + refresh, + rows_per_page, + url, + user_medias, + timezone, + role_name, + override_passwd, + ): # existing data existing_data = copy.deepcopy(zbx_user[0]) usrgrpids = [] - for usrgrp in existing_data['usrgrps']: - usrgrpids.append({'usrgrpid': usrgrp['usrgrpid']}) - existing_data['usrgrps'] = sorted(usrgrpids, key=lambda x: x['usrgrpid']) - existing_data['user_medias'] = existing_data['medias'] - for del_key in ['medias', 'attempt_clock', 'attempt_failed', 'attempt_ip', 'debug_mode', 'users_status', - 'gui_access']: + for usrgrp in existing_data["usrgrps"]: + usrgrpids.append({"usrgrpid": usrgrp["usrgrpid"]}) + existing_data["usrgrps"] = sorted(usrgrpids, key=lambda x: x["usrgrpid"]) + existing_data["user_medias"] = existing_data["medias"] + for del_key in [ + "medias", + "attempt_clock", + "attempt_failed", + "attempt_ip", + "debug_mode", + "users_status", + "gui_access", + ]: del existing_data[del_key] - if 'user_medias' in existing_data and existing_data['user_medias']: - for user_media in existing_data['user_medias']: - for del_key in ['mediaid', 'userid']: + if "user_medias" in existing_data and existing_data["user_medias"]: + for user_media in existing_data["user_medias"]: + for del_key in ["mediaid", "userid"]: del user_media[del_key] # request data request_data = { - 'userid': zbx_user[0]['userid'], - self.username_key(): username, - 'name': name, - 'surname': surname, - 'usrgrps': sorted(user_group_ids, key=lambda x: x['usrgrpid']), - 'lang': lang, - 'theme': theme, - 'autologin': autologin, - 'autologout': autologout, - 'refresh': refresh, - 'rows_per_page': rows_per_page, - 'url': url, + "userid": zbx_user[0]["userid"], + "username": username, + "name": name, + "surname": surname, + "usrgrps": sorted(user_group_ids, key=lambda x: x["usrgrpid"]), + "lang": lang, + "theme": theme, + "autologin": autologin, + "autologout": autologout, + "refresh": refresh, + "rows_per_page": rows_per_page, + "url": url, } if user_medias: - request_data['user_medias'] = user_medias + request_data["user_medias"] = user_medias else: - if 'user_medias' in existing_data and existing_data['user_medias']: - del existing_data['user_medias'] + if "user_medias" in existing_data and existing_data["user_medias"]: + del existing_data["user_medias"] if override_passwd: - request_data['passwd'] = passwd + request_data["passwd"] = passwd - # The type key has changed to roleid key since Zabbix 5.2 - if LooseVersion(self._zbx_api_version) < LooseVersion('5.2'): - request_data['type'] = user_type - else: - request_data['roleid'] = self.get_roleid_by_name(role_name) if role_name else None - request_data['timezone'] = timezone + request_data["roleid"] = ( + self.get_roleid_by_name(role_name) if role_name else None + ) + request_data["timezone"] = timezone request_data, del_keys = helper_normalize_data(request_data) existing_data, _del_keys = helper_normalize_data(existing_data, del_keys) user_parameter_difference_check_result = True diff_dict = {} - if not zabbix_utils.helper_compare_dictionaries(request_data, existing_data, diff_dict): + if not zabbix_utils.helper_compare_dictionaries( + request_data, existing_data, diff_dict + ): user_parameter_difference_check_result = False - if LooseVersion(self._zbx_api_version) >= LooseVersion('6.4'): + if LooseVersion(self._zbx_api_version) >= LooseVersion("6.4"): if user_medias: - request_data['medias'] = user_medias - del request_data['user_medias'] + request_data["medias"] = user_medias + del request_data["user_medias"] - diff_params = { - "before": existing_data, - "after": request_data - } + diff_params = {"before": existing_data, "after": request_data} return user_parameter_difference_check_result, diff_params - def add_user(self, username, name, surname, user_group_ids, passwd, lang, theme, autologin, autologout, refresh, - rows_per_page, url, user_medias, user_type, require_password, timezone, role_name): - - if role_name is None and LooseVersion(self._zbx_api_version) >= LooseVersion('5.2'): - # This variable is to set the default value because the module must have a backward-compatible. - # The default value will be removed at the version 2.0.0. - # https://github.com/ansible-collections/community.zabbix/pull/382 + def add_user( + self, + username, + name, + surname, + user_group_ids, + passwd, + lang, + theme, + autologin, + autologout, + refresh, + rows_per_page, + url, + user_medias, + require_password, + timezone, + role_name, + ): + + if role_name is None: role_name = "User role" user_ids = {} request_data = { - self.username_key(): username, - 'name': name, - 'surname': surname, - 'usrgrps': user_group_ids, - 'lang': lang, - 'theme': theme, - 'autologin': autologin, - 'autologout': autologout, - 'refresh': refresh, - 'rows_per_page': rows_per_page, - 'url': url, + "username": username, + "name": name, + "surname": surname, + "usrgrps": user_group_ids, + "lang": lang, + "theme": theme, + "autologin": autologin, + "autologout": autologout, + "refresh": refresh, + "rows_per_page": rows_per_page, + "url": url, } if user_medias: - if LooseVersion(self._zbx_api_version) <= LooseVersion('6.2'): - request_data['user_medias'] = user_medias + if LooseVersion(self._zbx_api_version) <= LooseVersion("6.2"): + request_data["user_medias"] = user_medias else: - request_data['medias'] = user_medias + request_data["medias"] = user_medias - if LooseVersion(self._zbx_api_version) < LooseVersion('4.0') or require_password: - request_data['passwd'] = passwd + if (require_password): + request_data["passwd"] = passwd # The type key has changed to roleid key since Zabbix 5.2 - if LooseVersion(self._zbx_api_version) < LooseVersion('5.2'): - request_data['type'] = user_type - else: - request_data['roleid'] = self.get_roleid_by_name(role_name) - request_data['timezone'] = timezone + request_data["roleid"] = self.get_roleid_by_name(role_name) + request_data["timezone"] = timezone request_data, _del_keys = helper_normalize_data(request_data) @@ -547,79 +584,84 @@ class User(ZabbixBase): try: user_ids = self._zapi.user.create(request_data) except Exception as e: - self._module.fail_json(msg="Failed to create user %s: %s" % (username, e)) + self._module.fail_json( + msg="Failed to create user %s: %s" % (username, e) + ) else: - diff_params = { - "before": "", - "after": request_data - } + diff_params = {"before": "", "after": request_data} return user_ids, diff_params - def update_user(self, zbx_user, username, name, surname, user_group_ids, passwd, lang, theme, autologin, autologout, - refresh, rows_per_page, url, user_medias, user_type, timezone, role_name, override_passwd): + def update_user( + self, + zbx_user, + username, + name, + surname, + user_group_ids, + passwd, + lang, + theme, + autologin, + autologout, + refresh, + rows_per_page, + url, + user_medias, + timezone, + role_name, + override_passwd, + current_passwd, + ): user_ids = {} request_data = { - 'userid': zbx_user[0]['userid'], - self.username_key(): username, - 'name': name, - 'surname': surname, - 'usrgrps': user_group_ids, - 'lang': lang, - 'theme': theme, - 'autologin': autologin, - 'autologout': autologout, - 'refresh': refresh, - 'rows_per_page': rows_per_page, - 'url': url, + "userid": zbx_user[0]["userid"], + "username": username, + "name": name, + "surname": surname, + "usrgrps": user_group_ids, + "lang": lang, + "theme": theme, + "autologin": autologin, + "autologout": autologout, + "refresh": refresh, + "rows_per_page": rows_per_page, + "url": url, } if override_passwd: - request_data['passwd'] = passwd + request_data["passwd"] = passwd + if current_passwd: + request_data["current_passwd"] = current_passwd - # The type key has changed to roleid key since Zabbix 5.2 - if LooseVersion(self._zbx_api_version) < LooseVersion('5.2'): - request_data['type'] = user_type - else: - request_data['roleid'] = self.get_roleid_by_name(role_name) if role_name else None - request_data['timezone'] = timezone + request_data["roleid"] = ( + self.get_roleid_by_name(role_name) if role_name else None + ) + request_data["timezone"] = timezone request_data, _del_keys = helper_normalize_data(request_data) - # In the case of zabbix 3.2 or less, it is necessary to use updatemedia method to update media. - if LooseVersion(self._zbx_api_version) <= LooseVersion('3.2'): - try: - user_ids = self._zapi.user.update(request_data) - except Exception as e: - self._module.fail_json(msg="Failed to update user %s: %s" % (username, e)) - + if LooseVersion(self._zbx_api_version) < LooseVersion("6.4"): try: if user_medias: - user_ids = self._zapi.user.updatemedia({ - 'users': [{'userid': zbx_user[0]['userid']}], - 'medias': user_medias - }) - except Exception as e: - self._module.fail_json(msg="Failed to update user medias %s: %s" % (username, e)) - - if (LooseVersion(self._zbx_api_version) >= LooseVersion('3.4') - and LooseVersion(self._zbx_api_version) < LooseVersion('6.4')): - try: - if user_medias: - request_data['user_medias'] = user_medias + request_data["user_medias"] = user_medias user_ids = self._zapi.user.update(request_data) except Exception as e: - self._module.fail_json(msg="Failed to update user %s: %s" % (username, e)) + self._module.fail_json( + msg="Failed to update user %s: %s" % (username, e) + ) - if LooseVersion(self._zbx_api_version) >= LooseVersion('6.4'): + if LooseVersion(self._zbx_api_version) >= LooseVersion("6.4"): try: if user_medias: - request_data['medias'] = user_medias + request_data["medias"] = user_medias user_ids = self._zapi.user.update(request_data) except Exception as e: - self._module.fail_json(msg="Failed to update user %s: %s" % (username, e)) + self._module.fail_json( + msg="Failed to update user %s: %s" % (username, e) + ) return user_ids @@ -629,107 +671,123 @@ class User(ZabbixBase): if not self._module.check_mode: try: - user_ids = self._zapi.user.delete([zbx_user[0]['userid']]) + user_ids = self._zapi.user.delete([zbx_user[0]["userid"]]) except Exception as e: - self._module.fail_json(msg="Failed to delete user %s: %s" % (username, e)) + self._module.fail_json( + msg="Failed to delete user %s: %s" % (username, e) + ) else: - diff_params = { - "before": zbx_user[0], - "after": "" - } + diff_params = {"before": zbx_user[0], "after": ""} return user_ids, diff_params def main(): argument_spec = zabbix_utils.zabbix_common_argument_spec() - argument_spec.update(dict( - username=dict(type='str', required=True, aliases=['alias']), - name=dict(type='str'), - surname=dict(type='str'), - usrgrps=dict(type='list'), - passwd=dict(type='str', required=False, no_log=True), - override_passwd=dict(type='bool', required=False, default=False, no_log=False), - lang=dict(type='str', choices=['en_GB', 'en_US', 'zh_CN', 'cs_CZ', 'fr_FR', - 'he_IL', 'it_IT', 'ko_KR', 'ja_JP', 'nb_NO', - 'pl_PL', 'pt_BR', 'pt_PT', 'ru_RU', 'sk_SK', - 'tr_TR', 'uk_UA', 'default']), - theme=dict(type='str', choices=['default', 'blue-theme', 'dark-theme']), - autologin=dict(type='bool'), - autologout=dict(type='str'), - refresh=dict(type='str'), - rows_per_page=dict(type='str'), - after_login_url=dict(type='str'), - user_medias=dict(type='list', elements='dict', - options=dict(mediatype=dict(type='str', default='Email'), - sendto=dict(type='raw', required=True), - period=dict(type='str', default='1-7,00:00-24:00'), - severity=dict(type='dict', - options=dict( - not_classified=dict(type='bool', default=True), - information=dict(type='bool', default=True), - warning=dict(type='bool', default=True), - average=dict(type='bool', default=True), - high=dict(type='bool', default=True), - disaster=dict(type='bool', default=True)), - default=dict( - not_classified=True, - information=True, - warning=True, - average=True, - high=True, - disaster=True)), - active=dict(type='bool', default=True))), - timezone=dict(type='str'), - role_name=dict(type='str'), - type=dict(type='str', choices=['Zabbix user', 'Zabbix admin', 'Zabbix super admin']), - state=dict(type='str', default="present", choices=['present', 'absent']) - )) + argument_spec.update( + dict( + username=dict(type="str", required=True), + name=dict(type="str"), + surname=dict(type="str"), + usrgrps=dict(type="list", elements="str"), + passwd=dict(type="str", required=False, no_log=True), + override_passwd=dict( + type="bool", required=False, default=False, no_log=False + ), + current_passwd=dict(type="str", required=False, no_log=True), + lang=dict( + type="str", + choices=[ + "en_GB", + "en_US", + "zh_CN", + "cs_CZ", + "fr_FR", + "he_IL", + "it_IT", + "ko_KR", + "ja_JP", + "nb_NO", + "pl_PL", + "pt_BR", + "pt_PT", + "ru_RU", + "sk_SK", + "tr_TR", + "uk_UA", + "default", + ], + ), + theme=dict(type="str", choices=["default", "blue-theme", "dark-theme"]), + autologin=dict(type="bool"), + autologout=dict(type="str"), + refresh=dict(type="str"), + rows_per_page=dict(type="str"), + after_login_url=dict(type="str"), + user_medias=dict( + type="list", + elements="dict", + options=dict( + mediatype=dict(type="str", default="Email"), + sendto=dict(type="raw", required=True), + period=dict(type="str", default="1-7,00:00-24:00"), + severity=dict( + type="dict", + options=dict( + not_classified=dict(type="bool", default=True), + information=dict(type="bool", default=True), + warning=dict(type="bool", default=True), + average=dict(type="bool", default=True), + high=dict(type="bool", default=True), + disaster=dict(type="bool", default=True), + ), + default=dict( + not_classified=True, + information=True, + warning=True, + average=True, + high=True, + disaster=True, + ), + ), + active=dict(type="bool", default=True), + ), + ), + timezone=dict(type="str"), + role_name=dict(type="str"), + state=dict(type="str", default="present", choices=["present", "absent"]), + ) + ) module = AnsibleModule( argument_spec=argument_spec, - required_if=[ - ['state', 'present', ['usrgrps']] - ], - supports_check_mode=True + required_if=[["state", "present", ["usrgrps"]]], + supports_check_mode=True, ) - zabbix_utils.require_creds_params(module) - - for p in ['server_url', 'login_user', 'login_password', 'timeout', 'validate_certs']: - if p in module.params and not module.params[p] is None: - module.warn('Option "%s" is deprecated with the move to httpapi connection and will be removed in the next release' % p) - - username = module.params['username'] - name = module.params['name'] - surname = module.params['surname'] - usrgrps = module.params['usrgrps'] - passwd = module.params['passwd'] - override_passwd = module.params['override_passwd'] - lang = module.params['lang'] - theme = module.params['theme'] - autologin = module.params['autologin'] - autologout = module.params['autologout'] - refresh = module.params['refresh'] - rows_per_page = module.params['rows_per_page'] - after_login_url = module.params['after_login_url'] - user_medias = module.params['user_medias'] - user_type = module.params['type'] - timezone = module.params['timezone'] - role_name = module.params['role_name'] - state = module.params['state'] + username = module.params["username"] + name = module.params["name"] + surname = module.params["surname"] + usrgrps = module.params["usrgrps"] + passwd = module.params["passwd"] + override_passwd = module.params["override_passwd"] + current_passwd = module.params["current_passwd"] + lang = module.params["lang"] + theme = module.params["theme"] + autologin = module.params["autologin"] + autologout = module.params["autologout"] + refresh = module.params["refresh"] + rows_per_page = module.params["rows_per_page"] + after_login_url = module.params["after_login_url"] + user_medias = module.params["user_medias"] + timezone = module.params["timezone"] + role_name = module.params["role_name"] + state = module.params["state"] if autologin is not None: if autologin: - autologin = '1' + autologin = "1" else: - autologin = '0' - - user_type_dict = { - 'Zabbix user': '1', - 'Zabbix admin': '2', - 'Zabbix super admin': '3' - } - user_type = user_type_dict[user_type] if user_type else None + autologin = "0" user = User(module) @@ -738,31 +796,78 @@ def main(): user_ids = {} zbx_user = user.check_user_exist(username) - if state == 'present': + if state == "present": user_group_ids, require_password = user.get_usergroups_by_name(usrgrps) - if LooseVersion(user._zbx_api_version) < LooseVersion('4.0') or require_password: + if (require_password): if passwd is None: - module.fail_json(msg='User password is required. One or more groups are not LDAP based.') + module.fail_json( + msg="User password is required. One or more groups are not LDAP based." + ) if zbx_user: - diff_check_result, diff_params = user.user_parameter_difference_check(zbx_user, username, name, surname, - user_group_ids, passwd, lang, theme, - autologin, autologout, refresh, - rows_per_page, after_login_url, - user_medias, user_type, timezone, - role_name, override_passwd) + diff_check_result, diff_params = user.user_parameter_difference_check( + zbx_user, + username, + name, + surname, + user_group_ids, + passwd, + lang, + theme, + autologin, + autologout, + refresh, + rows_per_page, + after_login_url, + user_medias, + timezone, + role_name, + override_passwd, + ) if not module.check_mode and diff_check_result: - user_ids = user.update_user(zbx_user, username, name, surname, user_group_ids, passwd, lang, - theme, autologin, autologout, refresh, rows_per_page, after_login_url, - user_medias, user_type, timezone, role_name, override_passwd) + user_ids = user.update_user( + zbx_user, + username, + name, + surname, + user_group_ids, + passwd, + lang, + theme, + autologin, + autologout, + refresh, + rows_per_page, + after_login_url, + user_medias, + timezone, + role_name, + override_passwd, + current_passwd, + ) else: diff_check_result = True - user_ids, diff_params = user.add_user(username, name, surname, user_group_ids, passwd, lang, theme, autologin, - autologout, refresh, rows_per_page, after_login_url, user_medias, - user_type, require_password, timezone, role_name) - - if state == 'absent': + user_ids, diff_params = user.add_user( + username, + name, + surname, + user_group_ids, + passwd, + lang, + theme, + autologin, + autologout, + refresh, + rows_per_page, + after_login_url, + user_medias, + require_password, + timezone, + role_name, + ) + + if state == "absent": if zbx_user: diff_check_result = True user_ids, diff_params = user.delete_user(zbx_user, username) diff --git a/ansible_collections/community/zabbix/plugins/modules/zabbix_user_directory.py b/ansible_collections/community/zabbix/plugins/modules/zabbix_user_directory.py index 818fe98c0..b383fcdcf 100644 --- a/ansible_collections/community/zabbix/plugins/modules/zabbix_user_directory.py +++ b/ansible_collections/community/zabbix/plugins/modules/zabbix_user_directory.py @@ -4,10 +4,11 @@ # 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 + __metaclass__ = type -DOCUMENTATION = r''' +DOCUMENTATION = r""" --- module: zabbix_user_directory short_description: Create/update/delete Zabbix user directories @@ -29,7 +30,7 @@ options: - This parameter is available since Zabbix 6.4. required: false type: str - choices: ['ldap', 'saml'] + choices: ["ldap", "saml"] provision_status: description: - User directory provisioning status. @@ -63,7 +64,7 @@ options: - User directory description. required: false type: str - default: '' + default: "" group_membership: description: - LDAP property containing groups of user. E.g. I(memberOf) @@ -150,7 +151,7 @@ options: - LDAP bind distinguished name string. Can be empty for anonymous binding. required: false type: str - default: '' + default: "" idp_entityid: description: - SAML URI that identifies the IdP in SAML messages. @@ -167,7 +168,7 @@ options: type: str sso_url: description: - - SAML URL of the IdP's SAML SSO service, to which Zabbix will send SAML authentication requests. + - SAML URL of the IdP"s SAML SSO service, to which Zabbix will send SAML authentication requests. - required if C(idp_type) is set to I(saml). - This parameter is available since Zabbix 6.4. required: false @@ -302,27 +303,27 @@ options: - State of the user directory. - On C(present), it will create if user directory does not exist or update it if the associated data is different. - On C(absent) will remove the user directory if it exists. - choices: ['present', 'absent'] - default: 'present' + choices: ["present", "absent"] + default: "present" type: str extends_documentation_fragment: - community.zabbix.zabbix -''' +""" -EXAMPLES = r''' +EXAMPLES = r""" --- # If you want to use Username and Password to be authenticated by Zabbix Server - name: Set credentials to access Zabbix Server API - set_fact: + ansible.builtin.set_fact: ansible_user: Admin ansible_httpapi_pass: zabbix # If you want to use API token to be authenticated by Zabbix Server # https://www.zabbix.com/documentation/current/en/manual/web_interface/frontend_sections/administration/general#api-tokens - name: Set API token - set_fact: + ansible.builtin.set_fact: ansible_zabbix_auth_key: 8ec0d52432c15c91fcafe9888500cf9a607f44091ab554dbee860f6b44fac895 - name: Create new user directory or update existing info (Zabbix <= 6.2) @@ -333,18 +334,18 @@ EXAMPLES = r''' ansible_httpapi_port: 443 ansible_httpapi_use_ssl: true ansible_httpapi_validate_certs: false - ansible_zabbix_url_path: 'zabbixeu' # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu + ansible_zabbix_url_path: "zabbixeu" # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu ansible_host: zabbix-example-fqdn.org community.zabbix.zabbix_user_directory: state: present name: TestUserDirectory - host: 'test.com' + host: "test.com" port: 389 - base_dn: 'ou=Users,dc=example,dc=org' - search_attribute: 'uid' - bind_dn: 'cn=ldap_search,dc=example,dc=org' - description: 'Test user directory' - search_filter: '(%{attr}=test_user)' + base_dn: "ou=Users,dc=example,dc=org" + search_attribute: "uid" + bind_dn: "cn=ldap_search,dc=example,dc=org" + description: "Test user directory" + search_filter: "(%{attr}=test_user)" start_tls: 0 - name: Create new user directory with LDAP IDP or update existing info (Zabbix >= 6.4) @@ -355,22 +356,22 @@ EXAMPLES = r''' ansible_httpapi_port: 443 ansible_httpapi_use_ssl: true ansible_httpapi_validate_certs: false - ansible_zabbix_url_path: 'zabbixeu' # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu + ansible_zabbix_url_path: "zabbixeu" # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu ansible_host: zabbix-example-fqdn.org community.zabbix.zabbix_user_directory: state: present name: TestUserDirectory idp_type: ldap - host: 'test.ca' + host: "test.ca" port: 389 - base_dn: 'ou=Users,dc=example,dc=org' - search_attribute: 'uid' + base_dn: "ou=Users,dc=example,dc=org" + search_attribute: "uid" provision_status: true group_name: cn group_basedn: ou=Group,dc=example,dc=org group_member: member user_ref_attr: uid - group_filter: '(member=uid=%{ref},ou=Users,dc=example,dc=com)' + group_filter: "(member=uid=%{ref},ou=Users,dc=example,dc=com)" user_username: first_name user_lastname: last_name provision_media: @@ -391,7 +392,7 @@ EXAMPLES = r''' ansible_httpapi_port: 443 ansible_httpapi_use_ssl: true ansible_httpapi_validate_certs: false - ansible_zabbix_url_path: 'zabbixeu' # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu + ansible_zabbix_url_path: "zabbixeu" # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu ansible_host: zabbix-example-fqdn.org community.zabbix.zabbix_user_directory: state: present @@ -414,7 +415,7 @@ EXAMPLES = r''' role: Guest role user_groups: - Guests -''' +""" from ansible.module_utils.basic import AnsibleModule @@ -426,220 +427,305 @@ import ansible_collections.community.zabbix.plugins.module_utils.helpers as zabb def main(): argument_spec = zabbix_utils.zabbix_common_argument_spec() - argument_spec.update(dict( - name=dict(type='str', required=True), - idp_type=dict(type='str', required=False, choices=['ldap', 'saml']), - host=dict(type='str', required=False), - port=dict(type='int', required=False), - base_dn=dict(type='str', required=False), - search_attribute=dict(type='str', required=False), - bind_dn=dict(type='str', required=False, default=''), - bind_password=dict(type='str', required=False, no_log=True), - description=dict(type='str', required=False, default=''), - search_filter=dict(type='str', default='(%{attr}=%{user})', required=False), - start_tls=dict(type='int', required=False, choices=[0, 1], default=0), - idp_entityid=dict(type='str', required=False), - sp_entityid=dict(type='str', required=False), - sso_url=dict(type='str', required=False), - slo_url=dict(type='str', required=False), - username_attribute=dict(type='str', required=False), - nameid_format=dict(type='str', required=False), - scim_status=dict(type='bool', required=False, default=False), - encrypt_nameid=dict(type='bool', required=False, default=False), - encrypt_assertions=dict(type='bool', required=False, default=False), - sign_messages=dict(type='bool', required=False, default=False), - sign_assertions=dict(type='bool', required=False, default=False), - sign_authn_requests=dict(type='bool', required=False, default=False), - sign_logout_requests=dict(type='bool', required=False, default=False), - sign_logout_responses=dict(type='bool', required=False, default=False), - provision_status=dict(type='bool', required=False, default=False), - group_basedn=dict(type='str', required=False), - group_filter=dict(type='str', required=False), - group_member=dict(type='str', required=False), - group_membership=dict(type='str', required=False), - group_name=dict(type='str', required=False), - user_lastname=dict(type='str', required=False), - user_ref_attr=dict(type='str', required=False), - user_username=dict(type='str', required=False), - provision_media=dict( - type='list', - required=False, - elements='dict', - options=dict( - name=dict(type='str', required=True), - mediatype=dict(type='str', required=True), - attribute=dict(type='str', required=True) - ) - ), - provision_groups=dict( - type='list', - required=False, - elements='dict', - options=dict( - name=dict(type='str', required=True), - role=dict(type='str', required=True), - user_groups=dict(type='list', elements='str', required=True) - ) - ), - state=dict(type='str', default='present', choices=['present', 'absent']) - )) - - module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True + argument_spec.update( + dict( + name=dict(type="str", required=True), + idp_type=dict(type="str", required=False, choices=["ldap", "saml"]), + host=dict(type="str", required=False), + port=dict(type="int", required=False), + base_dn=dict(type="str", required=False), + search_attribute=dict(type="str", required=False), + bind_dn=dict(type="str", required=False, default=""), + bind_password=dict(type="str", required=False, no_log=True), + description=dict(type="str", required=False, default=""), + search_filter=dict(type="str", default="(%{attr}=%{user})", required=False), + start_tls=dict(type="int", required=False, choices=[0, 1], default=0), + idp_entityid=dict(type="str", required=False), + sp_entityid=dict(type="str", required=False), + sso_url=dict(type="str", required=False), + slo_url=dict(type="str", required=False), + username_attribute=dict(type="str", required=False), + nameid_format=dict(type="str", required=False), + scim_status=dict(type="bool", required=False, default=False), + encrypt_nameid=dict(type="bool", required=False, default=False), + encrypt_assertions=dict(type="bool", required=False, default=False), + sign_messages=dict(type="bool", required=False, default=False), + sign_assertions=dict(type="bool", required=False, default=False), + sign_authn_requests=dict(type="bool", required=False, default=False), + sign_logout_requests=dict(type="bool", required=False, default=False), + sign_logout_responses=dict(type="bool", required=False, default=False), + provision_status=dict(type="bool", required=False, default=False), + group_basedn=dict(type="str", required=False), + group_filter=dict(type="str", required=False), + group_member=dict(type="str", required=False), + group_membership=dict(type="str", required=False), + group_name=dict(type="str", required=False), + user_lastname=dict(type="str", required=False), + user_ref_attr=dict(type="str", required=False), + user_username=dict(type="str", required=False), + provision_media=dict( + type="list", + required=False, + elements="dict", + options=dict( + name=dict(type="str", required=True), + mediatype=dict(type="str", required=True), + attribute=dict(type="str", required=True), + ), + ), + provision_groups=dict( + type="list", + required=False, + elements="dict", + options=dict( + name=dict(type="str", required=True), + role=dict(type="str", required=True), + user_groups=dict(type="list", elements="str", required=True), + ), + ), + state=dict(type="str", default="present", choices=["present", "absent"]), + ) ) - ''' For future when < 6.4 disappears we should use this, now we cannot do this as at this point Zabbix version is unknown + + module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True) + """ For future when < 6.4 disappears we should use this, now we cannot do this as at this point Zabbix version is unknown module = AnsibleModule( argument_spec=argument_spec, supports_check_mode=True, required_if=[ - ('state', 'present', ('idp_type',)), - ('idp_type', 'ldap', ('host', 'port', 'base_dn', 'search_attribute'), False), - ('idp_type', 'saml', ('idp_entityid', 'sp_entityid', 'sso_url', 'username_attribute'), False), - ('provision_status', 'true', ('provision_groups')) + ("state", "present", ("idp_type",)), + ("idp_type", "ldap", ("host", "port", "base_dn", "search_attribute"), False), + ("idp_type", "saml", ("idp_entityid", "sp_entityid", "sso_url", "username_attribute"), False), + ("provision_status", "true", ("provision_groups")) ] ) - ''' - - zabbix_utils.require_creds_params(module) - - for p in ['server_url', 'login_user', 'login_password', 'timeout', 'validate_certs']: - if p in module.params and not module.params[p] is None: - module.warn('Option "%s" is deprecated with the move to httpapi connection and will be removed in the next release' % p) - - parameters = { - 'name': module.params['name'] - } - for p in ['host', 'port', 'base_dn', 'search_attribute', 'bind_dn', 'bind_password', 'description', 'start_tls']: + """ + + parameters = {"name": module.params["name"]} + for p in [ + "host", + "port", + "base_dn", + "search_attribute", + "bind_dn", + "bind_password", + "description", + "start_tls", + ]: if module.params[p]: - if p in ['port', 'start_tls']: + if p in ["port", "start_tls"]: parameters[p] = str(module.params[p]) else: parameters[p] = module.params[p] - state = module.params['state'] + state = module.params["state"] user_directory = ZabbixBase(module) - if LooseVersion(user_directory._zbx_api_version) < LooseVersion('6.2'): - module.fail_json(msg='Zabbix < 6.2 does not support user directories.') + if LooseVersion(user_directory._zbx_api_version) < LooseVersion("6.2"): + module.fail_json(msg="Zabbix < 6.2 does not support user directories.") - if LooseVersion(user_directory._zbx_api_version) < LooseVersion('6.4'): - parameters['search_filter'] = module.params['search_filter'] - directory = user_directory._zapi.userdirectory.get({'filter': {'name': parameters['name']}}) + if LooseVersion(user_directory._zbx_api_version) < LooseVersion("6.4"): + parameters["search_filter"] = module.params["search_filter"] + directory = user_directory._zapi.userdirectory.get( + {"filter": {"name": parameters["name"]}} + ) else: # Zabbix >= 6.4 # Mandatory parameters check - if state == 'present' and not module.params['idp_type']: - module.fail_json('"idp_type" parameter must be provided when state is "present"') - if module.params['idp_type']: - if (module.params['idp_type'] == 'ldap' - and (not module.params['host'] or not module.params['port'] or not module.params['base_dn'] or not module.params['search_attribute'])): - module.fail_json('"host", "port", "base_dn", "search_attribute" must be provided when idp_type is "ldap"') - if (module.params['idp_type'] == 'saml' - and (not module.params['idp_entityid'] or not module.params['sp_entityid'] - or not module.params['sso_url'] or not module.params['username_attribute'])): - module.fail_json('"idp_entityid", "sp_entityid", "sso_url", "username_attribute" must be provided when idp_type is "ldap"') + if state == "present" and not module.params["idp_type"]: + module.fail_json( + "'idp_type' parameter must be provided when state is 'present'" + ) + if module.params["idp_type"]: + if module.params["idp_type"] == "ldap" and ( + not module.params["host"] + or not module.params["port"] + or not module.params["base_dn"] + or not module.params["search_attribute"] + ): + module.fail_json( + "'host', 'port', 'base_dn', 'search_attribute' must be provided when idp_type is 'ldap'" + ) + if module.params["idp_type"] == "saml" and ( + not module.params["idp_entityid"] + or not module.params["sp_entityid"] + or not module.params["sso_url"] + or not module.params["username_attribute"] + ): + module.fail_json( + "'idp_entityid', 'sp_entityid', 'sso_url', 'username_attribute' must be provided when idp_type is 'ldap'" + ) directory = user_directory._zapi.userdirectory.get( { - 'search': {'name': parameters['name']}, - 'selectProvisionMedia': 'extend', - 'selectProvisionGroups': 'extend' - }) - parameters['idp_type'] = str(zabbix_utils.helper_to_numeric_value(['', 'ldap', 'saml'], module.params['idp_type'])) - if parameters['idp_type'] == '1': + "search": {"name": parameters["name"]}, + "selectProvisionMedia": "extend", + "selectProvisionGroups": "extend", + } + ) + parameters["idp_type"] = str( + zabbix_utils.helper_to_numeric_value( + ["", "ldap", "saml"], module.params["idp_type"] + ) + ) + if parameters["idp_type"] == "1": # idp_type is ldap - parameters['search_filter'] = module.params['search_filter'] - elif parameters['idp_type'] == '2': + parameters["search_filter"] = module.params["search_filter"] + elif parameters["idp_type"] == "2": # idp_type is saml - for p in ['idp_entityid', 'sso_url', 'username_attribute', 'sp_entityid', 'slo_url', 'nameid_format']: + for p in [ + "idp_entityid", + "sso_url", + "username_attribute", + "sp_entityid", + "slo_url", + "nameid_format", + ]: # str parameters if module.params[p]: parameters[p] = module.params[p] - for p in ['scim_status', 'encrypt_nameid', 'encrypt_assertions', 'sign_messages', 'sign_assertions', - 'sign_authn_requests', 'sign_logout_requests', 'sign_logout_responses']: + for p in [ + "scim_status", + "encrypt_nameid", + "encrypt_assertions", + "sign_messages", + "sign_assertions", + "sign_authn_requests", + "sign_logout_requests", + "sign_logout_responses", + ]: # boolean parameters if module.params[p]: parameters[p] = str(int(module.params[p])) - if module.params['provision_status']: - parameters['provision_status'] = int(module.params['provision_status']) + if module.params["provision_status"]: + parameters["provision_status"] = int(module.params["provision_status"]) - if module.params['provision_media']: - if 'provision_status' not in parameters or not parameters['provision_status']: - module.fail_json('"provision_status" must be True to define "provision_media"') - parameters['provision_media'] = [] - for media in module.params['provision_media']: - media_type_name = media['mediatype'] - media_type_ids = user_directory._zapi.mediatype.get({'filter': {'name': media_type_name}}) + if module.params["provision_media"]: + if ( + "provision_status" not in parameters + or not parameters["provision_status"] + ): + module.fail_json( + "'provision_status' must be True to define 'provision_media'" + ) + parameters["provision_media"] = [] + for media in module.params["provision_media"]: + media_type_name = media["mediatype"] + media_type_ids = user_directory._zapi.mediatype.get( + {"filter": {"name": media_type_name}} + ) if not media_type_ids: - module.fail_json('Mediatype "%s" cannot be found' % media_type_name) - parameters['provision_media'].append( + module.fail_json("Mediatype '%s' cannot be found" % media_type_name) + parameters["provision_media"].append( { - 'name': media['name'], - 'mediatypeid': media_type_ids[0]['mediatypeid'], - 'attribute': media['attribute'] + "name": media["name"], + "mediatypeid": media_type_ids[0]["mediatypeid"], + "attribute": media["attribute"], } ) - if module.params['provision_groups']: - if 'provision_status' not in parameters or not parameters['provision_status']: - module.fail_json('"provision_status" must be True to define "provision_groups"') - parameters['provision_groups'] = [] - for group in module.params['provision_groups']: - role_name = group['role'] - role_ids = user_directory._zapi.role.get({'filter': {'name': role_name}}) + if module.params["provision_groups"]: + if ( + "provision_status" not in parameters + or not parameters["provision_status"] + ): + module.fail_json( + "'provision_status' must be True to define 'provision_groups'" + ) + parameters["provision_groups"] = [] + for group in module.params["provision_groups"]: + role_name = group["role"] + role_ids = user_directory._zapi.role.get( + {"filter": {"name": role_name}} + ) if not role_ids: - module.fail_json('Role "%s" cannot be found' % role_name) + module.fail_json("Role '%s' cannot be found" % role_name) user_groups = [] - for user_group in group['user_groups']: - ug_ids = user_directory._zapi.usergroup.get({'filter': {'name': user_group}}) + for user_group in group["user_groups"]: + ug_ids = user_directory._zapi.usergroup.get( + {"filter": {"name": user_group}} + ) if not ug_ids: - module.fail_json('User group "%s" cannot be found' % user_group) - user_groups.append({'usrgrpid': ug_ids[0]['usrgrpid']}) - parameters['provision_groups'].append( + module.fail_json("User group '%s' cannot be found" % user_group) + user_groups.append({"usrgrpid": ug_ids[0]["usrgrpid"]}) + parameters["provision_groups"].append( { - 'name': group['name'], - 'roleid': role_ids[0]['roleid'], - 'user_groups': user_groups + "name": group["name"], + "roleid": role_ids[0]["roleid"], + "user_groups": user_groups, } ) - for p in ['group_basedn', 'group_filter', 'group_member', 'group_membership', 'group_name', 'group_name', - 'user_lastname', 'user_ref_attr', 'user_username']: + for p in [ + "group_basedn", + "group_filter", + "group_member", + "group_membership", + "group_name", + "group_name", + "user_lastname", + "user_ref_attr", + "user_username", + ]: if module.params[p]: parameters[p] = module.params[p] if not directory: # No User Directory found with given name - if state == 'absent': - module.exit_json(changed=False, msg='User directory not found. Not changed: %s' % parameters['name']) + if state == "absent": + module.exit_json( + changed=False, + msg="User directory not found. Not changed: %s" % parameters["name"], + ) - elif state == 'present': + elif state == "present": if module.check_mode: module.exit_json(changed=True) else: user_directory._zapi.userdirectory.create(parameters) - module.exit_json(changed=True, result='Successfully added user directory %s' % parameters['name']) + module.exit_json( + changed=True, + result="Successfully added user directory %s" % parameters["name"], + ) else: # User Directory with given name exists - if state == 'absent': - user_directory._zapi.userdirectory.delete([directory[0]['userdirectoryid']]) - module.exit_json(changed=True, result='Successfully deleted user directory %s' % parameters['name']) - elif state == 'present': + if state == "absent": + if module.check_mode: + module.exit_json(changed=True) + user_directory._zapi.userdirectory.delete([directory[0]["userdirectoryid"]]) + module.exit_json( + changed=True, + result="Successfully deleted user directory %s" % parameters["name"], + ) + elif state == "present": diff_dict = {} - if 'provision_status' in directory[0]: + if "provision_status" in directory[0]: # Zabbix API returns provision_status as str we need it as int to correctly compare - directory[0]['provision_status'] = int(directory[0]['provision_status']) - if zabbix_utils.helper_compare_dictionaries(parameters, directory[0], diff_dict): - parameters['userdirectoryid'] = directory[0]['userdirectoryid'] + directory[0]["provision_status"] = int(directory[0]["provision_status"]) + if zabbix_utils.helper_compare_dictionaries( + parameters, directory[0], diff_dict + ): + parameters["userdirectoryid"] = directory[0]["userdirectoryid"] + if module.check_mode: + module.exit_json(changed=True) user_directory._zapi.userdirectory.update(parameters) - module.exit_json(changed=True, result='Successfully updated user directory %s' % parameters['name']) + module.exit_json( + changed=True, + result="Successfully updated user directory %s" + % parameters["name"], + ) else: - module.exit_json(changed=False, result='User directory %s is up-to date' % parameters['name']) + module.exit_json( + changed=False, + result="User directory %s is up-to date" % parameters["name"], + ) - module.exit_json(changed=False, result='User directory %s is up-to date' % parameters['name']) + module.exit_json( + changed=False, + result="User directory %s is up-to date" % parameters["name"], + ) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/ansible_collections/community/zabbix/plugins/modules/zabbix_user_info.py b/ansible_collections/community/zabbix/plugins/modules/zabbix_user_info.py index 8fd0323c4..c71479532 100644 --- a/ansible_collections/community/zabbix/plugins/modules/zabbix_user_info.py +++ b/ansible_collections/community/zabbix/plugins/modules/zabbix_user_info.py @@ -8,7 +8,7 @@ from __future__ import absolute_import, division, print_function __metaclass__ = type -DOCUMENTATION = ''' +DOCUMENTATION = """ module: zabbix_user_info short_description: Gather information about Zabbix user author: @@ -16,32 +16,30 @@ author: description: - This module allows you to search for Zabbix user entries. requirements: - - "python >= 2.6" + - "python >= 3.9" options: username: description: - - Name of the user alias in Zabbix. - - username is the unique identifier used and cannot be updated using this module. - - alias should be replaced with username - aliases: [ alias ] + - User name. + - sername is the unique identifier used and cannot be updated using this module. required: true type: str extends_documentation_fragment: - community.zabbix.zabbix -''' +""" -EXAMPLES = ''' +EXAMPLES = """ # If you want to use Username and Password to be authenticated by Zabbix Server - name: Set credentials to access Zabbix Server API - set_fact: + ansible.builtin.set_fact: ansible_user: Admin ansible_httpapi_pass: zabbix # If you want to use API token to be authenticated by Zabbix Server # https://www.zabbix.com/documentation/current/en/manual/web_interface/frontend_sections/administration/general#api-tokens - name: Set API token - set_fact: + ansible.builtin.set_fact: ansible_zabbix_auth_key: 8ec0d52432c15c91fcafe9888500cf9a607f44091ab554dbee860f6b44fac895 - name: Get zabbix user info @@ -52,13 +50,13 @@ EXAMPLES = ''' ansible_httpapi_port: 443 ansible_httpapi_use_ssl: true ansible_httpapi_validate_certs: false - ansible_zabbix_url_path: 'zabbixeu' # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu + ansible_zabbix_url_path: "zabbixeu" # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu ansible_host: zabbix-example-fqdn.org community.zabbix.zabbix_user_info: username: example -''' +""" -RETURN = ''' +RETURN = """ zabbix_user: description: example returned: always @@ -103,11 +101,10 @@ zabbix_user: } ] } -''' +""" from ansible.module_utils.basic import AnsibleModule -from ansible.module_utils.compat.version import LooseVersion from ansible_collections.community.zabbix.plugins.module_utils.base import ZabbixBase import ansible_collections.community.zabbix.plugins.module_utils.helpers as zabbix_utils @@ -117,13 +114,10 @@ class User(ZabbixBase): def get_user_by_user_username(self, username): zabbix_user = "" try: - data = {'output': 'extend', 'filter': {}, - 'getAccess': True, 'selectMedias': 'extend', - 'selectUsrgrps': 'extend'} - if LooseVersion(self._zbx_api_version) >= LooseVersion('5.4'): - data['filter']['username'] = username - else: - data['filter']['alias'] = username + data = {"output": "extend", "filter": {}, + "getAccess": True, "selectMedias": "extend", + "selectUsrgrps": "extend"} + data["filter"]["username"] = username zabbix_user = self._zapi.user.get(data) except Exception as e: @@ -141,20 +135,14 @@ class User(ZabbixBase): def main(): argument_spec = zabbix_utils.zabbix_common_argument_spec() argument_spec.update(dict( - username=dict(type='str', required=True, aliases=['alias']), + username=dict(type="str", required=True), )) module = AnsibleModule( argument_spec=argument_spec, supports_check_mode=True ) - zabbix_utils.require_creds_params(module) - - for p in ['server_url', 'login_user', 'login_password', 'timeout', 'validate_certs']: - if p in module.params and not module.params[p] is None: - module.warn('Option "%s" is deprecated with the move to httpapi connection and will be removed in the next release' % p) - - username = module.params['username'] + username = module.params["username"] user = User(module) zabbix_user = user.get_user_by_user_username(username) diff --git a/ansible_collections/community/zabbix/plugins/modules/zabbix_user_role.py b/ansible_collections/community/zabbix/plugins/modules/zabbix_user_role.py index 596ef5570..3495f39e1 100644 --- a/ansible_collections/community/zabbix/plugins/modules/zabbix_user_role.py +++ b/ansible_collections/community/zabbix/plugins/modules/zabbix_user_role.py @@ -8,7 +8,7 @@ from __future__ import (absolute_import, division, print_function) __metaclass__ = type -DOCUMENTATION = r''' +DOCUMENTATION = r""" module: zabbix_user_role short_description: Adds or removes zabbix roles author: @@ -16,15 +16,15 @@ author: description: - This module adds or removes zabbix roles requirements: - - "python >= 2.6" + - "python >= 3.9" options: state: description: - State of the user_role. - On C(present), it will create if user_role does not exist or update the user_role if the associated data is different. - On C(absent) will remove a user_role if it exists. - default: 'present' - choices: ['present', 'absent'] + default: "present" + choices: ["present", "absent"] type: str required: false name: @@ -47,19 +47,19 @@ options: required: false extends_documentation_fragment: - community.zabbix.zabbix -''' +""" -EXAMPLES = r''' +EXAMPLES = r""" # If you want to use Username and Password to be authenticated by Zabbix Server - name: Set credentials to access Zabbix Server API - set_fact: + ansible.builtin.set_fact: ansible_user: Admin ansible_httpapi_pass: zabbix # If you want to use API token to be authenticated by Zabbix Server # https://www.zabbix.com/documentation/current/en/manual/web_interface/frontend_sections/administration/general#api-tokens - name: Set API token - set_fact: + ansible.builtin.set_fact: ansible_zabbix_auth_key: 8ec0d52432c15c91fcafe9888500cf9a607f44091ab554dbee860f6b44fac895 # Create user role Operators with ui elements monitoring.hosts @@ -73,7 +73,7 @@ EXAMPLES = r''' ansible_httpapi_port: 443 ansible_httpapi_use_ssl: true ansible_httpapi_validate_certs: false - ansible_zabbix_url_path: 'zabbixeu' # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu + ansible_zabbix_url_path: "zabbixeu" # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu ansible_host: zabbix-example-fqdn.org community.zabbix.zabbix_user_role: state: present @@ -86,21 +86,21 @@ EXAMPLES = r''' status: 0 - name: "monitoring.maps" status: 1 -''' +""" -RETURN = r''' +RETURN = r""" # Return values msg: description: The result of the action type: str returned: always - sample: 'No action' + sample: "No action" changed: description: The consequence of the action type: bool returned: always sample: false -''' +""" from ansible.module_utils.basic import AnsibleModule @@ -135,7 +135,7 @@ class UserRole(ZabbixBase): verdict = True for rule, value in inp.items(): if not isinstance(value, list): - verdict = verdict and self.__find_val(out.get(rule, ''), value) + verdict = verdict and self.__find_val(out.get(rule, ""), value) else: if len(value): if not isinstance(value[0], dict): @@ -162,10 +162,10 @@ def main(): argument_spec = zabbix_utils.zabbix_common_argument_spec() argument_spec.update(dict( - state=dict(type='str', required=False, default='present', choices=['present', 'absent']), - name=dict(type='str', required=True), - type=dict(type='str', required=False, choices=["User", "Admin", "Super Admin"], default='User'), - rules=dict(type='dict', required=False, default={}), + state=dict(type="str", required=False, default="present", choices=["present", "absent"]), + name=dict(type="str", required=True), + type=dict(type="str", required=False, choices=["User", "Admin", "Super Admin"], default="User"), + rules=dict(type="dict", required=False, default={}), )) # the AnsibleModule object @@ -174,19 +174,12 @@ def main(): supports_check_mode=False ) - zabbix_utils.require_creds_params(module) - - for p in ['server_url', 'login_user', 'login_password', 'timeout', 'validate_certs']: - if p in module.params and not module.params[p] is None: - module.warn('Option "%s" is deprecated with the move to httpapi connection ' - 'and will be removed in the next release' % p) - - state = module.params['state'] - name = module.params['name'] + state = module.params["state"] + name = module.params["name"] type = zabbix_utils.helper_to_numeric_value( - ['', 'user', 'admin', 'super admin'], module.params['type'].lower() + ["", "user", "admin", "super admin"], module.params["type"].lower() ) - rules = module.params['rules'] + rules = module.params["rules"] user_role = UserRole(module) @@ -194,19 +187,19 @@ def main(): if result: if len(result) == 1: role = result[0] - if role['readonly'] != 1: - roleid = role['roleid'] - if state == 'absent': + if role["readonly"] != 1: + roleid = role["roleid"] + if state == "absent": result = user_role._zapi.role.delete([f"{roleid}"]) changed = True msg = "Role deleted" else: - if not user_role.is_part_of(rules, role['rules']): + if not user_role.is_part_of(rules, role["rules"]): result = user_role._zapi.role.update({"roleid": roleid, "rules": rules}) changed = True msg = "Role updated" else: - module.fail_json(msg='Too many role matches') + module.fail_json(msg="Too many role matches") else: user_role._zapi.role.create({ "name": name, @@ -219,5 +212,5 @@ def main(): module.exit_json(msg=msg, changed=changed) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/ansible_collections/community/zabbix/plugins/modules/zabbix_usergroup.py b/ansible_collections/community/zabbix/plugins/modules/zabbix_usergroup.py index b2a341472..14539d932 100644 --- a/ansible_collections/community/zabbix/plugins/modules/zabbix_usergroup.py +++ b/ansible_collections/community/zabbix/plugins/modules/zabbix_usergroup.py @@ -5,9 +5,10 @@ # 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 + __metaclass__ = type -DOCUMENTATION = r''' +DOCUMENTATION = r""" --- module: zabbix_usergroup short_description: Create/delete/update Zabbix user groups @@ -18,7 +19,7 @@ description: author: - "Tobias Birkefeld (@tcraxs)" requirements: - - "python >= 2.6" + - "python >= 3.9" options: name: description: @@ -126,13 +127,13 @@ options: - Tag name. required: false type: str - default: '' + default: "" value: description: - Tag value. required: false type: str - default: '' + default: "" userdirectory: description: - Authentication user directory when gui_access set to LDAP or System default. @@ -148,23 +149,22 @@ options: type: str default: "present" choices: [ "present", "absent" ] -notes: - - Only Zabbix >= 4.0 is supported. + extends_documentation_fragment: - community.zabbix.zabbix -''' +""" -EXAMPLES = r''' +EXAMPLES = r""" # If you want to use Username and Password to be authenticated by Zabbix Server - name: Set credentials to access Zabbix Server API - set_fact: + ansible.builtin.set_fact: ansible_user: Admin ansible_httpapi_pass: zabbix # If you want to use API token to be authenticated by Zabbix Server # https://www.zabbix.com/documentation/current/en/manual/web_interface/frontend_sections/administration/general#api-tokens - name: Set API token - set_fact: + ansible.builtin.set_fact: ansible_zabbix_auth_key: 8ec0d52432c15c91fcafe9888500cf9a607f44091ab554dbee860f6b44fac895 # Base create user group example @@ -176,10 +176,11 @@ EXAMPLES = r''' ansible_httpapi_port: 443 ansible_httpapi_use_ssl: true ansible_httpapi_validate_certs: false - ansible_zabbix_url_path: 'zabbixeu' # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu + ansible_zabbix_url_path: "zabbixeu" # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu ansible_host: zabbix-example-fqdn.org community.zabbix.zabbix_usergroup: name: ACME + userdirectory: LDAP infra 1 state: present # Base create user group with selected user directory for LDAP authentication @@ -191,7 +192,7 @@ EXAMPLES = r''' ansible_httpapi_port: 443 ansible_httpapi_use_ssl: true ansible_httpapi_validate_certs: false - ansible_zabbix_url_path: 'zabbixeu' # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu + ansible_zabbix_url_path: "zabbixeu" # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu ansible_host: zabbix-example-fqdn.org community.zabbix.zabbix_usergroup: name: ACME @@ -207,7 +208,7 @@ EXAMPLES = r''' ansible_httpapi_port: 443 ansible_httpapi_use_ssl: true ansible_httpapi_validate_certs: false - ansible_zabbix_url_path: 'zabbixeu' # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu + ansible_zabbix_url_path: "zabbixeu" # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu ansible_host: zabbix-example-fqdn.org community.zabbix.zabbix_usergroup: name: ACME @@ -222,7 +223,7 @@ EXAMPLES = r''' ansible_httpapi_port: 443 ansible_httpapi_use_ssl: true ansible_httpapi_validate_certs: false - ansible_zabbix_url_path: 'zabbixeu' # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu + ansible_zabbix_url_path: "zabbixeu" # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu ansible_host: zabbix-example-fqdn.org community.zabbix.zabbix_usergroup: name: ACME @@ -242,7 +243,7 @@ EXAMPLES = r''' ansible_httpapi_port: 443 ansible_httpapi_use_ssl: true ansible_httpapi_validate_certs: false - ansible_zabbix_url_path: 'zabbixeu' # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu + ansible_zabbix_url_path: "zabbixeu" # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu ansible_host: zabbix-example-fqdn.org community.zabbix.zabbix_usergroup: name: ACME @@ -267,7 +268,7 @@ EXAMPLES = r''' ansible_httpapi_port: 443 ansible_httpapi_use_ssl: true ansible_httpapi_validate_certs: false - ansible_zabbix_url_path: 'zabbixeu' # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu + ansible_zabbix_url_path: "zabbixeu" # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu ansible_host: zabbix-example-fqdn.org community.zabbix.zabbix_usergroup: name: ACME @@ -289,35 +290,35 @@ EXAMPLES = r''' ansible_httpapi_port: 443 ansible_httpapi_use_ssl: true ansible_httpapi_validate_certs: false - ansible_zabbix_url_path: 'zabbixeu' # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu + ansible_zabbix_url_path: "zabbixeu" # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu ansible_host: zabbix-example-fqdn.org community.zabbix.zabbix_usergroup: name: ACME state: absent -''' +""" -RETURN = r''' +RETURN = r""" state: description: User group state at the end of execution. returned: on success type: str - sample: 'present' + sample: "present" usergroup: description: User group name. returned: on success type: str - sample: 'ACME' + sample: "ACME" usrgrpid: description: User group id, if created, changed or deleted. returned: on success type: str - sample: '42' + sample: "42" msg: description: The result of the operation returned: always type: str - sample: 'User group created: ACME, ID: 42' -''' + sample: "User group created: ACME, ID: 42" +""" from ansible.module_utils.basic import AnsibleModule @@ -341,16 +342,15 @@ class Rights(ZabbixBase): host group matching host group name. """ try: - _hostgroup = self._zapi.hostgroup.get({ - 'output': 'extend', - 'filter': {'name': [name]} - }) + _hostgroup = self._zapi.hostgroup.get( + {"output": "extend", "filter": {"name": [name]}} + ) if len(_hostgroup) < 1: - self._module.fail_json(msg='Host group not found: %s' % name) + self._module.fail_json(msg="Host group not found: %s" % name) else: return _hostgroup[0] except Exception as e: - self._module.fail_json(msg='Failed to get host group "%s": %s' % (name, e)) + self._module.fail_json(msg="Failed to get host group '%s': %s" % (name, e)) def construct_the_data(self, _rights): """Construct the user defined rights to fit the Zabbix API requirements @@ -366,13 +366,12 @@ class Rights(ZabbixBase): constructed_data = [] for right in _rights: constructed_right = { - 'id': self.get_hostgroup_by_hostgroup_name(right.get('host_group'))['groupid'], - 'permission': zabbix_utils.helper_to_numeric_value([ - 'denied', - None, - 'read-only', - 'read-write'], right.get('permission') - ) + "id": self.get_hostgroup_by_hostgroup_name(right.get("host_group"))[ + "groupid" + ], + "permission": zabbix_utils.helper_to_numeric_value( + ["denied", None, "read-only", "read-write"], right.get("permission") + ), } constructed_data.append(constructed_right) return zabbix_utils.helper_cleanup_data(constructed_data) @@ -394,15 +393,15 @@ class HostgroupRights(ZabbixBase): """ try: _hostgroup = self._zapi.hostgroup.get({ - 'output': 'extend', - 'filter': {'name': [name]} + "output": "extend", + "filter": {"name": [name]} }) if len(_hostgroup) < 1: - self._module.fail_json(msg='Host group not found: %s' % name) + self._module.fail_json(msg="Host group not found: %s" % name) else: return _hostgroup[0] except Exception as e: - self._module.fail_json(msg='Failed to get host group "%s": %s' % (name, e)) + self._module.fail_json(msg="Failed to get host group '%s': %s" % (name, e)) def construct_the_data(self, _rights): """Construct the user defined host group rights to fit the Zabbix API requirements @@ -418,12 +417,12 @@ class HostgroupRights(ZabbixBase): constructed_data = [] for right in _rights: constructed_right = { - 'id': self.get_hostgroup_by_hostgroup_name(right.get('host_group'))['groupid'], - 'permission': zabbix_utils.helper_to_numeric_value([ - 'denied', + "id": self.get_hostgroup_by_hostgroup_name(right.get("host_group"))["groupid"], + "permission": zabbix_utils.helper_to_numeric_value([ + "denied", None, - 'read-only', - 'read-write'], right.get('permission') + "read-only", + "read-write"], right.get("permission") ) } constructed_data.append(constructed_right) @@ -446,15 +445,15 @@ class TemplategroupRights(ZabbixBase): """ try: _templategroup = self._zapi.templategroup.get({ - 'output': 'extend', - 'filter': {'name': [name]} + "output": "extend", + "filter": {"name": [name]} }) if len(_templategroup) < 1: - self._module.fail_json(msg='Template group not found: %s' % name) + self._module.fail_json(msg="Template group not found: %s" % name) else: return _templategroup[0] except Exception as e: - self._module.fail_json(msg='Failed to get template group "%s": %s' % (name, e)) + self._module.fail_json(msg="Failed to get template group '%s': %s" % (name, e)) def construct_the_data(self, _rights): """Construct the user defined template rights to fit the Zabbix API requirements @@ -470,12 +469,12 @@ class TemplategroupRights(ZabbixBase): constructed_data = [] for right in _rights: constructed_right = { - 'id': self.get_templategroup_by_templategroup_name(right.get('template_group'))['groupid'], - 'permission': zabbix_utils.helper_to_numeric_value([ - 'denied', + "id": self.get_templategroup_by_templategroup_name(right.get("template_group"))["groupid"], + "permission": zabbix_utils.helper_to_numeric_value([ + "denied", None, - 'read-only', - 'read-write'], right.get('permission') + "read-only", + "read-write"], right.get("permission") ) } constructed_data.append(constructed_right) @@ -501,9 +500,11 @@ class TagFilters(Rights): constructed_data = [] for tag_filter in _tag_filters: constructed_tag_filter = { - 'groupid': self.get_hostgroup_by_hostgroup_name(tag_filter.get('host_group'))['groupid'], - 'tag': tag_filter.get('tag'), - 'value': tag_filter.get('value') + "groupid": self.get_hostgroup_by_hostgroup_name( + tag_filter.get("host_group") + )["groupid"], + "tag": tag_filter.get("tag"), + "value": tag_filter.get("value"), } constructed_data.append(constructed_tag_filter) return zabbix_utils.helper_cleanup_data(constructed_data) @@ -520,46 +521,50 @@ class UserGroup(ZabbixBase): dict: dictionary of specified parameters """ _params = { - 'name': kwargs['name'], - 'gui_access': zabbix_utils.helper_to_numeric_value([ - 'default', - 'internal', - 'LDAP', - 'disable'], kwargs['gui_access'] + "name": kwargs["name"], + "gui_access": zabbix_utils.helper_to_numeric_value( + ["default", "internal", "LDAP", "disable"], kwargs["gui_access"] ), - 'debug_mode': zabbix_utils.helper_to_numeric_value([ - 'disabled', - 'enabled'], kwargs['debug_mode'] + "debug_mode": zabbix_utils.helper_to_numeric_value( + ["disabled", "enabled"], kwargs["debug_mode"] ), - 'users_status': zabbix_utils.helper_to_numeric_value([ - 'enabled', - 'disabled'], kwargs['status'] + "users_status": zabbix_utils.helper_to_numeric_value( + ["enabled", "disabled"], kwargs["status"] ), - 'tag_filters': kwargs['tag_filters'] + "tag_filters": kwargs["tag_filters"], } - if LooseVersion(self._zbx_api_version) < LooseVersion('6.2'): - _params['rights'] = kwargs['rights'] + if LooseVersion(self._zbx_api_version) < LooseVersion("6.2"): + _params["rights"] = kwargs["rights"] else: - _params['hostgroup_rights'] = kwargs['hostgroup_rights'] - _params['templategroup_rights'] = kwargs['templategroup_rights'] + _params["hostgroup_rights"] = kwargs["hostgroup_rights"] + _params["templategroup_rights"] = kwargs["templategroup_rights"] - if kwargs['userdirectory']: + if kwargs["userdirectory"]: try: - if LooseVersion(self._zbx_api_version) <= LooseVersion('6.2'): - _userdir = self._zapi.userdirectory.get({ - 'output': 'extend', - 'filter': {'name': [kwargs['userdirectory']]} - }) + if LooseVersion(self._zbx_api_version) <= LooseVersion("6.2"): + _userdir = self._zapi.userdirectory.get( + { + "output": "extend", + "filter": {"name": [kwargs["userdirectory"]]}, + } + ) else: - _userdir = self._zapi.userdirectory.get({ - 'output': 'extend', - 'search': {'name': [kwargs['userdirectory']]} - }) + _userdir = self._zapi.userdirectory.get( + { + "output": "extend", + "search": {"name": [kwargs["userdirectory"]]}, + } + ) except Exception as e: - self._module.fail_json(msg='Failed to get user directory "%s": %s' % (kwargs['userdirectory'], e)) + self._module.fail_json( + msg="Failed to get user directory '%s': %s" + % (kwargs["userdirectory"], e) + ) if len(_userdir) == 0: - self._module.fail_json(msg='User directory "%s" not found' % kwargs['userdirectory']) - _params['userdirectoryid'] = _userdir[0]['userdirectoryid'] + self._module.fail_json( + msg="User directory '%s' not found" % kwargs["userdirectory"] + ) + _params["userdirectoryid"] = _userdir[0]["userdirectoryid"] return _params @@ -573,14 +578,15 @@ class UserGroup(ZabbixBase): The return value. True for success, False otherwise. """ try: - _usergroup = self._zapi.usergroup.get({ - 'output': 'extend', - 'filter': {'name': [name]} - }) + _usergroup = self._zapi.usergroup.get( + {"output": "extend", "filter": {"name": [name]}} + ) if len(_usergroup) > 0: return _usergroup except Exception as e: - self._module.fail_json(msg='Failed to check if user group "%s" exists: %s' % (name, e)) + self._module.fail_json( + msg="Failed to check if user group '%s' exists: %s" % (name, e) + ) def get_usergroup_by_usergroup_name(self, name): """Get user group by user group name. @@ -592,28 +598,32 @@ class UserGroup(ZabbixBase): User group matching user group name. """ try: - if LooseVersion(self._zbx_api_version) < LooseVersion('6.2'): - _usergroup = self._zapi.usergroup.get({ - 'output': 'extend', - 'selectTagFilters': 'extend', - 'selectRights': 'extend', - 'filter': {'name': [name]} - }) + if LooseVersion(self._zbx_api_version) < LooseVersion("6.2"): + _usergroup = self._zapi.usergroup.get( + { + "output": "extend", + "selectTagFilters": "extend", + "selectRights": "extend", + "filter": {"name": [name]}, + } + ) else: - _usergroup = self._zapi.usergroup.get({ - 'output': 'extend', - 'selectTagFilters': 'extend', - 'selectHostGroupRights': 'extend', - 'selectTemplateGroupRights': 'extend', - 'filter': {'name': [name]} - }) + _usergroup = self._zapi.usergroup.get( + { + "output": "extend", + "selectTagFilters": "extend", + "selectHostGroupRights": "extend", + "selectTemplateGroupRights": "extend", + "filter": {"name": [name]}, + } + ) if len(_usergroup) < 1: - self._module.fail_json(msg='User group not found: %s' % name) + self._module.fail_json(msg="User group not found: %s" % name) else: return _usergroup[0] except Exception as e: - self._module.fail_json(msg='Failed to get user group "%s": %s' % (name, e)) + self._module.fail_json(msg="Failed to get user group '%s': %s" % (name, e)) def check_difference(self, **kwargs): """Check difference between user group and user specified parameters. @@ -624,10 +634,16 @@ class UserGroup(ZabbixBase): Returns: dict: dictionary of differences """ - existing_usergroup = zabbix_utils.helper_convert_unicode_to_str(self.get_usergroup_by_usergroup_name(kwargs['name'])) - parameters = zabbix_utils.helper_convert_unicode_to_str(self._construct_parameters(**kwargs)) + existing_usergroup = zabbix_utils.helper_convert_unicode_to_str( + self.get_usergroup_by_usergroup_name(kwargs["name"]) + ) + parameters = zabbix_utils.helper_convert_unicode_to_str( + self._construct_parameters(**kwargs) + ) change_parameters = {} - _diff = zabbix_utils.helper_compare_dictionaries(parameters, existing_usergroup, change_parameters) + _diff = zabbix_utils.helper_compare_dictionaries( + parameters, existing_usergroup, change_parameters + ) return _diff def update(self, **kwargs): @@ -644,7 +660,9 @@ class UserGroup(ZabbixBase): self._module.exit_json(changed=True) return self._zapi.usergroup.update(kwargs) except Exception as e: - self._module.fail_json(msg='Failed to update user group "%s": %s' % (kwargs['usrgrpid'], e)) + self._module.fail_json( + msg="Failed to update user group '%s': %s" % (kwargs["usrgrpid"], e) + ) def add(self, **kwargs): """Add user group. @@ -660,9 +678,11 @@ class UserGroup(ZabbixBase): self._module.exit_json(changed=True) parameters = self._construct_parameters(**kwargs) usergroup = self._zapi.usergroup.create(parameters) - return usergroup['usrgrpids'][0] + return usergroup["usrgrpids"][0] except Exception as e: - self._module.fail_json(msg='Failed to create user group "%s": %s' % (kwargs['name'], e)) + self._module.fail_json( + msg="Failed to create user group '%s': %s" % (kwargs["name"], e) + ) def delete(self, usrgrpid): """Delete user group. @@ -679,62 +699,102 @@ class UserGroup(ZabbixBase): else: return self._zapi.usergroup.delete([usrgrpid]) except Exception as e: - self._module.fail_json(msg='Failed to delete user group "%s": %s' % (usrgrpid, e)) + self._module.fail_json( + msg="Failed to delete user group '%s': %s" % (usrgrpid, e) + ) def main(): argument_spec = zabbix_utils.zabbix_common_argument_spec() argument_spec.update( - name=dict(type='str', required=True, aliases=['user_group']), - gui_access=dict(type='str', required=False, default='default', choices=['default', 'internal', 'LDAP', 'disable']), - debug_mode=dict(type='str', required=False, default='disabled', choices=['disabled', 'enabled']), - status=dict(type='str', required=False, default='enabled', choices=['enabled', 'disabled']), - rights=dict(type='list', elements='dict', required=False, options=dict( - host_group=dict(type='str', required=True), - permission=dict(type='str', required=True, choices=['denied', 'read-only', 'read-write']) - )), - hostgroup_rights=dict(type='list', elements='dict', required=False, options=dict( - host_group=dict(type='str', required=True), - permission=dict(type='str', required=True, choices=['denied', 'read-only', 'read-write']) - )), - templategroup_rights=dict(type='list', elements='dict', required=False, options=dict( - template_group=dict(type='str', required=True), - permission=dict(type='str', required=True, choices=['denied', 'read-only', 'read-write']) - )), - tag_filters=dict(type='list', elements='dict', required=False, options=dict( - host_group=dict(type='str', required=True), - tag=dict(type='str', default=''), - value=dict(type='str', default='') - )), - userdirectory=dict(type='str', required=False), - state=dict(type='str', default='present', choices=['present', 'absent']) - ) - - module = AnsibleModule( - argument_spec=argument_spec, - supports_check_mode=True + name=dict(type="str", required=True, aliases=["user_group"]), + gui_access=dict( + type="str", + required=False, + default="default", + choices=["default", "internal", "LDAP", "disable"], + ), + debug_mode=dict( + type="str", + required=False, + default="disabled", + choices=["disabled", "enabled"], + ), + status=dict( + type="str", + required=False, + default="enabled", + choices=["enabled", "disabled"], + ), + rights=dict( + type="list", + elements="dict", + required=False, + options=dict( + host_group=dict(type="str", required=True), + permission=dict( + type="str", + required=True, + choices=["denied", "read-only", "read-write"], + ), + ), + ), + hostgroup_rights=dict( + type="list", + elements="dict", + required=False, + options=dict( + host_group=dict(type="str", required=True), + permission=dict( + type="str", + required=True, + choices=["denied", "read-only", "read-write"], + ), + ), + ), + templategroup_rights=dict( + type="list", + elements="dict", + required=False, + options=dict( + template_group=dict(type="str", required=True), + permission=dict( + type="str", + required=True, + choices=["denied", "read-only", "read-write"], + ), + ), + ), + tag_filters=dict( + type="list", + elements="dict", + required=False, + options=dict( + host_group=dict(type="str", required=True), + tag=dict(type="str", default=""), + value=dict(type="str", default=""), + ), + ), + userdirectory=dict(type="str", required=False), + state=dict(type="str", default="present", choices=["present", "absent"]), ) - zabbix_utils.require_creds_params(module) + module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True) - for p in ['server_url', 'login_user', 'login_password', 'timeout', 'validate_certs']: - if p in module.params and not module.params[p] is None: - module.warn('Option "%s" is deprecated with the move to httpapi connection and will be removed in the next release' % p) - - name = module.params['name'] - gui_access = module.params['gui_access'] - debug_mode = module.params['debug_mode'] - status = module.params['status'] - rights = module.params['rights'] - hostgroup_rights = module.params['hostgroup_rights'] - templategroup_rights = module.params['templategroup_rights'] - tag_filters = module.params['tag_filters'] - userdirectory = module.params['userdirectory'] - state = module.params['state'] + name = module.params["name"] + gui_access = module.params["gui_access"] + debug_mode = module.params["debug_mode"] + status = module.params["status"] + rights = module.params["rights"] + hostgroup_rights = module.params["hostgroup_rights"] + templategroup_rights = module.params["templategroup_rights"] + tag_filters = module.params["tag_filters"] + userdirectory = module.params["userdirectory"] + state = module.params["state"] userGroup = UserGroup(module) zbx = userGroup._zapi - if LooseVersion(userGroup._zbx_api_version) < LooseVersion('6.2'): + if LooseVersion(userGroup._zbx_api_version) < LooseVersion("6.2"): rgts = Rights(module, zbx) else: hostgroup_rgts = HostgroupRights(module, zbx) @@ -744,12 +804,18 @@ def main(): usergroup_exists = userGroup.check_if_usergroup_exists(name) if usergroup_exists: - usrgrpid = userGroup.get_usergroup_by_usergroup_name(name)['usrgrpid'] - if state == 'absent': + usrgrpid = userGroup.get_usergroup_by_usergroup_name(name)["usrgrpid"] + if state == "absent": userGroup.delete(usrgrpid) - module.exit_json(changed=True, state=state, usergroup=name, usrgrpid=usrgrpid, msg='User group deleted: %s, ID: %s' % (name, usrgrpid)) + module.exit_json( + changed=True, + state=state, + usergroup=name, + usrgrpid=usrgrpid, + msg="User group deleted: %s, ID: %s" % (name, usrgrpid), + ) else: - if LooseVersion(userGroup._zbx_api_version) < LooseVersion('6.2'): + if LooseVersion(userGroup._zbx_api_version) < LooseVersion("6.2"): difference = userGroup.check_difference( usrgrpid=usrgrpid, name=name, @@ -757,7 +823,7 @@ def main(): debug_mode=debug_mode, status=status, rights=rgts.construct_the_data(rights), - tag_filters=tgflts.construct_the_data(tag_filters) + tag_filters=tgflts.construct_the_data(tag_filters), ) else: difference = userGroup.check_difference( @@ -766,31 +832,49 @@ def main(): gui_access=gui_access, debug_mode=debug_mode, status=status, - hostgroup_rights=hostgroup_rgts.construct_the_data(hostgroup_rights), - templategroup_rights=templategroup_rgts.construct_the_data(templategroup_rights), + hostgroup_rights=hostgroup_rgts.construct_the_data( + hostgroup_rights + ), + templategroup_rights=templategroup_rgts.construct_the_data( + templategroup_rights + ), tag_filters=tgflts.construct_the_data(tag_filters), - userdirectory=userdirectory + userdirectory=userdirectory, ) if difference == {}: - module.exit_json(changed=False, state=state, usergroup=name, usrgrpid=usrgrpid, msg='User group is up to date: %s' % name) + module.exit_json( + changed=False, + state=state, + usergroup=name, + usrgrpid=usrgrpid, + msg="User group is up to date: %s" % name, + ) else: - userGroup.update( + userGroup.update(usrgrpid=usrgrpid, **difference) + module.exit_json( + changed=True, + state=state, + usergroup=name, usrgrpid=usrgrpid, - **difference + msg="User group updated: %s, ID: %s" % (name, usrgrpid), ) - module.exit_json(changed=True, state=state, usergroup=name, usrgrpid=usrgrpid, msg='User group updated: %s, ID: %s' % (name, usrgrpid)) else: - if state == 'absent': - module.exit_json(changed=False, state=state, usergroup=name, msg='User group %s does not exists, nothing to delete' % name) + if state == "absent": + module.exit_json( + changed=False, + state=state, + usergroup=name, + msg="User group %s does not exists, nothing to delete" % name, + ) else: - if LooseVersion(userGroup._zbx_api_version) < LooseVersion('6.2'): + if LooseVersion(userGroup._zbx_api_version) < LooseVersion("6.2"): usrgrpid = userGroup.add( name=name, gui_access=gui_access, debug_mode=debug_mode, status=status, rights=rgts.construct_the_data(rights), - tag_filters=tgflts.construct_the_data(tag_filters) + tag_filters=tgflts.construct_the_data(tag_filters), ) else: usrgrpid = userGroup.add( @@ -798,13 +882,23 @@ def main(): gui_access=gui_access, debug_mode=debug_mode, status=status, - hostgroup_rights=hostgroup_rgts.construct_the_data(hostgroup_rights), - templategroup_rights=templategroup_rgts.construct_the_data(templategroup_rights), + hostgroup_rights=hostgroup_rgts.construct_the_data( + hostgroup_rights + ), + templategroup_rights=templategroup_rgts.construct_the_data( + templategroup_rights + ), tag_filters=tgflts.construct_the_data(tag_filters), - userdirectory=userdirectory + userdirectory=userdirectory, ) - module.exit_json(changed=True, state=state, usergroup=name, usrgrpid=usrgrpid, msg='User group created: %s, ID: %s' % (name, usrgrpid)) + module.exit_json( + changed=True, + state=state, + usergroup=name, + usrgrpid=usrgrpid, + msg="User group created: %s, ID: %s" % (name, usrgrpid), + ) -if __name__ == '__main__': +if __name__ == "__main__": main() diff --git a/ansible_collections/community/zabbix/plugins/modules/zabbix_valuemap.py b/ansible_collections/community/zabbix/plugins/modules/zabbix_valuemap.py index 196f8ff7e..3d9a64954 100644 --- a/ansible_collections/community/zabbix/plugins/modules/zabbix_valuemap.py +++ b/ansible_collections/community/zabbix/plugins/modules/zabbix_valuemap.py @@ -8,7 +8,7 @@ from __future__ import absolute_import, division, print_function __metaclass__ = type -DOCUMENTATION = r''' +DOCUMENTATION = r""" --- module: zabbix_valuemap short_description: Create/update/delete Zabbix value maps @@ -17,56 +17,56 @@ description: author: - "Ruben Tsirunyan (@rubentsirunyan)" requirements: - - "python >= 2.6" + - "python >= 3.9" options: name: - type: 'str' + type: "str" description: - Name of the value map. required: true state: - type: 'str' + type: "str" description: - State of the value map. - On C(present), it will create a value map if it does not exist or update the value map if the associated data is different. - On C(absent), it will remove the value map if it exists. - choices: ['present', 'absent'] - default: 'present' + choices: ["present", "absent"] + default: "present" mappings: - type: 'list' + type: "list" elements: dict description: - List of value mappings for the value map. - Required when I(state=present). suboptions: value: - type: 'str' + type: "str" description: Original value. required: true map_to: - type: 'str' + type: "str" description: Value to which the original value is mapped to. required: true extends_documentation_fragment: - community.zabbix.zabbix -''' +""" -RETURN = r''' -''' +RETURN = r""" +""" -EXAMPLES = r''' +EXAMPLES = r""" # If you want to use Username and Password to be authenticated by Zabbix Server - name: Set credentials to access Zabbix Server API - set_fact: + ansible.builtin.set_fact: ansible_user: Admin ansible_httpapi_pass: zabbix # If you want to use API token to be authenticated by Zabbix Server # https://www.zabbix.com/documentation/current/en/manual/web_interface/frontend_sections/administration/general#api-tokens - name: Set API token - set_fact: + ansible.builtin.set_fact: ansible_zabbix_auth_key: 8ec0d52432c15c91fcafe9888500cf9a607f44091ab554dbee860f6b44fac895 - name: Create a value map @@ -77,7 +77,7 @@ EXAMPLES = r''' ansible_httpapi_port: 443 ansible_httpapi_use_ssl: true ansible_httpapi_validate_certs: false - ansible_zabbix_url_path: 'zabbixeu' # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu + ansible_zabbix_url_path: "zabbixeu" # If Zabbix WebUI runs on non-default (zabbix) path ,e.g. http://<FQDN>/zabbixeu ansible_host: zabbix-example-fqdn.org community.zabbix.zabbix_valuemap: name: Numbers @@ -87,7 +87,7 @@ EXAMPLES = r''' - value: 2 map_to: two state: present -''' +""" from ansible.module_utils.basic import AnsibleModule @@ -104,31 +104,31 @@ def construct_parameters(**kwargs): Returns: A dictionary of arguments in a format that is understandable by Zabbix API. """ - if kwargs['mappings'] is None: + if kwargs["mappings"] is None: return dict( - name=kwargs['name'] + name=kwargs["name"] ) return dict( - name=kwargs['name'], + name=kwargs["name"], mappings=[ dict( - value=mapping['value'], - newvalue=mapping['map_to'] - ) for mapping in kwargs['mappings'] + value=mapping["value"], + newvalue=mapping["map_to"] + ) for mapping in kwargs["mappings"] ] ) def diff(existing, new): - """Constructs the diff for Ansible's --diff option. + """Constructs the diff for Ansible"s --diff option. Args: existing (dict): Existing valuemap data. new (dict): New valuemap data. Returns: - A dictionary like {'before': existing, 'after': new} + A dictionary like {"before": existing, "after": new} with filtered empty values. """ before = {} @@ -136,11 +136,11 @@ def diff(existing, new): for key in new: before[key] = existing[key] if new[key] is None: - after[key] = '' + after[key] = "" else: after[key] = new[key] - return {'before': before, 'after': after} + return {"before": before, "after": after} def get_update_params(existing_valuemap, **kwargs): @@ -158,8 +158,8 @@ def get_update_params(existing_valuemap, **kwargs): """ params_to_update = {} - if sorted(existing_valuemap['mappings'], key=lambda k: k['value']) != sorted(kwargs['mappings'], key=lambda k: k['value']): - params_to_update['mappings'] = kwargs['mappings'] + if sorted(existing_valuemap["mappings"], key=lambda k: k["value"]) != sorted(kwargs["mappings"], key=lambda k: k["value"]): + params_to_update["mappings"] = kwargs["mappings"] return params_to_update, diff(existing_valuemap, kwargs) @@ -176,9 +176,9 @@ class ValuemapModule(ZabbixBase): """ try: valuemap_list = self._zapi.valuemap.get({ - 'output': 'extend', - 'selectMappings': 'extend', - 'filter': {'name': [name]} + "output": "extend", + "selectMappings": "extend", + "filter": {"name": [name]} }) if len(valuemap_list) < 1: return False, None @@ -197,26 +197,26 @@ class ValuemapModule(ZabbixBase): try: self._zapi.valuemap.update(kwargs) except Exception as e: - self._module.fail_json(msg="Failed to update valuemap '{_id}': {e}".format(_id=kwargs['valuemapid'], e=e)) + self._module.fail_json(msg="Failed to update valuemap '{_id}': {e}".format(_id=kwargs["valuemapid"], e=e)) def create(self, **kwargs): try: self._zapi.valuemap.create(kwargs) except Exception as e: - self._module.fail_json(msg="Failed to create valuemap '{name}': {e}".format(name=kwargs['description'], e=e)) + self._module.fail_json(msg="Failed to create valuemap '{name}': {e}".format(name=kwargs["description"], e=e)) def main(): argument_spec = zabbix_utils.zabbix_common_argument_spec() argument_spec.update(dict( - name=dict(type='str', required=True), - state=dict(type='str', default='present', choices=['present', 'absent']), + name=dict(type="str", required=True), + state=dict(type="str", default="present", choices=["present", "absent"]), mappings=dict( - type='list', - elements='dict', + type="list", + elements="dict", options=dict( - value=dict(type='str', required=True), - map_to=dict(type='str', required=True) + value=dict(type="str", required=True), + map_to=dict(type="str", required=True) ) ) )) @@ -224,20 +224,15 @@ def main(): argument_spec=argument_spec, supports_check_mode=True, required_if=[ - ['state', 'present', ['mappings']], + ["state", "present", ["mappings"]], ] ) - zabbix_utils.require_creds_params(module) - - for p in ['server_url', 'login_user', 'login_password', 'timeout', 'validate_certs']: - if p in module.params and not module.params[p] is None: - module.warn('Option "%s" is deprecated with the move to httpapi connection and will be removed in the next release' % p) vm = ValuemapModule(module) - name = module.params['name'] - state = module.params['state'] - mappings = module.params['mappings'] + name = module.params["name"] + state = module.params["state"] + mappings = module.params["mappings"] valuemap_exists, valuemap_object = vm.check_if_valuemap_exists(name) @@ -247,8 +242,8 @@ def main(): ) if valuemap_exists: - valuemap_id = valuemap_object['valuemapid'] - if state == 'absent': + valuemap_id = valuemap_object["valuemapid"] + if state == "absent": if module.check_mode: module.exit_json( changed=True, @@ -313,5 +308,5 @@ def main(): ) -if __name__ == '__main__': +if __name__ == "__main__": main() |