summaryrefslogtreecommitdiffstats
path: root/ansible_collections/community/zabbix/plugins/module_utils
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-13 12:04:41 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-13 12:04:41 +0000
commit975f66f2eebe9dadba04f275774d4ab83f74cf25 (patch)
tree89bd26a93aaae6a25749145b7e4bca4a1e75b2be /ansible_collections/community/zabbix/plugins/module_utils
parentInitial commit. (diff)
downloadansible-975f66f2eebe9dadba04f275774d4ab83f74cf25.tar.xz
ansible-975f66f2eebe9dadba04f275774d4ab83f74cf25.zip
Adding upstream version 7.7.0+dfsg.upstream/7.7.0+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'ansible_collections/community/zabbix/plugins/module_utils')
-rw-r--r--ansible_collections/community/zabbix/plugins/module_utils/__init__.py0
-rw-r--r--ansible_collections/community/zabbix/plugins/module_utils/_version.py343
-rw-r--r--ansible_collections/community/zabbix/plugins/module_utils/api_request.py84
-rw-r--r--ansible_collections/community/zabbix/plugins/module_utils/base.py33
-rw-r--r--ansible_collections/community/zabbix/plugins/module_utils/helpers.py216
-rw-r--r--ansible_collections/community/zabbix/plugins/module_utils/wrappers.py84
6 files changed, 760 insertions, 0 deletions
diff --git a/ansible_collections/community/zabbix/plugins/module_utils/__init__.py b/ansible_collections/community/zabbix/plugins/module_utils/__init__.py
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/ansible_collections/community/zabbix/plugins/module_utils/__init__.py
diff --git a/ansible_collections/community/zabbix/plugins/module_utils/_version.py b/ansible_collections/community/zabbix/plugins/module_utils/_version.py
new file mode 100644
index 000000000..ce027171c
--- /dev/null
+++ b/ansible_collections/community/zabbix/plugins/module_utils/_version.py
@@ -0,0 +1,343 @@
+# Vendored copy of distutils/version.py from CPython 3.9.5
+#
+# Implements multiple version numbering conventions for the
+# Python Module Distribution Utilities.
+#
+# PSF License (see PSF-license.txt or https://opensource.org/licenses/Python-2.0)
+#
+
+"""Provides classes to represent module version numbers (one class for
+each style of version numbering). There are currently two such classes
+implemented: StrictVersion and LooseVersion.
+
+Every version number class implements the following interface:
+ * the 'parse' method takes a string and parses it to some internal
+ representation; if the string is an invalid version number,
+ 'parse' raises a ValueError exception
+ * the class constructor takes an optional string argument which,
+ if supplied, is passed to 'parse'
+ * __str__ reconstructs the string that was passed to 'parse' (or
+ an equivalent string -- ie. one that will generate an equivalent
+ version number instance)
+ * __repr__ generates Python code to recreate the version number instance
+ * _cmp compares the current instance with either another instance
+ of the same class or a string (which will be parsed to an instance
+ of the same class, thus must follow the same rules)
+"""
+
+from __future__ import (absolute_import, division, print_function)
+__metaclass__ = type
+
+import re
+
+try:
+ RE_FLAGS = re.VERBOSE | re.ASCII
+except AttributeError:
+ RE_FLAGS = re.VERBOSE
+
+
+class Version:
+ """Abstract base class for version numbering classes. Just provides
+ constructor (__init__) and reproducer (__repr__), because those
+ seem to be the same for all version numbering classes; and route
+ rich comparisons to _cmp.
+ """
+
+ def __init__(self, vstring=None):
+ if vstring:
+ self.parse(vstring)
+
+ def __repr__(self):
+ return "%s ('%s')" % (self.__class__.__name__, str(self))
+
+ def __eq__(self, other):
+ c = self._cmp(other)
+ if c is NotImplemented:
+ return c
+ return c == 0
+
+ def __lt__(self, other):
+ c = self._cmp(other)
+ if c is NotImplemented:
+ return c
+ return c < 0
+
+ def __le__(self, other):
+ c = self._cmp(other)
+ if c is NotImplemented:
+ return c
+ return c <= 0
+
+ def __gt__(self, other):
+ c = self._cmp(other)
+ if c is NotImplemented:
+ return c
+ return c > 0
+
+ def __ge__(self, other):
+ c = self._cmp(other)
+ if c is NotImplemented:
+ return c
+ return c >= 0
+
+
+# Interface for version-number classes -- must be implemented
+# by the following classes (the concrete ones -- Version should
+# be treated as an abstract class).
+# __init__ (string) - create and take same action as 'parse'
+# (string parameter is optional)
+# parse (string) - convert a string representation to whatever
+# internal representation is appropriate for
+# this style of version numbering
+# __str__ (self) - convert back to a string; should be very similar
+# (if not identical to) the string supplied to parse
+# __repr__ (self) - generate Python code to recreate
+# the instance
+# _cmp (self, other) - compare two version numbers ('other' may
+# be an unparsed version string, or another
+# instance of your version class)
+
+
+class StrictVersion(Version):
+ """Version numbering for anal retentives and software idealists.
+ Implements the standard interface for version number classes as
+ described above. A version number consists of two or three
+ dot-separated numeric components, with an optional "pre-release" tag
+ on the end. The pre-release tag consists of the letter 'a' or 'b'
+ followed by a number. If the numeric components of two version
+ numbers are equal, then one with a pre-release tag will always
+ be deemed earlier (lesser) than one without.
+
+ The following are valid version numbers (shown in the order that
+ would be obtained by sorting according to the supplied cmp function):
+
+ 0.4 0.4.0 (these two are equivalent)
+ 0.4.1
+ 0.5a1
+ 0.5b3
+ 0.5
+ 0.9.6
+ 1.0
+ 1.0.4a3
+ 1.0.4b1
+ 1.0.4
+
+ The following are examples of invalid version numbers:
+
+ 1
+ 2.7.2.2
+ 1.3.a4
+ 1.3pl1
+ 1.3c4
+
+ The rationale for this version numbering system will be explained
+ in the distutils documentation.
+ """
+
+ version_re = re.compile(r'^(\d+) \. (\d+) (\. (\d+))? ([ab](\d+))?$',
+ RE_FLAGS)
+
+ def parse(self, vstring):
+ match = self.version_re.match(vstring)
+ if not match:
+ raise ValueError("invalid version number '%s'" % vstring)
+
+ (major, minor, patch, prerelease, prerelease_num) = \
+ match.group(1, 2, 4, 5, 6)
+
+ if patch:
+ self.version = tuple(map(int, [major, minor, patch]))
+ else:
+ self.version = tuple(map(int, [major, minor])) + (0,)
+
+ if prerelease:
+ self.prerelease = (prerelease[0], int(prerelease_num))
+ else:
+ self.prerelease = None
+
+ def __str__(self):
+ if self.version[2] == 0:
+ vstring = '.'.join(map(str, self.version[0:2]))
+ else:
+ vstring = '.'.join(map(str, self.version))
+
+ if self.prerelease:
+ vstring = vstring + self.prerelease[0] + str(self.prerelease[1])
+
+ return vstring
+
+ def _cmp(self, other):
+ if isinstance(other, str):
+ other = StrictVersion(other)
+ elif not isinstance(other, StrictVersion):
+ return NotImplemented
+
+ if self.version != other.version:
+ # numeric versions don't match
+ # prerelease stuff doesn't matter
+ if self.version < other.version:
+ return -1
+ else:
+ return 1
+
+ # have to compare prerelease
+ # case 1: neither has prerelease; they're equal
+ # case 2: self has prerelease, other doesn't; other is greater
+ # case 3: self doesn't have prerelease, other does: self is greater
+ # case 4: both have prerelease: must compare them!
+
+ if (not self.prerelease and not other.prerelease):
+ return 0
+ elif (self.prerelease and not other.prerelease):
+ return -1
+ elif (not self.prerelease and other.prerelease):
+ return 1
+ elif (self.prerelease and other.prerelease):
+ if self.prerelease == other.prerelease:
+ return 0
+ elif self.prerelease < other.prerelease:
+ return -1
+ else:
+ return 1
+ else:
+ raise AssertionError("never get here")
+
+# end class StrictVersion
+
+# The rules according to Greg Stein:
+# 1) a version number has 1 or more numbers separated by a period or by
+# sequences of letters. If only periods, then these are compared
+# left-to-right to determine an ordering.
+# 2) sequences of letters are part of the tuple for comparison and are
+# compared lexicographically
+# 3) recognize the numeric components may have leading zeroes
+#
+# The LooseVersion class below implements these rules: a version number
+# string is split up into a tuple of integer and string components, and
+# comparison is a simple tuple comparison. This means that version
+# numbers behave in a predictable and obvious way, but a way that might
+# not necessarily be how people *want* version numbers to behave. There
+# wouldn't be a problem if people could stick to purely numeric version
+# numbers: just split on period and compare the numbers as tuples.
+# However, people insist on putting letters into their version numbers;
+# the most common purpose seems to be:
+# - indicating a "pre-release" version
+# ('alpha', 'beta', 'a', 'b', 'pre', 'p')
+# - indicating a post-release patch ('p', 'pl', 'patch')
+# but of course this can't cover all version number schemes, and there's
+# no way to know what a programmer means without asking him.
+#
+# The problem is what to do with letters (and other non-numeric
+# characters) in a version number. The current implementation does the
+# obvious and predictable thing: keep them as strings and compare
+# lexically within a tuple comparison. This has the desired effect if
+# an appended letter sequence implies something "post-release":
+# eg. "0.99" < "0.99pl14" < "1.0", and "5.001" < "5.001m" < "5.002".
+#
+# However, if letters in a version number imply a pre-release version,
+# the "obvious" thing isn't correct. Eg. you would expect that
+# "1.5.1" < "1.5.2a2" < "1.5.2", but under the tuple/lexical comparison
+# implemented here, this just isn't so.
+#
+# Two possible solutions come to mind. The first is to tie the
+# comparison algorithm to a particular set of semantic rules, as has
+# been done in the StrictVersion class above. This works great as long
+# as everyone can go along with bondage and discipline. Hopefully a
+# (large) subset of Python module programmers will agree that the
+# particular flavour of bondage and discipline provided by StrictVersion
+# provides enough benefit to be worth using, and will submit their
+# version numbering scheme to its domination. The free-thinking
+# anarchists in the lot will never give in, though, and something needs
+# to be done to accommodate them.
+#
+# Perhaps a "moderately strict" version class could be implemented that
+# lets almost anything slide (syntactically), and makes some heuristic
+# assumptions about non-digits in version number strings. This could
+# sink into special-case-hell, though; if I was as talented and
+# idiosyncratic as Larry Wall, I'd go ahead and implement a class that
+# somehow knows that "1.2.1" < "1.2.2a2" < "1.2.2" < "1.2.2pl3", and is
+# just as happy dealing with things like "2g6" and "1.13++". I don't
+# think I'm smart enough to do it right though.
+#
+# In any case, I've coded the test suite for this module (see
+# ../test/test_version.py) specifically to fail on things like comparing
+# "1.2a2" and "1.2". That's not because the *code* is doing anything
+# wrong, it's because the simple, obvious design doesn't match my
+# complicated, hairy expectations for real-world version numbers. It
+# would be a snap to fix the test suite to say, "Yep, LooseVersion does
+# the Right Thing" (ie. the code matches the conception). But I'd rather
+# have a conception that matches common notions about version numbers.
+
+
+class LooseVersion(Version):
+ """Version numbering for anarchists and software realists.
+ Implements the standard interface for version number classes as
+ described above. A version number consists of a series of numbers,
+ separated by either periods or strings of letters. When comparing
+ version numbers, the numeric components will be compared
+ numerically, and the alphabetic components lexically. The following
+ are all valid version numbers, in no particular order:
+
+ 1.5.1
+ 1.5.2b2
+ 161
+ 3.10a
+ 8.02
+ 3.4j
+ 1996.07.12
+ 3.2.pl0
+ 3.1.1.6
+ 2g6
+ 11g
+ 0.960923
+ 2.2beta29
+ 1.13++
+ 5.5.kw
+ 2.0b1pl0
+
+ In fact, there is no such thing as an invalid version number under
+ this scheme; the rules for comparison are simple and predictable,
+ but may not always give the results you want (for some definition
+ of "want").
+ """
+
+ component_re = re.compile(r'(\d+ | [a-z]+ | \.)', re.VERBOSE)
+
+ def __init__(self, vstring=None):
+ if vstring:
+ self.parse(vstring)
+
+ def parse(self, vstring):
+ # I've given up on thinking I can reconstruct the version string
+ # from the parsed tuple -- so I just store the string here for
+ # use by __str__
+ self.vstring = vstring
+ components = [x for x in self.component_re.split(vstring) if x and x != '.']
+ for i, obj in enumerate(components):
+ try:
+ components[i] = int(obj)
+ except ValueError:
+ pass
+
+ self.version = components
+
+ def __str__(self):
+ return self.vstring
+
+ def __repr__(self):
+ return "LooseVersion ('%s')" % str(self)
+
+ def _cmp(self, other):
+ if isinstance(other, str):
+ other = LooseVersion(other)
+ elif not isinstance(other, LooseVersion):
+ return NotImplemented
+
+ if self.version == other.version:
+ return 0
+ if self.version < other.version:
+ return -1
+ if self.version > other.version:
+ return 1
+
+# end class LooseVersion
diff --git a/ansible_collections/community/zabbix/plugins/module_utils/api_request.py b/ansible_collections/community/zabbix/plugins/module_utils/api_request.py
new file mode 100644
index 000000000..a29f492de
--- /dev/null
+++ b/ansible_collections/community/zabbix/plugins/module_utils/api_request.py
@@ -0,0 +1,84 @@
+# -*- coding: utf-8 -*-
+
+# (c) 2021, Markus Fischbacher (fischbacher.markus@gmail.com)
+# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
+
+# Quick Link to Zabbix API docs: https://www.zabbix.com/documentation/current/manual/api
+
+
+from __future__ import absolute_import, division, print_function
+
+__metaclass__ = type
+
+from uuid import uuid4
+
+from ansible.module_utils.urls import CertificateError
+from ansible.module_utils.connection import ConnectionError
+from ansible.module_utils.connection import Connection
+from ansible.module_utils._text import to_text
+
+
+class ZabbixApiRequest(object):
+
+ def __init__(self, module):
+ self.module = module
+ self.connection = Connection(self.module._socket_path)
+
+ def _httpapi_error_handle(self, payload=None):
+ try:
+ code, response = self.connection.send_request(data=payload)
+ except ConnectionError as e:
+ self.module.fail_json(msg="connection error occurred: {0}".format(e))
+ except CertificateError as e:
+ self.module.fail_json(msg="certificate error occurred: {0}".format(e))
+ except ValueError as e:
+ self.module.fail_json(msg="certificate not found: {0}".format(e))
+
+ if code == 404:
+ if to_text(u"Object not found") in to_text(response) or to_text(
+ u"Could not find object"
+ ) in to_text(response):
+ return {}
+
+ if not (code >= 200 and code < 300):
+ self.module.fail_json(
+ msg="Zabbix httpapi returned error {0} with message {1}".format(
+ code, response
+ )
+ )
+
+ return response
+
+ def api_version(self):
+ return self.connection.api_version()
+
+ @staticmethod
+ def payload_builder(method_, params, jsonrpc_version='2.0', reqid=str(uuid4()), **kwargs):
+ req = {'jsonrpc': jsonrpc_version, 'method': method_, 'id': reqid}
+ req['params'] = params
+ return req
+
+ def __getattr__(self, name):
+ return ZabbixApiSection(self, name)
+
+
+class ZabbixApiSection(object):
+ parent = None
+ name = None
+
+ def __init__(self, parent, name):
+ self.name = name
+ self.parent = parent
+
+ def __getattr__(self, name):
+ def method(opts=None):
+ if self.name == "configuration" and name == "import_":
+ _method = "configuration.import"
+ else:
+ _method = "%s.%s" % (self.name, name)
+ if not opts:
+ opts = {}
+ payload = ZabbixApiRequest.payload_builder(_method, opts)
+ return self.parent._httpapi_error_handle(payload=payload)
+
+ return method
diff --git a/ansible_collections/community/zabbix/plugins/module_utils/base.py b/ansible_collections/community/zabbix/plugins/module_utils/base.py
new file mode 100644
index 000000000..8858a02e1
--- /dev/null
+++ b/ansible_collections/community/zabbix/plugins/module_utils/base.py
@@ -0,0 +1,33 @@
+#!/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
+
+from ansible_collections.community.zabbix.plugins.module_utils.wrappers import ZapiWrapper
+from ansible_collections.community.zabbix.plugins.module_utils.api_request import ZabbixApiRequest
+
+
+class ZabbixBase(object):
+ """
+ The base class for deriving off module classes
+ """
+ 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()
diff --git a/ansible_collections/community/zabbix/plugins/module_utils/helpers.py b/ansible_collections/community/zabbix/plugins/module_utils/helpers.py
new file mode 100644
index 000000000..6c9c0fca5
--- /dev/null
+++ b/ansible_collections/community/zabbix/plugins/module_utils/helpers.py
@@ -0,0 +1,216 @@
+#!/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
+
+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():
+ """
+ Return a dictionary with connection options.
+ 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,
+ default=None
+ ),
+ http_login_password=dict(
+ type='str',
+ required=False,
+ default=None,
+ no_log=True
+ ),
+ timeout=dict(
+ type='int'
+ ),
+ validate_certs=dict(
+ type='bool',
+ required=False,
+ fallback=(env_fallback, ['ZABBIX_VALIDATE_CERTS'])
+ ),
+ )
+
+
+def helper_cleanup_data(obj):
+ """
+ Removes the None values from the object and returns the object
+ Args:
+ obj: object to cleanup
+
+ Returns:
+ object: cleaned object
+ """
+ if isinstance(obj, (list, tuple, set)):
+ return type(obj)(helper_cleanup_data(x) for x in obj if x is not None)
+ elif isinstance(obj, dict):
+ return type(obj)((helper_cleanup_data(k), helper_cleanup_data(v))
+ for k, v in obj.items() if k is not None and v is not None)
+ else:
+ return obj
+
+
+def helper_to_numeric_value(elements, value):
+ """Converts string values to integers
+
+ Parameters:
+ elements: list of elements to enumerate
+ value: string value
+
+ Returns:
+ int: converted integer
+ """
+ if value is None:
+ return None
+ for index, element in enumerate(elements):
+ if isinstance(element, str) and element.lower() == value.lower():
+ return index
+ if isinstance(element, list):
+ for deep_element in element:
+ if isinstance(deep_element, str) and deep_element.lower() == value.lower():
+ return index
+
+
+def helper_convert_unicode_to_str(data):
+ """Converts unicode objects to strings in dictionary
+
+ Parameters:
+ data: unicode object
+
+ Returns:
+ dict: strings in dictionary
+ """
+ if isinstance(data, dict):
+ return dict(map(helper_convert_unicode_to_str, data.items()))
+ elif isinstance(data, (list, tuple, set)):
+ return type(data)(map(helper_convert_unicode_to_str, data))
+ elif data is None:
+ return data
+ else:
+ return str(data)
+
+
+def helper_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 helper_compare_dictionaries() function.
+
+ Parameters:
+ 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):
+ for item2 in l2:
+ diff_dict2 = {}
+ diff_dict2 = helper_compare_dictionaries(item, item2, diff_dict2)
+ if len(diff_dict2) == 0:
+ break
+ if len(diff_dict2) != 0:
+ diff_dict.insert(i, item)
+ else:
+ if item != l2[i]:
+ diff_dict.append(item)
+ while {} in diff_dict:
+ diff_dict.remove({})
+ return diff_dict
+
+
+def helper_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 helper_compare_lists() function.
+
+ Parameters:
+ 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] = {}
+ helper_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] = []
+ helper_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 helper_normalize_data(data, del_keys=None):
+ """
+ Delete None parameter or specified keys from data.
+
+ Parameters:
+ data: dictionary
+
+ Returns:
+ data: falsene parameter removed data
+ del_keys: deleted keys
+ """
+ if del_keys is None:
+ del_keys = []
+
+ for key, value in data.items():
+ if value is None:
+ del_keys.append(key)
+
+ for key in del_keys:
+ if key in data.keys():
+ del data[key]
+
+ return data, del_keys
diff --git a/ansible_collections/community/zabbix/plugins/module_utils/wrappers.py b/ansible_collections/community/zabbix/plugins/module_utils/wrappers.py
new file mode 100644
index 000000000..a982108f8
--- /dev/null
+++ b/ansible_collections/community/zabbix/plugins/module_utils/wrappers.py
@@ -0,0 +1,84 @@
+#!/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