mirror of
https://github.com/valitydev/salt.git
synced 2024-11-06 16:45:27 +00:00
Add state modules for Zabbix templates, actions and valuemaps
This commit is contained in:
parent
8f3f3146ba
commit
e5cf77d3d1
5
doc/ref/states/all/salt.states.zabbix_action.rst
Normal file
5
doc/ref/states/all/salt.states.zabbix_action.rst
Normal file
@ -0,0 +1,5 @@
|
||||
salt.states.zabbix_action module
|
||||
==============================
|
||||
|
||||
.. automodule:: salt.states.zabbix_action
|
||||
:members:
|
5
doc/ref/states/all/salt.states.zabbix_template.rst
Normal file
5
doc/ref/states/all/salt.states.zabbix_template.rst
Normal file
@ -0,0 +1,5 @@
|
||||
salt.states.zabbix_template module
|
||||
==============================
|
||||
|
||||
.. automodule:: salt.states.zabbix_template
|
||||
:members:
|
5
doc/ref/states/all/salt.states.zabbix_valuemap.rst
Normal file
5
doc/ref/states/all/salt.states.zabbix_valuemap.rst
Normal file
@ -0,0 +1,5 @@
|
||||
salt.states.zabbix_valuemap module
|
||||
==============================
|
||||
|
||||
.. automodule:: salt.states.zabbix_valuemap
|
||||
:members:
|
@ -23,34 +23,91 @@ Support for Zabbix
|
||||
|
||||
:codeauthor: Jiri Kotlin <jiri.kotlin@ultimum.io>
|
||||
'''
|
||||
from __future__ import absolute_import, print_function, unicode_literals
|
||||
from __future__ import absolute_import
|
||||
from __future__ import print_function
|
||||
from __future__ import unicode_literals
|
||||
|
||||
# Import python libs
|
||||
import logging
|
||||
import socket
|
||||
import json
|
||||
import os
|
||||
|
||||
# Import salt libs
|
||||
from salt.ext import six
|
||||
import salt.utils.http
|
||||
import salt.utils.json
|
||||
import salt.utils.path
|
||||
from salt.utils.versions import LooseVersion as _LooseVersion
|
||||
from salt.ext.six.moves.urllib.error import HTTPError, URLError # pylint: disable=import-error,no-name-in-module
|
||||
try:
|
||||
import salt.utils
|
||||
from salt.ext import six
|
||||
from salt.utils.versions import LooseVersion as _LooseVersion
|
||||
# pylint: disable=import-error,no-name-in-module,unused-import
|
||||
from salt.ext.six.moves.urllib.error import HTTPError, URLError
|
||||
from salt.exceptions import SaltException
|
||||
IMPORTS_OK = True
|
||||
except ImportError:
|
||||
IMPORTS_OK = False
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
INTERFACE_DEFAULT_PORTS = [10050, 161, 623, 12345]
|
||||
|
||||
ZABBIX_TOP_LEVEL_OBJECTS = ('hostgroup', 'template', 'host', 'maintenance', 'action', 'drule', 'service', 'proxy',
|
||||
'screen', 'usergroup', 'mediatype', 'script', 'valuemap')
|
||||
|
||||
# Zabbix object and its ID name mapping
|
||||
ZABBIX_ID_MAPPER = {
|
||||
'action': 'actionid',
|
||||
'alert': 'alertid',
|
||||
'application': 'applicationid',
|
||||
'dhost': 'dhostid',
|
||||
'dservice': 'dserviceid',
|
||||
'dcheck': 'dcheckid',
|
||||
'drule': 'druleid',
|
||||
'event': 'eventid',
|
||||
'graph': 'graphid',
|
||||
'graphitem': 'gitemid',
|
||||
'graphprototype': 'graphid',
|
||||
'history': 'itemid',
|
||||
'host': 'hostid',
|
||||
'hostgroup': 'groupid',
|
||||
'hostinterface': 'interfaceid',
|
||||
'hostprototype': 'hostid',
|
||||
'iconmap': 'iconmapid',
|
||||
'image': 'imageid',
|
||||
'item': 'itemid',
|
||||
'itemprototype': 'itemid',
|
||||
'service': 'serviceid',
|
||||
'discoveryrule': 'itemid',
|
||||
'maintenance': 'maintenanceid',
|
||||
'map': 'sysmapid',
|
||||
'usermedia': 'mediaid',
|
||||
'mediatype': 'mediatypeid',
|
||||
'proxy': 'proxyid',
|
||||
'screen': 'screenid',
|
||||
'screenitem': 'screenitemid',
|
||||
'script': 'scriptid',
|
||||
'template': 'templateid',
|
||||
'templatescreen': 'screenid',
|
||||
'templatescreenitem': 'screenitemid',
|
||||
'trend': 'itemid',
|
||||
'trigger': 'triggerid',
|
||||
'triggerprototype': 'triggerid',
|
||||
'user': 'userid',
|
||||
'usergroup': 'usrgrpid',
|
||||
'usermacro': 'globalmacroid',
|
||||
'valuemap': 'valuemapid',
|
||||
'httptest': 'httptestid'
|
||||
}
|
||||
|
||||
# Define the module's virtual name
|
||||
__virtualname__ = 'zabbix'
|
||||
|
||||
|
||||
def __virtual__():
|
||||
'''
|
||||
Only load the module if Zabbix server is installed
|
||||
Only load the module if all modules are imported correctly.
|
||||
'''
|
||||
if salt.utils.path.which('zabbix_server'):
|
||||
if IMPORTS_OK:
|
||||
return __virtualname__
|
||||
return (False, 'The zabbix execution module cannot be loaded: zabbix not installed.')
|
||||
return False, 'Importing modules failed.'
|
||||
|
||||
|
||||
def _frontend_url():
|
||||
@ -86,7 +143,9 @@ def _query(method, params, url, auth=None):
|
||||
:param url: url of zabbix api
|
||||
:param auth: auth token for zabbix api (only for methods with required authentication)
|
||||
|
||||
:return: Response from API with desired data in JSON format.
|
||||
:return: Response from API with desired data in JSON format. In case of error returns more specific description.
|
||||
|
||||
.. versionchanged:: 2017.7
|
||||
'''
|
||||
|
||||
unauthenticated_methods = ['user.login', 'apiinfo.version', ]
|
||||
@ -99,17 +158,28 @@ def _query(method, params, url, auth=None):
|
||||
|
||||
data = salt.utils.json.dumps(data)
|
||||
|
||||
log.info('_QUERY input:\nurl: %s\ndata: %s', six.text_type(url), six.text_type(data))
|
||||
|
||||
try:
|
||||
result = salt.utils.http.query(url,
|
||||
method='POST',
|
||||
data=data,
|
||||
header_dict=header_dict,
|
||||
decode_type='json',
|
||||
decode=True,)
|
||||
decode=True,
|
||||
status=True,
|
||||
headers=True)
|
||||
log.info('_QUERY result: %s', six.text_type(result))
|
||||
if 'error' in result:
|
||||
raise SaltException('Zabbix API: Status: {0} ({1})'.format(result['status'], result['error']))
|
||||
ret = result.get('dict', {})
|
||||
if 'error' in ret:
|
||||
raise SaltException('Zabbix API: {} ({})'.format(ret['error']['message'], ret['error']['data']))
|
||||
return ret
|
||||
except (URLError, socket.gaierror):
|
||||
return {}
|
||||
except ValueError as err:
|
||||
raise SaltException('URL or HTTP headers are probably not correct! ({})'.format(err))
|
||||
except socket.error as err:
|
||||
raise SaltException('Check hostname in URL! ({})'.format(err))
|
||||
|
||||
|
||||
def _login(**kwargs):
|
||||
@ -171,8 +241,8 @@ def _login(**kwargs):
|
||||
return connargs
|
||||
else:
|
||||
raise KeyError
|
||||
except KeyError:
|
||||
return False
|
||||
except KeyError as err:
|
||||
raise SaltException('URL is probably not correct! ({})'.format(err))
|
||||
|
||||
|
||||
def _params_extend(params, _ignore_name=False, **kwargs):
|
||||
@ -208,6 +278,160 @@ def _params_extend(params, _ignore_name=False, **kwargs):
|
||||
return params
|
||||
|
||||
|
||||
def get_zabbix_id_mapper():
|
||||
'''
|
||||
.. versionadded:: 2017.7
|
||||
|
||||
Make ZABBIX_ID_MAPPER constant available to state modules.
|
||||
|
||||
:return: ZABBIX_ID_MAPPER
|
||||
'''
|
||||
return ZABBIX_ID_MAPPER
|
||||
|
||||
|
||||
def substitute_params(input_object, extend_params=None, filter_key='name', **kwargs):
|
||||
'''
|
||||
.. versionadded:: 2017.7
|
||||
|
||||
Go through Zabbix object params specification and if needed get given object ID from Zabbix API and put it back
|
||||
as a value. Definition of the object is done via dict with keys "query_object" and "query_name".
|
||||
|
||||
:param input_object: Zabbix object type specified in state file
|
||||
:param extend_params: Specify query with params
|
||||
:param filter_key: Custom filtering key (default: name)
|
||||
:param _connection_user: Optional - zabbix user (can also be set in opts or pillar, see module's docstring)
|
||||
:param _connection_password: Optional - zabbix password (can also be set in opts or pillar, see module's docstring)
|
||||
:param _connection_url: Optional - url of zabbix frontend (can also be set in opts, pillar, see module's docstring)
|
||||
|
||||
:return: Params structure with values converted to string for further comparison purposes
|
||||
'''
|
||||
if extend_params is None:
|
||||
extend_params = {}
|
||||
if isinstance(input_object, list):
|
||||
return [substitute_params(oitem, extend_params, filter_key, **kwargs) for oitem in input_object]
|
||||
elif isinstance(input_object, dict):
|
||||
if 'query_object' in input_object:
|
||||
query_params = {}
|
||||
if input_object['query_object'] not in ZABBIX_TOP_LEVEL_OBJECTS:
|
||||
query_params.update(extend_params)
|
||||
try:
|
||||
query_params.update({'filter': {filter_key: input_object['query_name']}})
|
||||
return get_object_id_by_params(input_object['query_object'], query_params, **kwargs)
|
||||
except KeyError:
|
||||
raise SaltException('Qyerying object ID requested '
|
||||
'but object name not provided: {0}'.format(input_object))
|
||||
else:
|
||||
return {key: substitute_params(val, extend_params, filter_key, **kwargs)
|
||||
for key, val in input_object.items()}
|
||||
else:
|
||||
# Zabbix response is always str, return everything in str as well
|
||||
return six.text_type(input_object)
|
||||
|
||||
|
||||
# pylint: disable=too-many-return-statements
|
||||
def compare_params(defined, existing, return_old_value=False):
|
||||
'''
|
||||
.. versionadded:: 2017.7
|
||||
|
||||
Compares Zabbix object definition against existing Zabbix object.
|
||||
|
||||
:param defined: Zabbix object definition taken from sls file.
|
||||
:param existing: Existing Zabbix object taken from result of an API call.
|
||||
:param return_old_value: Default False. If True, returns dict("old"=old_val, "new"=new_val) for rollback purpose.
|
||||
:return: Params that are different from existing object. Result extended by object ID can be passed directly to
|
||||
Zabbix API update method.
|
||||
'''
|
||||
# Comparison of data types
|
||||
if not isinstance(defined, type(existing)):
|
||||
raise SaltException('Zabbix object comparison failed (data type mismatch). Expecting {0}, got {1}. '
|
||||
'Existing value: "{2}", defined value: "{3}").'.format(type(existing),
|
||||
type(defined),
|
||||
existing,
|
||||
defined))
|
||||
|
||||
# Comparison of values
|
||||
if not salt.utils.is_iter(defined):
|
||||
if six.text_type(defined) != six.text_type(existing) and return_old_value:
|
||||
return {'new': six.text_type(defined), 'old': six.text_type(existing)}
|
||||
elif six.text_type(defined) != six.text_type(existing) and not return_old_value:
|
||||
return six.text_type(defined)
|
||||
|
||||
# Comparison of lists of values or lists of dicts
|
||||
if isinstance(defined, list):
|
||||
if len(defined) != len(existing):
|
||||
log.info('Different list length!')
|
||||
return {'new': defined, 'old': existing} if return_old_value else defined
|
||||
else:
|
||||
difflist = []
|
||||
for ditem in defined:
|
||||
d_in_e = []
|
||||
for eitem in existing:
|
||||
comp = compare_params(ditem, eitem, return_old_value)
|
||||
if return_old_value:
|
||||
d_in_e.append(comp['new'])
|
||||
else:
|
||||
d_in_e.append(comp)
|
||||
if all(d_in_e):
|
||||
difflist.append(ditem)
|
||||
# If there is any difference in a list then whole defined list must be returned and provided for update
|
||||
if any(difflist) and return_old_value:
|
||||
return {'new': defined, 'old': existing}
|
||||
elif any(difflist) and not return_old_value:
|
||||
return defined
|
||||
|
||||
# Comparison of dicts
|
||||
if isinstance(defined, dict):
|
||||
try:
|
||||
# defined must be a subset of existing to be compared
|
||||
if set(defined) <= set(existing):
|
||||
intersection = set(defined) & set(existing)
|
||||
diffdict = {'new': {}, 'old': {}} if return_old_value else {}
|
||||
for i in intersection:
|
||||
comp = compare_params(defined[i], existing[i], return_old_value)
|
||||
if return_old_value:
|
||||
if comp or (not comp and isinstance(comp, list)):
|
||||
diffdict['new'].update({i: defined[i]})
|
||||
diffdict['old'].update({i: existing[i]})
|
||||
else:
|
||||
if comp or (not comp and isinstance(comp, list)):
|
||||
diffdict.update({i: defined[i]})
|
||||
return diffdict
|
||||
|
||||
return {'new': defined, 'old': existing} if return_old_value else defined
|
||||
|
||||
except TypeError:
|
||||
raise SaltException('Zabbix object comparison failed (data type mismatch). Expecting {0}, got {1}. '
|
||||
'Existing value: "{2}", defined value: "{3}").'.format(type(existing),
|
||||
type(defined),
|
||||
existing,
|
||||
defined))
|
||||
|
||||
|
||||
def get_object_id_by_params(obj, params=None, **connection_args):
|
||||
'''
|
||||
.. versionadded:: 2017.7
|
||||
|
||||
Get ID of single Zabbix object specified by its name.
|
||||
|
||||
:param obj: Zabbix object type
|
||||
:param params: Parameters by which object is uniquely identified
|
||||
:param _connection_user: Optional - zabbix user (can also be set in opts or pillar, see module's docstring)
|
||||
:param _connection_password: Optional - zabbix password (can also be set in opts or pillar, see module's docstring)
|
||||
:param _connection_url: Optional - url of zabbix frontend (can also be set in opts, pillar, see module's docstring)
|
||||
|
||||
:return: object ID
|
||||
'''
|
||||
if params is None:
|
||||
params = {}
|
||||
res = run_query(obj + '.get', params, **connection_args)
|
||||
if res and len(res) == 1:
|
||||
return six.text_type(res[0][ZABBIX_ID_MAPPER[obj]])
|
||||
else:
|
||||
raise SaltException('Zabbix API: Object does not exist or bad Zabbix user permissions or other unexpected '
|
||||
'result. Called method {0} with params {1}. '
|
||||
'Result: {2}'.format(obj + '.get', params, res))
|
||||
|
||||
|
||||
def apiinfo_version(**connection_args):
|
||||
'''
|
||||
Retrieve the version of the Zabbix API.
|
||||
@ -846,8 +1070,8 @@ def host_create(host, groups, interfaces, **connection_args):
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' zabbix.host_create technicalname 4
|
||||
interfaces='{type: 1, main: 1, useip: 1, ip: "192.168.3.1", dns: "", port: 10050}'
|
||||
visible_name='Host Visible Name' inventory_mode=0 inventory='{"alias": "something"}'
|
||||
interfaces='{if_type: 1, main: 1, useip: 1, ip_: "192.168.3.1", dns: "", port: 10050}'
|
||||
visible_name='Host Visible Name'
|
||||
'''
|
||||
conn_args = _login(**connection_args)
|
||||
ret = False
|
||||
@ -1466,7 +1690,7 @@ def hostinterface_get(hostids, **connection_args):
|
||||
return ret
|
||||
|
||||
|
||||
def hostinterface_create(hostid, ip, dns='', main=1, type=1, useip=1, port=None, **connection_args):
|
||||
def hostinterface_create(hostid, ip_, dns='', main=1, if_type=1, useip=1, port=None, **connection_args):
|
||||
'''
|
||||
Create new host interface
|
||||
NOTE: This function accepts all standard host group interface: keyword argument names differ depending
|
||||
@ -1475,11 +1699,11 @@ def hostinterface_create(hostid, ip, dns='', main=1, type=1, useip=1, port=None,
|
||||
.. versionadded:: 2016.3.0
|
||||
|
||||
:param hostid: ID of the host the interface belongs to
|
||||
:param ip: IP address used by the interface
|
||||
:param ip_: IP address used by the interface
|
||||
:param dns: DNS name used by the interface
|
||||
:param main: whether the interface is used as default on the host (0 - not default, 1 - default)
|
||||
:param port: port number used by the interface
|
||||
:param type: Interface type (1 - agent; 2 - SNMP; 3 - IPMI; 4 - JMX)
|
||||
:param if_type: Interface type (1 - agent; 2 - SNMP; 3 - IPMI; 4 - JMX)
|
||||
:param useip: Whether the connection should be made via IP (0 - connect using host DNS name; 1 - connect using
|
||||
host IP address for this host interface)
|
||||
:param _connection_user: Optional - zabbix user (can also be set in opts or pillar, see module's docstring)
|
||||
@ -1497,12 +1721,18 @@ def hostinterface_create(hostid, ip, dns='', main=1, type=1, useip=1, port=None,
|
||||
ret = False
|
||||
|
||||
if not port:
|
||||
port = INTERFACE_DEFAULT_PORTS[type]
|
||||
port = INTERFACE_DEFAULT_PORTS[if_type]
|
||||
|
||||
try:
|
||||
if conn_args:
|
||||
method = 'hostinterface.create'
|
||||
params = {"hostid": hostid, "ip": ip, "dns": dns, "main": main, "port": port, "type": type, "useip": useip}
|
||||
params = {"hostid": hostid,
|
||||
"ip": ip_,
|
||||
"dns": dns,
|
||||
"main": main,
|
||||
"port": port,
|
||||
"type": if_type,
|
||||
"useip": useip}
|
||||
params = _params_extend(params, **connection_args)
|
||||
ret = _query(method, params, conn_args['url'], conn_args['auth'])
|
||||
return ret['result']['interfaceids']
|
||||
@ -1565,7 +1795,7 @@ def hostinterface_update(interfaceid, **connection_args):
|
||||
CLI Example:
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' zabbix.hostinterface_update 6 ip=0.0.0.2
|
||||
salt '*' zabbix.hostinterface_update 6 ip_=0.0.0.2
|
||||
'''
|
||||
conn_args = _login(**connection_args)
|
||||
ret = False
|
||||
@ -1879,8 +2109,9 @@ def mediatype_get(name=None, mediatypeids=None, **connection_args):
|
||||
_connection_password: zabbix password (can also be set in opts or pillar, see module's docstring)
|
||||
_connection_url: url of zabbix frontend (can also be set in opts or pillar, see module's docstring)
|
||||
|
||||
all optional mediatype.get parameters: keyword argument names differ depending on your zabbix
|
||||
version,nsee: https://www.zabbix.com/documentation/2.2/manual/api/reference/mediatype/get
|
||||
all optional mediatype.get parameters: keyword argument names depends on your zabbix version, see:
|
||||
|
||||
https://www.zabbix.com/documentation/2.2/manual/api/reference/mediatype/get
|
||||
|
||||
Returns:
|
||||
Array with mediatype details, False if no mediatype found or on failure.
|
||||
@ -2039,8 +2270,9 @@ def template_get(name=None, host=None, templateids=None, **connection_args):
|
||||
_connection_password: zabbix password (can also be set in opts or pillar, see module's docstring)
|
||||
_connection_url: url of zabbix frontend (can also be set in opts or pillar, see module's docstring)
|
||||
|
||||
all optional template.get parameters: keyword argument names differ depending on your zabbix
|
||||
version, see: https://www.zabbix.com/documentation/2.4/manual/api/reference/template/get
|
||||
all optional template.get parameters: keyword argument names depends on your zabbix version, see:
|
||||
|
||||
https://www.zabbix.com/documentation/2.4/manual/api/reference/template/get
|
||||
|
||||
Returns:
|
||||
Array with convenient template details, False if no template found or on failure.
|
||||
@ -2085,8 +2317,9 @@ def run_query(method, params, **connection_args):
|
||||
_connection_password: zabbix password (can also be set in opts or pillar, see module's docstring)
|
||||
_connection_url: url of zabbix frontend (can also be set in opts or pillar, see module's docstring)
|
||||
|
||||
all optional template.get parameters: keyword argument names differ depending on your zabbix
|
||||
version, see: https://www.zabbix.com/documentation/2.4/manual/api/reference/
|
||||
all optional template.get parameters: keyword argument names depends on your zabbix version, see:
|
||||
|
||||
https://www.zabbix.com/documentation/2.4/manual/api/reference/
|
||||
|
||||
Returns:
|
||||
Response from Zabbix API
|
||||
@ -2100,10 +2333,86 @@ def run_query(method, params, **connection_args):
|
||||
ret = False
|
||||
try:
|
||||
if conn_args:
|
||||
method = method
|
||||
params = params
|
||||
params = _params_extend(params, **connection_args)
|
||||
ret = _query(method, params, conn_args['url'], conn_args['auth'])
|
||||
if isinstance(ret['result'], bool):
|
||||
return ret['result']
|
||||
return ret['result'] if len(ret['result']) > 0 else False
|
||||
else:
|
||||
raise KeyError
|
||||
except KeyError:
|
||||
return ret
|
||||
|
||||
|
||||
def configuration_import(config_file, rules=None, file_format='xml', **connection_args):
|
||||
'''
|
||||
.. versionadded:: 2017.7
|
||||
|
||||
Imports Zabbix configuration sepcified in file to Zabbix server.
|
||||
|
||||
:param config_file: File with Zabbix config (local or remote)
|
||||
:param rules: Optional - Rules that have to be different from default (defaults are the same as in Zabbix web UI.)
|
||||
:param file_format: Config file format (default: xml)
|
||||
:param _connection_user: Optional - zabbix user (can also be set in opts or pillar, see module's docstring)
|
||||
:param _connection_password: Optional - zabbix password (can also be set in opts or pillar, see module's docstring)
|
||||
:param _connection_url: Optional - url of zabbix frontend (can also be set in opts, pillar, see module's docstring)
|
||||
|
||||
CLI Example:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
salt '*' zabbix.configuration_import salt://zabbix/config/zabbix_templates.xml \
|
||||
"{'screens': {'createMissing': True, 'updateExisting': True}}"
|
||||
'''
|
||||
if rules is None:
|
||||
rules = {}
|
||||
default_rules = {'applications': {'createMissing': True, 'updateExisting': False, 'deleteMissing': False},
|
||||
'discoveryRules': {'createMissing': True, 'updateExisting': True, 'deleteMissing': False},
|
||||
'graphs': {'createMissing': True, 'updateExisting': True, 'deleteMissing': False},
|
||||
'groups': {'createMissing': True},
|
||||
'hosts': {'createMissing': False, 'updateExisting': False},
|
||||
'images': {'createMissing': False, 'updateExisting': False},
|
||||
'items': {'createMissing': True, 'updateExisting': True, 'deleteMissing': False},
|
||||
'maps': {'createMissing': False, 'updateExisting': False},
|
||||
'screens': {'createMissing': False, 'updateExisting': False},
|
||||
'templateLinkage': {'createMissing': True},
|
||||
'templates': {'createMissing': True, 'updateExisting': True},
|
||||
'templateScreens': {'createMissing': True, 'updateExisting': True, 'deleteMissing': False},
|
||||
'triggers': {'createMissing': True, 'updateExisting': True, 'deleteMissing': False},
|
||||
'valueMaps': {'createMissing': True, 'updateExisting': False}}
|
||||
new_rules = dict(default_rules)
|
||||
|
||||
if rules:
|
||||
for rule in rules:
|
||||
if rule in new_rules:
|
||||
new_rules[rule].update(rules[rule])
|
||||
else:
|
||||
new_rules[rule] = rules[rule]
|
||||
if 'salt://' in config_file:
|
||||
tmpfile = salt.utils.mkstemp()
|
||||
cfile = __salt__['cp.get_file'](config_file, tmpfile)
|
||||
if not cfile or os.path.getsize(cfile) == 0:
|
||||
return {'name': config_file, 'result': False, 'message': 'Failed to fetch config file.'}
|
||||
else:
|
||||
cfile = config_file
|
||||
if not os.path.isfile(cfile):
|
||||
return {'name': config_file, 'result': False, 'message': 'Invalid file path.'}
|
||||
|
||||
with salt.utils.fopen(cfile, mode='r') as fp_:
|
||||
xml = fp_.read()
|
||||
|
||||
if 'salt://' in config_file:
|
||||
salt.utils.safe_rm(cfile)
|
||||
|
||||
params = {'format': file_format,
|
||||
'rules': new_rules,
|
||||
'source': xml}
|
||||
log.info('CONFIGURATION IMPORT: rules: %s', six.text_type(params['rules']))
|
||||
try:
|
||||
run_query('configuration.import', params, **connection_args)
|
||||
return {'name': config_file, 'result': True, 'message': 'Zabbix API "configuration.import" method '
|
||||
'called successfully.'}
|
||||
except SaltException as exc:
|
||||
return {'name': config_file, 'result': False, 'message': six.text_type(exc)}
|
||||
|
195
salt/states/zabbix_action.py
Normal file
195
salt/states/zabbix_action.py
Normal file
@ -0,0 +1,195 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
.. versionadded:: 2017.7
|
||||
|
||||
Management of Zabbix Action object over Zabbix API.
|
||||
|
||||
:codeauthor: Jakub Sliva <jakub.sliva@ultimum.io>
|
||||
'''
|
||||
from __future__ import absolute_import
|
||||
from __future__ import unicode_literals
|
||||
import logging
|
||||
import json
|
||||
|
||||
try:
|
||||
from salt.ext import six
|
||||
from salt.exceptions import SaltException
|
||||
IMPORTS_OK = True
|
||||
except ImportError:
|
||||
IMPORTS_OK = False
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def __virtual__():
|
||||
'''
|
||||
Only make these states available if Zabbix module and run_query function is available
|
||||
and all 3rd party modules imported.
|
||||
'''
|
||||
if 'zabbix.run_query' in __salt__ and IMPORTS_OK:
|
||||
return True
|
||||
return False, 'Import zabbix or other needed modules failed.'
|
||||
|
||||
|
||||
def present(name, params, **kwargs):
|
||||
'''
|
||||
Creates Zabbix Action object or if differs update it according defined parameters
|
||||
|
||||
:param name: Zabbix Action name
|
||||
:param params: Definition of the Zabbix Action
|
||||
:param _connection_user: Optional - zabbix user (can also be set in opts or pillar, see module's docstring)
|
||||
:param _connection_password: Optional - zabbix password (can also be set in opts or pillar, see module's docstring)
|
||||
:param _connection_url: Optional - url of zabbix frontend (can also be set in opts, pillar, see module's docstring)
|
||||
|
||||
If there is a need to get a value from current zabbix online (e.g. id of a hostgroup you want to put a discovered
|
||||
system into), put a dictionary with two keys "query_object" and "query_name" instead of the value.
|
||||
In this example we want to get object id of hostgroup named "Virtual machines" and "Databases".
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
zabbix-action-present:
|
||||
zabbix_action.present:
|
||||
- name: VMs
|
||||
- params:
|
||||
eventsource: 2
|
||||
status: 0
|
||||
filter:
|
||||
evaltype: 2
|
||||
conditions:
|
||||
- conditiontype: 24
|
||||
operator: 2
|
||||
value: 'virtual'
|
||||
- conditiontype: 24
|
||||
operator: 2
|
||||
value: 'kvm'
|
||||
operations:
|
||||
- operationtype: 2
|
||||
- operationtype: 4
|
||||
opgroup:
|
||||
- groupid:
|
||||
query_object: hostgroup
|
||||
query_name: Virtual machines
|
||||
- groupid:
|
||||
query_object: hostgroup
|
||||
query_name: Databases
|
||||
'''
|
||||
zabbix_id_mapper = __salt__['zabbix.get_zabbix_id_mapper']()
|
||||
|
||||
dry_run = __opts__['test']
|
||||
ret = {'name': name, 'result': False, 'comment': '', 'changes': {}}
|
||||
|
||||
# Create input params substituting functions with their results
|
||||
params['name'] = name
|
||||
params['operations'] = params['operations'] if 'operations' in params else []
|
||||
if 'filter' in params:
|
||||
params['filter']['conditions'] = params['filter']['conditions'] if 'conditions' in params['filter'] else []
|
||||
|
||||
input_params = __salt__['zabbix.substitute_params'](params, **kwargs)
|
||||
log.info('Zabbix Action: input params: %s', six.text_type(json.dumps(input_params, indent=4)))
|
||||
|
||||
search = {'output': 'extend',
|
||||
'selectOperations': 'extend',
|
||||
'selectFilter': 'extend',
|
||||
'filter': {
|
||||
'name': name
|
||||
}}
|
||||
# GET Action object if exists
|
||||
action_get = __salt__['zabbix.run_query']('action.get', search, **kwargs)
|
||||
log.info('Zabbix Action: action.get result: %s', six.text_type(json.dumps(action_get, indent=4)))
|
||||
|
||||
existing_obj = __salt__['zabbix.substitute_params'](action_get[0], **kwargs) \
|
||||
if action_get and len(action_get) == 1 else False
|
||||
|
||||
if existing_obj:
|
||||
diff_params = __salt__['zabbix.compare_params'](input_params, existing_obj)
|
||||
log.info('Zabbix Action: input params: {%s', six.text_type(json.dumps(input_params, indent=4)))
|
||||
log.info('Zabbix Action: Object comparison result. Differences: %s', six.text_type(diff_params))
|
||||
|
||||
if diff_params:
|
||||
diff_params[zabbix_id_mapper['action']] = existing_obj[zabbix_id_mapper['action']]
|
||||
# diff_params['name'] = 'VMs' - BUG - https://support.zabbix.com/browse/ZBX-12078
|
||||
log.info('Zabbix Action: update params: %s', six.text_type(json.dumps(diff_params, indent=4)))
|
||||
|
||||
if dry_run:
|
||||
ret['result'] = True
|
||||
ret['comment'] = 'Zabbix Action "{0}" would be fixed.'.format(name)
|
||||
ret['changes'] = {name: {'old': 'Zabbix Action "{0}" differs '
|
||||
'in following parameters: {1}'.format(name, diff_params),
|
||||
'new': 'Zabbix Action "{0}" would correspond to definition.'.format(name)}}
|
||||
else:
|
||||
action_update = __salt__['zabbix.run_query']('action.update', diff_params, **kwargs)
|
||||
log.info('Zabbix Action: action.update result: %s', six.text_type(action_update))
|
||||
if action_update:
|
||||
ret['result'] = True
|
||||
ret['comment'] = 'Zabbix Action "{0}" updated.'.format(name)
|
||||
ret['changes'] = {name: {'old': 'Zabbix Action "{0}" differed '
|
||||
'in following parameters: {1}'.format(name, diff_params),
|
||||
'new': 'Zabbix Action "{0}" fixed.'.format(name)}}
|
||||
|
||||
else:
|
||||
ret['result'] = True
|
||||
ret['comment'] = 'Zabbix Action "{0}" already exists and corresponds to a definition.'.format(name)
|
||||
|
||||
else:
|
||||
if dry_run:
|
||||
ret['result'] = True
|
||||
ret['comment'] = 'Zabbix Action "{0}" would be created.'.format(name)
|
||||
ret['changes'] = {name: {'old': 'Zabbix Action "{0}" does not exist.'.format(name),
|
||||
'new': 'Zabbix Action "{0}" would be created according definition.'.format(name)}}
|
||||
else:
|
||||
# ACTION.CREATE
|
||||
action_create = __salt__['zabbix.run_query']('action.create', input_params, **kwargs)
|
||||
log.info('Zabbix Action: action.create result: ' + six.text_type(action_create))
|
||||
|
||||
if action_create:
|
||||
ret['result'] = True
|
||||
ret['comment'] = 'Zabbix Action "{0}" created.'.format(name)
|
||||
ret['changes'] = {name: {'old': 'Zabbix Action "{0}" did not exist.'.format(name),
|
||||
'new': 'Zabbix Action "{0}" created according definition.'.format(name)}}
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
def absent(name, **kwargs):
|
||||
'''
|
||||
Makes the Zabbix Action to be absent (either does not exist or delete it).
|
||||
|
||||
:param name: Zabbix Action name
|
||||
:param _connection_user: Optional - zabbix user (can also be set in opts or pillar, see module's docstring)
|
||||
:param _connection_password: Optional - zabbix password (can also be set in opts or pillar, see module's docstring)
|
||||
:param _connection_url: Optional - url of zabbix frontend (can also be set in opts, pillar, see module's docstring)
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
zabbix-action-absent:
|
||||
zabbix_action.absent:
|
||||
- name: Action name
|
||||
'''
|
||||
dry_run = __opts__['test']
|
||||
ret = {'name': name, 'result': False, 'comment': '', 'changes': {}}
|
||||
|
||||
try:
|
||||
object_id = __salt__['zabbix.get_object_id_by_params']('action', {'filter': {'name': name}}, **kwargs)
|
||||
except SaltException:
|
||||
object_id = False
|
||||
|
||||
if not object_id:
|
||||
ret['result'] = True
|
||||
ret['comment'] = 'Zabbix Action "{0}" does not exist.'.format(name)
|
||||
else:
|
||||
if dry_run:
|
||||
ret['result'] = True
|
||||
ret['comment'] = 'Zabbix Action "{0}" would be deleted.'.format(name)
|
||||
ret['changes'] = {name: {'old': 'Zabbix Action "{0}" exists.'.format(name),
|
||||
'new': 'Zabbix Action "{0}" would be deleted.'.format(name)}}
|
||||
else:
|
||||
action_delete = __salt__['zabbix.run_query']('action.delete', [object_id], **kwargs)
|
||||
|
||||
if action_delete:
|
||||
ret['result'] = True
|
||||
ret['comment'] = 'Zabbix Action "{0}" deleted.'.format(name)
|
||||
ret['changes'] = {name: {'old': 'Zabbix Action "{0}" existed.'.format(name),
|
||||
'new': 'Zabbix Action "{0}" deleted.'.format(name)}}
|
||||
|
||||
return ret
|
708
salt/states/zabbix_template.py
Normal file
708
salt/states/zabbix_template.py
Normal file
@ -0,0 +1,708 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
.. versionadded:: 2017.7
|
||||
|
||||
Management of Zabbix Template object over Zabbix API.
|
||||
|
||||
:codeauthor: Jakub Sliva <jakub.sliva@ultimum.io>
|
||||
'''
|
||||
from __future__ import absolute_import
|
||||
from __future__ import unicode_literals
|
||||
import logging
|
||||
import json
|
||||
|
||||
try:
|
||||
from salt.ext import six
|
||||
from salt.exceptions import SaltException
|
||||
IMPORTS_OK = True
|
||||
except ImportError:
|
||||
IMPORTS_OK = False
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
TEMPLATE_RELATIONS = ['groups', 'hosts', 'macros']
|
||||
TEMPLATE_COMPONENT_ORDER = ('applications',
|
||||
'items',
|
||||
'triggers',
|
||||
'gitems',
|
||||
'graphs',
|
||||
'screens',
|
||||
'httpTests',
|
||||
'discoveries')
|
||||
DISCOVERYRULE_COMPONENT_ORDER = ('itemprototypes', 'triggerprototypes', 'graphprototypes', 'hostprototypes')
|
||||
TEMPLATE_COMPONENT_DEF = {
|
||||
# 'component': {'qtype': 'component type to query',
|
||||
# 'qidname': 'component id name',
|
||||
# 'qselectpid': 'particular component selection attribute name (parent id name)',
|
||||
# 'ptype': 'parent component type',
|
||||
# 'pid': 'parent component id',
|
||||
# 'pid_ref_name': 'component's creation reference name for parent id',
|
||||
# 'res_id_name': 'jsonrpc modification call result key name of list of affected IDs'},
|
||||
# 'output': {'output': 'extend', 'selectApplications': 'extend', 'templated': 'true'},
|
||||
# 'inherited': 'attribute name for inheritance toggling',
|
||||
# 'filter': 'child component unique identification attribute name',
|
||||
'applications': {'qtype': 'application',
|
||||
'qidname': 'applicationid',
|
||||
'qselectpid': 'templateids',
|
||||
'ptype': 'template',
|
||||
'pid': 'templateid',
|
||||
'pid_ref_name': 'hostid',
|
||||
'res_id_name': 'applicationids',
|
||||
'output': {'output': 'extend', 'templated': 'true'},
|
||||
'inherited': 'inherited',
|
||||
'adjust': True,
|
||||
'filter': 'name',
|
||||
'ro_attrs': ['applicationid', 'flags', 'templateids']},
|
||||
'items': {'qtype': 'item',
|
||||
'qidname': 'itemid',
|
||||
'qselectpid': 'templateids',
|
||||
'ptype': 'template',
|
||||
'pid': 'templateid',
|
||||
'pid_ref_name': 'hostid',
|
||||
'res_id_name': 'itemids',
|
||||
'output': {'output': 'extend', 'selectApplications': 'extend', 'templated': 'true'},
|
||||
'inherited': 'inherited',
|
||||
'adjust': False,
|
||||
'filter': 'name',
|
||||
'ro_attrs': ['itemid', 'error', 'flags', 'lastclock', 'lastns',
|
||||
'lastvalue', 'prevvalue', 'state', 'templateid']},
|
||||
'triggers': {'qtype': 'trigger',
|
||||
'qidname': 'triggerid',
|
||||
'qselectpid': 'templateids',
|
||||
'ptype': 'template',
|
||||
'pid': 'templateid',
|
||||
'pid_ref_name': None,
|
||||
'res_id_name': 'triggerids',
|
||||
'output': {'output': 'extend', 'selectDependencies': 'expand',
|
||||
'templated': 'true', 'expandExpression': 'true'},
|
||||
'inherited': 'inherited',
|
||||
'adjust': False,
|
||||
'filter': 'description',
|
||||
'ro_attrs': ['error', 'flags', 'lastchange', 'state', 'templateid', 'value']},
|
||||
'graphs': {'qtype': 'graph',
|
||||
'qidname': 'graphid',
|
||||
'qselectpid': 'templateids',
|
||||
'ptype': 'template',
|
||||
'pid': 'templateid',
|
||||
'pid_ref_name': None,
|
||||
'res_id_name': 'graphids',
|
||||
'output': {'output': 'extend', 'selectGraphItems': 'extend', 'templated': 'true'},
|
||||
'inherited': 'inherited',
|
||||
'adjust': False,
|
||||
'filter': 'name',
|
||||
'ro_attrs': ['graphid', 'flags', 'templateid']},
|
||||
'gitems': {'qtype': 'graphitem',
|
||||
'qidname': 'itemid',
|
||||
'qselectpid': 'graphids',
|
||||
'ptype': 'graph',
|
||||
'pid': 'graphid',
|
||||
'pid_ref_name': None,
|
||||
'res_id_name': None,
|
||||
'output': {'output': 'extend'},
|
||||
'inherited': 'inherited',
|
||||
'adjust': False,
|
||||
'filter': 'name',
|
||||
'ro_attrs': ['gitemid']},
|
||||
# "Template screen"
|
||||
'screens': {'qtype': 'templatescreen',
|
||||
'qidname': 'screenid',
|
||||
'qselectpid': 'templateids',
|
||||
'ptype': 'template',
|
||||
'pid': 'templateid',
|
||||
'pid_ref_name': 'templateid',
|
||||
'res_id_name': 'screenids',
|
||||
'output': {'output': 'extend', 'selectUsers': 'extend', 'selectUserGroups': 'extend',
|
||||
'selectScreenItems': 'extend', 'noInheritance': 'true'},
|
||||
'inherited': 'noInheritance',
|
||||
'adjust': False,
|
||||
'filter': 'name',
|
||||
'ro_attrs': ['screenid']},
|
||||
# "LLD rule"
|
||||
'discoveries': {'qtype': 'discoveryrule',
|
||||
'qidname': 'itemid',
|
||||
'qselectpid': 'templateids',
|
||||
'ptype': 'template',
|
||||
'pid': 'templateid',
|
||||
'pid_ref_name': 'hostid',
|
||||
'res_id_name': 'itemids',
|
||||
'output': {'output': 'extend', 'selectFilter': 'extend', 'templated': 'true'},
|
||||
'inherited': 'inherited',
|
||||
'adjust': False,
|
||||
'filter': 'key_',
|
||||
'ro_attrs': ['itemid', 'error', 'state', 'templateid']},
|
||||
# "Web scenario"
|
||||
'httpTests': {'qtype': 'httptest',
|
||||
'qidname': 'httptestid',
|
||||
'qselectpid': 'templateids',
|
||||
'ptype': 'template',
|
||||
'pid': 'templateid',
|
||||
'pid_ref_name': 'hostid',
|
||||
'res_id_name': 'httptestids',
|
||||
'output': {'output': 'extend', 'selectSteps': 'extend', 'templated': 'true'},
|
||||
'inherited': 'inherited',
|
||||
'adjust': False,
|
||||
'filter': 'name',
|
||||
'ro_attrs': ['httptestid', 'nextcheck', 'templateid']},
|
||||
# discoveries => discoveryrule
|
||||
'itemprototypes': {'qtype': 'itemprototype',
|
||||
'qidname': 'itemid',
|
||||
'qselectpid': 'discoveryids',
|
||||
'ptype': 'discoveryrule',
|
||||
'pid': 'itemid',
|
||||
'pid_ref_name': 'ruleid',
|
||||
# exception only in case of itemprototype - needs both parent ruleid and hostid
|
||||
'pid_ref_name2': 'hostid',
|
||||
'res_id_name': 'itemids',
|
||||
'output': {'output': 'extend', 'selectSteps': 'extend', 'selectApplications': 'extend',
|
||||
'templated': 'true'},
|
||||
'adjust': False,
|
||||
'inherited': 'inherited',
|
||||
'filter': 'name',
|
||||
'ro_attrs': ['itemid', 'templateid']},
|
||||
'triggerprototypes': {'qtype': 'triggerprototype',
|
||||
'qidname': 'triggerid',
|
||||
'qselectpid': 'discoveryids',
|
||||
'ptype': 'discoveryrule',
|
||||
'pid': 'itemid',
|
||||
'pid_ref_name': None,
|
||||
'res_id_name': 'triggerids',
|
||||
'output': {'output': 'extend', 'selectTags': 'extend', 'selectDependencies': 'extend',
|
||||
'templated': 'true', 'expandExpression': 'true'},
|
||||
'inherited': 'inherited',
|
||||
'adjust': False,
|
||||
'filter': 'description',
|
||||
'ro_attrs': ['triggerid', 'templateid']},
|
||||
'graphprototypes': {'qtype': 'graphprototype',
|
||||
'qidname': 'graphid',
|
||||
'qselectpid': 'discoveryids',
|
||||
'ptype': 'discoveryrule',
|
||||
'pid': 'itemid',
|
||||
'pid_ref_name': None,
|
||||
'res_id_name': 'graphids',
|
||||
'output': {'output': 'extend', 'selectGraphItems': 'extend', 'templated': 'true'},
|
||||
'inherited': 'inherited',
|
||||
'adjust': False,
|
||||
'filter': 'name',
|
||||
'ro_attrs': ['graphid', 'templateid']},
|
||||
'hostprototypes': {'qtype': 'hostprototype',
|
||||
'qidname': 'hostid',
|
||||
'qselectpid': 'discoveryids',
|
||||
'ptype': 'discoveryrule',
|
||||
'pid': 'itemid',
|
||||
'pid_ref_name': 'ruleid',
|
||||
'res_id_name': 'hostids',
|
||||
'output': {'output': 'extend', 'selectGroupLinks': 'expand', 'selectGroupPrototypes': 'expand',
|
||||
'selectTemplates': 'expand'},
|
||||
'inherited': 'inherited',
|
||||
'adjust': False,
|
||||
'filter': 'host',
|
||||
'ro_attrs': ['hostid', 'templateid']}
|
||||
}
|
||||
|
||||
# CHANGE_STACK = [{'component': 'items', 'action': 'create', 'params': dict|list}]
|
||||
CHANGE_STACK = []
|
||||
|
||||
|
||||
def __virtual__():
|
||||
'''
|
||||
Only make these states available if Zabbix module and run_query function is available
|
||||
and all 3rd party modules imported.
|
||||
'''
|
||||
if 'zabbix.run_query' in __salt__ and IMPORTS_OK:
|
||||
return True
|
||||
return False, 'Import zabbix or other needed modules failed.'
|
||||
|
||||
|
||||
def _diff_and_merge_host_list(defined, existing):
|
||||
'''
|
||||
If Zabbix template is to be updated then list of assigned hosts must be provided in all or nothing manner to prevent
|
||||
some externally assigned hosts to be detached.
|
||||
|
||||
:param defined: list of hosts defined in sls
|
||||
:param existing: list of hosts taken from live Zabbix
|
||||
:return: list to be updated (combinated or empty list)
|
||||
'''
|
||||
try:
|
||||
defined_host_ids = set([host['hostid'] for host in defined])
|
||||
existing_host_ids = set([host['hostid'] for host in existing])
|
||||
except KeyError:
|
||||
raise SaltException('List of hosts in template not defined correctly.')
|
||||
|
||||
diff = defined_host_ids - existing_host_ids
|
||||
return [{'hostid': six.text_type(hostid)} for hostid in diff | existing_host_ids] if diff else []
|
||||
|
||||
|
||||
def _get_existing_template_c_list(component, parent_id, **kwargs):
|
||||
'''
|
||||
Make a list of given component type not inherited from other templates because Zabbix API returns only list of all
|
||||
and list of inherited component items so we have to do a difference list.
|
||||
|
||||
:param component: Template component (application, item, etc...)
|
||||
:param parent_id: ID of existing template the component is assigned to
|
||||
:return List of non-inherited (own) components
|
||||
'''
|
||||
c_def = TEMPLATE_COMPONENT_DEF[component]
|
||||
q_params = dict(c_def['output'])
|
||||
q_params.update({c_def['qselectpid']: parent_id})
|
||||
|
||||
existing_clist_all = __salt__['zabbix.run_query'](c_def['qtype'] + '.get', q_params, **kwargs)
|
||||
|
||||
# in some cases (e.g. templatescreens) the logic is reversed (even name of the flag is different!)
|
||||
if c_def['inherited'] == 'inherited':
|
||||
q_params.update({c_def['inherited']: 'true'})
|
||||
existing_clist_inherited = __salt__['zabbix.run_query'](c_def['qtype'] + '.get', q_params, **kwargs)
|
||||
else:
|
||||
existing_clist_inherited = []
|
||||
|
||||
if existing_clist_inherited:
|
||||
return [c_all for c_all in existing_clist_all if c_all not in existing_clist_inherited]
|
||||
|
||||
return existing_clist_all
|
||||
|
||||
|
||||
def _adjust_object_lists(obj):
|
||||
'''
|
||||
For creation or update of object that have attribute which contains a list Zabbix awaits plain list of IDs while
|
||||
querying Zabbix for same object returns list of dicts
|
||||
|
||||
:param obj: Zabbix object parameters
|
||||
'''
|
||||
for subcomp in TEMPLATE_COMPONENT_DEF:
|
||||
if subcomp in obj and TEMPLATE_COMPONENT_DEF[subcomp]['adjust']:
|
||||
obj[subcomp] = [item[TEMPLATE_COMPONENT_DEF[subcomp]['qidname']] for item in obj[subcomp]]
|
||||
|
||||
|
||||
def _manage_component(component, parent_id, defined, existing, template_id=None, **kwargs):
|
||||
'''
|
||||
Takes particular component list, compares it with existing, call appropriate API methods - create, update, delete.
|
||||
|
||||
:param component: component name
|
||||
:param parent_id: ID of parent entity under which component should be created
|
||||
:param defined: list of defined items of named component
|
||||
:param existing: list of existing items of named component
|
||||
:param template_id: In case that component need also template ID for creation (although parent_id is given?!?!?)
|
||||
'''
|
||||
zabbix_id_mapper = __salt__['zabbix.get_zabbix_id_mapper']()
|
||||
|
||||
dry_run = __opts__['test']
|
||||
c_def = TEMPLATE_COMPONENT_DEF[component]
|
||||
compare_key = c_def['filter']
|
||||
|
||||
defined_set = set([item[compare_key] for item in defined])
|
||||
existing_set = set([item[compare_key] for item in existing])
|
||||
|
||||
create_set = defined_set - existing_set
|
||||
update_set = defined_set & existing_set
|
||||
delete_set = existing_set - defined_set
|
||||
|
||||
create_list = [item for item in defined if item[compare_key] in create_set]
|
||||
for object_params in create_list:
|
||||
if parent_id:
|
||||
object_params.update({c_def['pid_ref_name']: parent_id})
|
||||
|
||||
if 'pid_ref_name2' in c_def:
|
||||
object_params.update({c_def['pid_ref_name2']: template_id})
|
||||
|
||||
_adjust_object_lists(object_params)
|
||||
|
||||
if not dry_run:
|
||||
object_create = __salt__['zabbix.run_query'](c_def['qtype'] + '.create', object_params, **kwargs)
|
||||
if object_create:
|
||||
object_ids = object_create[c_def['res_id_name']]
|
||||
CHANGE_STACK.append({'component': component, 'action': 'create', 'params': object_params,
|
||||
c_def['filter']: object_params[c_def['filter']], 'object_id': object_ids})
|
||||
else:
|
||||
CHANGE_STACK.append({'component': component, 'action': 'create', 'params': object_params,
|
||||
'object_id': 'CREATED '+TEMPLATE_COMPONENT_DEF[component]['qtype']+' ID'})
|
||||
|
||||
delete_list = [item for item in existing if item[compare_key] in delete_set]
|
||||
for object_del in delete_list:
|
||||
object_id_name = zabbix_id_mapper[c_def['qtype']]
|
||||
CHANGE_STACK.append({'component': component, 'action': 'delete', 'params': [object_del[object_id_name]]})
|
||||
if not dry_run:
|
||||
__salt__['zabbix.run_query'](c_def['qtype'] + '.delete', [object_del[object_id_name]], **kwargs)
|
||||
|
||||
for object_name in update_set:
|
||||
ditem = next((item for item in defined if item[compare_key] == object_name), None)
|
||||
eitem = next((item for item in existing if item[compare_key] == object_name), None)
|
||||
diff_params = __salt__['zabbix.compare_params'](ditem, eitem, True)
|
||||
|
||||
if diff_params['new']:
|
||||
diff_params['new'][zabbix_id_mapper[c_def['qtype']]] = eitem[zabbix_id_mapper[c_def['qtype']]]
|
||||
diff_params['old'][zabbix_id_mapper[c_def['qtype']]] = eitem[zabbix_id_mapper[c_def['qtype']]]
|
||||
_adjust_object_lists(diff_params['new'])
|
||||
_adjust_object_lists(diff_params['old'])
|
||||
CHANGE_STACK.append({'component': component, 'action': 'update', 'params': diff_params['new']})
|
||||
|
||||
if not dry_run:
|
||||
__salt__['zabbix.run_query'](c_def['qtype'] + '.update', diff_params['new'], **kwargs)
|
||||
|
||||
|
||||
# pylint: disable=too-many-statements,too-many-locals
|
||||
def present(name, params, static_host_list=True, **kwargs):
|
||||
'''
|
||||
Creates Zabbix Template object or if differs update it according defined parameters. See Zabbix API documentation.
|
||||
|
||||
Zabbix API version: >3.0
|
||||
|
||||
:param name: Zabbix Template name
|
||||
:param params: Additional parameters according to Zabbix API documentation
|
||||
:param static_host_list: If hosts assigned to the template are controlled only by this state or can be also
|
||||
assigned externally
|
||||
:param _connection_user: Optional - zabbix user (can also be set in opts or pillar, see module's docstring)
|
||||
:param _connection_password: Optional - zabbix password (can also be set in opts or pillar, see module's docstring)
|
||||
:param _connection_url: Optional - url of zabbix frontend (can also be set in opts, pillar, see module's docstring)
|
||||
|
||||
.. note::
|
||||
|
||||
If there is a need to get a value from current zabbix online (e.g. ids of host groups you want the template
|
||||
to be associated with), put a dictionary with two keys "query_object" and "query_name" instead of the value.
|
||||
In this example we want to create template named "Testing Template", assign it to hostgroup Templates,
|
||||
link it to two ceph nodes and create a macro.
|
||||
|
||||
.. note::
|
||||
|
||||
IMPORTANT NOTE:
|
||||
Objects (except for template name) are identified by name (or by other key in some exceptional cases)
|
||||
so changing name of object means deleting old one and creating new one with new ID !!!
|
||||
|
||||
.. note::
|
||||
|
||||
NOT SUPPORTED FEATURES:
|
||||
- linked templates
|
||||
- trigger dependencies
|
||||
- groups and group prototypes for host prototypes
|
||||
|
||||
SLS Example:
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
zabbix-template-present:
|
||||
zabbix_template.present:
|
||||
- name: Testing Template
|
||||
# Do not touch existing assigned hosts
|
||||
# True will detach all other hosts than defined here
|
||||
- static_host_list: False
|
||||
- params:
|
||||
description: Template for Ceph nodes
|
||||
groups:
|
||||
# groups must already exist
|
||||
# template must be at least in one hostgroup
|
||||
- groupid:
|
||||
query_object: hostgroup
|
||||
query_name: Templates
|
||||
macros:
|
||||
- macro: "{$CEPH_CLUSTER_NAME}"
|
||||
value: ceph
|
||||
hosts:
|
||||
# hosts must already exist
|
||||
- hostid:
|
||||
query_object: host
|
||||
query_name: ceph-osd-01
|
||||
- hostid:
|
||||
query_object: host
|
||||
query_name: ceph-osd-02
|
||||
# templates:
|
||||
# Linked templates - not supported by state module but can be linked manually (will not be touched)
|
||||
|
||||
applications:
|
||||
- name: Ceph OSD
|
||||
items:
|
||||
- name: Ceph OSD avg fill item
|
||||
key_: ceph.osd_avg_fill
|
||||
type: 2
|
||||
value_type: 0
|
||||
delay: 60
|
||||
units: '%'
|
||||
description: 'Average fill of OSD'
|
||||
applications:
|
||||
- applicationid:
|
||||
query_object: application
|
||||
query_name: Ceph OSD
|
||||
triggers:
|
||||
- description: "Ceph OSD filled more that 90%"
|
||||
expression: "{{'{'}}Testing Template:ceph.osd_avg_fill.last(){{'}'}}>90"
|
||||
priority: 4
|
||||
discoveries:
|
||||
- name: Mounted filesystem discovery
|
||||
key_: vfs.fs.discovery
|
||||
type: 0
|
||||
delay: 60
|
||||
itemprototypes:
|
||||
- name: Free disk space on {{'{#'}}FSNAME}
|
||||
key_: vfs.fs.size[{{'{#'}}FSNAME},free]
|
||||
type: 0
|
||||
value_type: 3
|
||||
delay: 60
|
||||
applications:
|
||||
- applicationid:
|
||||
query_object: application
|
||||
query_name: Ceph OSD
|
||||
triggerprototypes:
|
||||
- description: "Free disk space is less than 20% on volume {{'{#'}}FSNAME{{'}'}}"
|
||||
expression: "{{'{'}}Testing Template:vfs.fs.size[{{'{#'}}FSNAME},free].last(){{'}'}}<20"
|
||||
graphs:
|
||||
- name: Ceph OSD avg fill graph
|
||||
width: 900
|
||||
height: 200
|
||||
graphtype: 0
|
||||
gitems:
|
||||
- color: F63100
|
||||
itemid:
|
||||
query_object: item
|
||||
query_name: Ceph OSD avg fill item
|
||||
screens:
|
||||
- name: Ceph
|
||||
hsize: 1
|
||||
vsize: 1
|
||||
screenitems:
|
||||
- x: 0
|
||||
y: 0
|
||||
resourcetype: 0
|
||||
resourceid:
|
||||
query_object: graph
|
||||
query_name: Ceph OSD avg fill graph
|
||||
'''
|
||||
zabbix_id_mapper = __salt__['zabbix.get_zabbix_id_mapper']()
|
||||
|
||||
dry_run = __opts__['test']
|
||||
ret = {'name': name, 'result': False, 'comment': '', 'changes': {}}
|
||||
params['host'] = name
|
||||
|
||||
# Divide template yaml definition into parts
|
||||
# - template definition itself
|
||||
# - simple template components
|
||||
# - components that have other sub-components
|
||||
# (e.g. discoveries - where parent ID is needed in advance for sub-component manipulation)
|
||||
template_definition = {}
|
||||
template_components = {}
|
||||
discovery_components = []
|
||||
|
||||
for attr in params:
|
||||
if attr in TEMPLATE_COMPONENT_ORDER and six.text_type(attr) != 'discoveries':
|
||||
template_components[attr] = params[attr]
|
||||
|
||||
elif six.text_type(attr) == 'discoveries':
|
||||
d_rules = []
|
||||
for d_rule in params[attr]:
|
||||
d_rule_components = {'query_pid': {'component': attr,
|
||||
'filter_val': d_rule[TEMPLATE_COMPONENT_DEF[attr]['filter']]}}
|
||||
for proto_name in DISCOVERYRULE_COMPONENT_ORDER:
|
||||
if proto_name in d_rule:
|
||||
d_rule_components[proto_name] = d_rule[proto_name]
|
||||
del d_rule[proto_name]
|
||||
|
||||
discovery_components.append(d_rule_components)
|
||||
d_rules.append(d_rule)
|
||||
|
||||
template_components[attr] = d_rules
|
||||
|
||||
else:
|
||||
template_definition[attr] = params[attr]
|
||||
|
||||
# if a component is not defined, it means to remove existing items during update (empty list)
|
||||
for attr in TEMPLATE_COMPONENT_ORDER:
|
||||
if attr not in template_components:
|
||||
template_components[attr] = []
|
||||
|
||||
# if a component is not defined, it means to remove existing items during update (empty list)
|
||||
for attr in TEMPLATE_RELATIONS:
|
||||
template_definition[attr] = params[attr] if attr in params and params[attr] else []
|
||||
|
||||
defined_obj = __salt__['zabbix.substitute_params'](template_definition, **kwargs)
|
||||
log.info('SUBSTITUTED template_definition: %s', six.text_type(json.dumps(defined_obj, indent=4)))
|
||||
|
||||
tmpl_get = __salt__['zabbix.run_query']('template.get',
|
||||
{'output': 'extend', 'selectGroups': 'groupid', 'selectHosts': 'hostid',
|
||||
'selectTemplates': 'templateid', 'selectMacros': 'extend',
|
||||
'filter': {'host': name}},
|
||||
**kwargs)
|
||||
log.info('TEMPLATE get result: %s', six.text_type(json.dumps(tmpl_get, indent=4)))
|
||||
|
||||
existing_obj = __salt__['zabbix.substitute_params'](tmpl_get[0], **kwargs) \
|
||||
if tmpl_get and len(tmpl_get) == 1 else False
|
||||
|
||||
if existing_obj:
|
||||
template_id = existing_obj[zabbix_id_mapper['template']]
|
||||
|
||||
if not static_host_list:
|
||||
# Prepare objects for comparison
|
||||
defined_wo_hosts = defined_obj
|
||||
if 'hosts' in defined_obj:
|
||||
defined_hosts = defined_obj['hosts']
|
||||
del defined_wo_hosts['hosts']
|
||||
else:
|
||||
defined_hosts = []
|
||||
|
||||
existing_wo_hosts = existing_obj
|
||||
if 'hosts' in existing_obj:
|
||||
existing_hosts = existing_obj['hosts']
|
||||
del existing_wo_hosts['hosts']
|
||||
else:
|
||||
existing_hosts = []
|
||||
|
||||
# Compare host list separately from the rest of the object comparison since the merged list is needed for
|
||||
# update
|
||||
hosts_list = _diff_and_merge_host_list(defined_hosts, existing_hosts)
|
||||
|
||||
# Compare objects without hosts
|
||||
diff_params = __salt__['zabbix.compare_params'](defined_wo_hosts, existing_wo_hosts, True)
|
||||
|
||||
# Merge comparison results together
|
||||
if ('new' in diff_params and 'hosts' in diff_params['new']) or hosts_list:
|
||||
diff_params['new']['hosts'] = hosts_list
|
||||
|
||||
else:
|
||||
diff_params = __salt__['zabbix.compare_params'](defined_obj, existing_obj, True)
|
||||
|
||||
if diff_params['new']:
|
||||
diff_params['new'][zabbix_id_mapper['template']] = template_id
|
||||
diff_params['old'][zabbix_id_mapper['template']] = template_id
|
||||
log.info('TEMPLATE: update params: %s', six.text_type(json.dumps(diff_params, indent=4)))
|
||||
|
||||
CHANGE_STACK.append({'component': 'template', 'action': 'update', 'params': diff_params['new']})
|
||||
if not dry_run:
|
||||
tmpl_update = __salt__['zabbix.run_query']('template.update', diff_params['new'], **kwargs)
|
||||
log.info('TEMPLATE update result: %s', six.text_type(tmpl_update))
|
||||
|
||||
else:
|
||||
CHANGE_STACK.append({'component': 'template', 'action': 'create', 'params': defined_obj})
|
||||
if not dry_run:
|
||||
tmpl_create = __salt__['zabbix.run_query']('template.create', defined_obj, **kwargs)
|
||||
log.info('TEMPLATE create result: ' + six.text_type(tmpl_create))
|
||||
if tmpl_create:
|
||||
template_id = tmpl_create['templateids'][0]
|
||||
|
||||
log.info('\n\ntemplate_components: %s', json.dumps(template_components, indent=4))
|
||||
log.info('\n\ndiscovery_components: %s', json.dumps(discovery_components, indent=4))
|
||||
log.info('\n\nCurrent CHANGE_STACK: %s', six.text_type(json.dumps(CHANGE_STACK, indent=4)))
|
||||
|
||||
if existing_obj or not dry_run:
|
||||
for component in TEMPLATE_COMPONENT_ORDER:
|
||||
log.info('\n\n\n\n\nCOMPONENT: %s\n\n', six.text_type(json.dumps(component)))
|
||||
# 1) query for components which belongs to the template
|
||||
existing_c_list = _get_existing_template_c_list(component, template_id, **kwargs)
|
||||
existing_c_list_subs = __salt__['zabbix.substitute_params'](existing_c_list, **kwargs) \
|
||||
if existing_c_list else []
|
||||
|
||||
if component in template_components:
|
||||
defined_c_list_subs = __salt__['zabbix.substitute_params'](
|
||||
template_components[component],
|
||||
extend_params={TEMPLATE_COMPONENT_DEF[component]['qselectpid']: template_id},
|
||||
filter_key=TEMPLATE_COMPONENT_DEF[component]['filter'],
|
||||
**kwargs)
|
||||
else:
|
||||
defined_c_list_subs = []
|
||||
# 2) take lists of particular component and compare -> do create, update and delete actions
|
||||
_manage_component(component, template_id, defined_c_list_subs, existing_c_list_subs, **kwargs)
|
||||
|
||||
log.info('\n\nCurrent CHANGE_STACK: %s', six.text_type(json.dumps(CHANGE_STACK, indent=4)))
|
||||
|
||||
for d_rule_component in discovery_components:
|
||||
# query for parent id -> "query_pid": {"filter_val": "vfs.fs.discovery", "component": "discoveries"}
|
||||
q_def = d_rule_component['query_pid']
|
||||
c_def = TEMPLATE_COMPONENT_DEF[q_def['component']]
|
||||
q_object = c_def['qtype']
|
||||
q_params = dict(c_def['output'])
|
||||
q_params.update({c_def['qselectpid']: template_id})
|
||||
q_params.update({'filter': {c_def['filter']: q_def['filter_val']}})
|
||||
|
||||
parent_id = __salt__['zabbix.get_object_id_by_params'](q_object, q_params, **kwargs)
|
||||
|
||||
for proto_name in DISCOVERYRULE_COMPONENT_ORDER:
|
||||
log.info('\n\n\n\n\nPROTOTYPE_NAME: %s\n\n', six.text_type(json.dumps(proto_name)))
|
||||
existing_p_list = _get_existing_template_c_list(proto_name, parent_id, **kwargs)
|
||||
existing_p_list_subs = __salt__['zabbix.substitute_params'](existing_p_list, **kwargs)\
|
||||
if existing_p_list else []
|
||||
|
||||
if proto_name in d_rule_component:
|
||||
defined_p_list_subs = __salt__['zabbix.substitute_params'](
|
||||
d_rule_component[proto_name],
|
||||
extend_params={c_def['qselectpid']: template_id},
|
||||
**kwargs)
|
||||
else:
|
||||
defined_p_list_subs = []
|
||||
|
||||
_manage_component(proto_name,
|
||||
parent_id,
|
||||
defined_p_list_subs,
|
||||
existing_p_list_subs,
|
||||
template_id=template_id,
|
||||
**kwargs)
|
||||
|
||||
log.info('\n\nCurrent CHANGE_STACK: %s', six.text_type(json.dumps(CHANGE_STACK, indent=4)))
|
||||
|
||||
if not CHANGE_STACK:
|
||||
ret['result'] = True
|
||||
ret['comment'] = 'Zabbix Template "{0}" already exists and corresponds to a definition.'.format(name)
|
||||
else:
|
||||
tmpl_action = next((item for item in CHANGE_STACK
|
||||
if item['component'] == 'template' and item['action'] == 'create'), None)
|
||||
if tmpl_action:
|
||||
ret['result'] = True
|
||||
if dry_run:
|
||||
ret['comment'] = 'Zabbix Template "{0}" would be created.'.format(name)
|
||||
ret['changes'] = {name: {'old': 'Zabbix Template "{0}" does not exist.'.format(name),
|
||||
'new': 'Zabbix Template "{0}" would be created '
|
||||
'according definition.'.format(name)}}
|
||||
else:
|
||||
ret['comment'] = 'Zabbix Template "{0}" created.'.format(name)
|
||||
ret['changes'] = {name: {'old': 'Zabbix Template "{0}" did not exist.'.format(name),
|
||||
'new': 'Zabbix Template "{0}" created according definition.'.format(name)}}
|
||||
else:
|
||||
ret['result'] = True
|
||||
if dry_run:
|
||||
ret['comment'] = 'Zabbix Template "{0}" would be updated.'.format(name)
|
||||
ret['changes'] = {name: {'old': 'Zabbix Template "{0}" differs.'.format(name),
|
||||
'new': 'Zabbix Template "{0}" would be updated '
|
||||
'according definition.'.format(name)}}
|
||||
else:
|
||||
ret['comment'] = 'Zabbix Template "{0}" updated.'.format(name)
|
||||
ret['changes'] = {name: {'old': 'Zabbix Template "{0}" differed.'.format(name),
|
||||
'new': 'Zabbix Template "{0}" updated according definition.'.format(name)}}
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
def absent(name, **kwargs):
|
||||
'''
|
||||
Makes the Zabbix Template to be absent (either does not exist or delete it).
|
||||
|
||||
:param name: Zabbix Template name
|
||||
:param _connection_user: Optional - zabbix user (can also be set in opts or pillar, see module's docstring)
|
||||
:param _connection_password: Optional - zabbix password (can also be set in opts or pillar, see module's docstring)
|
||||
:param _connection_url: Optional - url of zabbix frontend (can also be set in opts, pillar, see module's docstring)
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
zabbix-template-absent:
|
||||
zabbix_template.absent:
|
||||
- name: Ceph OSD
|
||||
'''
|
||||
dry_run = __opts__['test']
|
||||
ret = {'name': name, 'result': False, 'comment': '', 'changes': {}}
|
||||
|
||||
try:
|
||||
object_id = __salt__['zabbix.get_object_id_by_params']('template', {'filter': {'name': name}}, **kwargs)
|
||||
except SaltException:
|
||||
object_id = False
|
||||
|
||||
if not object_id:
|
||||
ret['result'] = True
|
||||
ret['comment'] = 'Zabbix Template "{0}" does not exist.'.format(name)
|
||||
else:
|
||||
if dry_run:
|
||||
ret['result'] = True
|
||||
ret['comment'] = 'Zabbix Template "{0}" would be deleted.'.format(name)
|
||||
ret['changes'] = {name: {'old': 'Zabbix Template "{0}" exists.'.format(name),
|
||||
'new': 'Zabbix Template "{0}" would be deleted.'.format(name)}}
|
||||
else:
|
||||
tmpl_delete = __salt__['zabbix.run_query']('template.delete', [object_id], **kwargs)
|
||||
if tmpl_delete:
|
||||
ret['result'] = True
|
||||
ret['comment'] = 'Zabbix Template "{0}" deleted.'.format(name)
|
||||
ret['changes'] = {name: {'old': 'Zabbix Template "{0}" existed.'.format(name),
|
||||
'new': 'Zabbix Template "{0}" deleted.'.format(name)}}
|
||||
|
||||
return ret
|
170
salt/states/zabbix_valuemap.py
Normal file
170
salt/states/zabbix_valuemap.py
Normal file
@ -0,0 +1,170 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
.. versionadded:: 2017.7
|
||||
|
||||
Management of Zabbix Valuemap object over Zabbix API.
|
||||
|
||||
:codeauthor: Jakub Sliva <jakub.sliva@ultimum.io>
|
||||
'''
|
||||
from __future__ import absolute_import
|
||||
from __future__ import unicode_literals
|
||||
import logging
|
||||
import json
|
||||
|
||||
try:
|
||||
from salt.ext import six
|
||||
from salt.exceptions import SaltException
|
||||
IMPORTS_OK = True
|
||||
except ImportError:
|
||||
IMPORTS_OK = False
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def __virtual__():
|
||||
'''
|
||||
Only make these states available if Zabbix module and run_query function is available
|
||||
and all 3rd party modules imported.
|
||||
'''
|
||||
if 'zabbix.run_query' in __salt__ and IMPORTS_OK:
|
||||
return True
|
||||
return False, 'Import zabbix or other needed modules failed.'
|
||||
|
||||
|
||||
def present(name, params, **kwargs):
|
||||
'''
|
||||
Creates Zabbix Value map object or if differs update it according defined parameters
|
||||
|
||||
:param name: Zabbix Value map name
|
||||
:param params: Definition of the Zabbix Value map
|
||||
:param _connection_user: Optional - zabbix user (can also be set in opts or pillar, see module's docstring)
|
||||
:param _connection_password: Optional - zabbix password (can also be set in opts or pillar, see module's docstring)
|
||||
:param _connection_url: Optional - url of zabbix frontend (can also be set in opts, pillar, see module's docstring)
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
zabbix-valuemap-present:
|
||||
zabbix_valuemap.present:
|
||||
- name: Number mapping
|
||||
- params:
|
||||
mappings:
|
||||
- value: 1
|
||||
newvalue: one
|
||||
- value: 2
|
||||
newvalue: two
|
||||
'''
|
||||
zabbix_id_mapper = __salt__['zabbix.get_zabbix_id_mapper']()
|
||||
|
||||
dry_run = __opts__['test']
|
||||
ret = {'name': name, 'result': False, 'comment': '', 'changes': {}}
|
||||
|
||||
# Create input params substituting functions with their results
|
||||
params['name'] = name
|
||||
input_params = __salt__['zabbix.substitute_params'](params, **kwargs)
|
||||
log.info('Zabbix Value map: input params: %s', six.text_type(json.dumps(input_params, indent=4)))
|
||||
|
||||
search = {'output': 'extend',
|
||||
'selectMappings': 'extend',
|
||||
'filter': {
|
||||
'name': name
|
||||
}}
|
||||
# GET Value map object if exists
|
||||
valuemap_get = __salt__['zabbix.run_query']('valuemap.get', search, **kwargs)
|
||||
log.info('Zabbix Value map: valuemap.get result: %s', six.text_type(json.dumps(valuemap_get, indent=4)))
|
||||
|
||||
existing_obj = __salt__['zabbix.substitute_params'](valuemap_get[0], **kwargs) \
|
||||
if valuemap_get and len(valuemap_get) == 1 else False
|
||||
|
||||
if existing_obj:
|
||||
diff_params = __salt__['zabbix.compare_params'](input_params, existing_obj)
|
||||
log.info('Zabbix Value map: input params: {%s', six.text_type(json.dumps(input_params, indent=4)))
|
||||
log.info('Zabbix Value map: Object comparison result. Differences: %s', six.text_type(diff_params))
|
||||
|
||||
if diff_params:
|
||||
diff_params[zabbix_id_mapper['valuemap']] = existing_obj[zabbix_id_mapper['valuemap']]
|
||||
log.info('Zabbix Value map: update params: %s', six.text_type(json.dumps(diff_params, indent=4)))
|
||||
|
||||
if dry_run:
|
||||
ret['result'] = True
|
||||
ret['comment'] = 'Zabbix Value map "{0}" would be fixed.'.format(name)
|
||||
ret['changes'] = {name: {'old': 'Zabbix Value map "{0}" differs '
|
||||
'in following parameters: {1}'.format(name, diff_params),
|
||||
'new': 'Zabbix Value map "{0}" would correspond to definition.'.format(name)}}
|
||||
else:
|
||||
valuemap_update = __salt__['zabbix.run_query']('valuemap.update', diff_params, **kwargs)
|
||||
log.info('Zabbix Value map: valuemap.update result: %s', six.text_type(valuemap_update))
|
||||
if valuemap_update:
|
||||
ret['result'] = True
|
||||
ret['comment'] = 'Zabbix Value map "{0}" updated.'.format(name)
|
||||
ret['changes'] = {name: {'old': 'Zabbix Value map "{0}" differed '
|
||||
'in following parameters: {1}'.format(name, diff_params),
|
||||
'new': 'Zabbix Value map "{0}" fixed.'.format(name)}}
|
||||
|
||||
else:
|
||||
ret['result'] = True
|
||||
ret['comment'] = 'Zabbix Value map "{0}" already exists and corresponds to a definition.'.format(name)
|
||||
|
||||
else:
|
||||
if dry_run:
|
||||
ret['result'] = True
|
||||
ret['comment'] = 'Zabbix Value map "{0}" would be created.'.format(name)
|
||||
ret['changes'] = {name: {'old': 'Zabbix Value map "{0}" does not exist.'.format(name),
|
||||
'new': 'Zabbix Value map "{0}" would be created '
|
||||
'according definition.'.format(name)}}
|
||||
else:
|
||||
# ACTION.CREATE
|
||||
valuemap_create = __salt__['zabbix.run_query']('valuemap.create', input_params, **kwargs)
|
||||
log.info('Zabbix Value map: valuemap.create result: ' + six.text_type(valuemap_create))
|
||||
|
||||
if valuemap_create:
|
||||
ret['result'] = True
|
||||
ret['comment'] = 'Zabbix Value map "{0}" created.'.format(name)
|
||||
ret['changes'] = {name: {'old': 'Zabbix Value map "{0}" did not exist.'.format(name),
|
||||
'new': 'Zabbix Value map "{0}" created according definition.'.format(name)}}
|
||||
|
||||
return ret
|
||||
|
||||
|
||||
def absent(name, **kwargs):
|
||||
'''
|
||||
Makes the Zabbix Value map to be absent (either does not exist or delete it).
|
||||
|
||||
:param name: Zabbix Value map name
|
||||
:param _connection_user: Optional - zabbix user (can also be set in opts or pillar, see module's docstring)
|
||||
:param _connection_password: Optional - zabbix password (can also be set in opts or pillar, see module's docstring)
|
||||
:param _connection_url: Optional - url of zabbix frontend (can also be set in opts, pillar, see module's docstring)
|
||||
|
||||
.. code-block:: yaml
|
||||
|
||||
zabbix-valuemap-absent:
|
||||
zabbix_valuemap.absent:
|
||||
- name: Value map name
|
||||
'''
|
||||
dry_run = __opts__['test']
|
||||
ret = {'name': name, 'result': False, 'comment': '', 'changes': {}}
|
||||
|
||||
try:
|
||||
object_id = __salt__['zabbix.get_object_id_by_params']('valuemap', {'filter': {'name': name}}, **kwargs)
|
||||
except SaltException:
|
||||
object_id = False
|
||||
|
||||
if not object_id:
|
||||
ret['result'] = True
|
||||
ret['comment'] = 'Zabbix Value map "{0}" does not exist.'.format(name)
|
||||
else:
|
||||
if dry_run:
|
||||
ret['result'] = True
|
||||
ret['comment'] = 'Zabbix Value map "{0}" would be deleted.'.format(name)
|
||||
ret['changes'] = {name: {'old': 'Zabbix Value map "{0}" exists.'.format(name),
|
||||
'new': 'Zabbix Value map "{0}" would be deleted.'.format(name)}}
|
||||
else:
|
||||
valuemap_delete = __salt__['zabbix.run_query']('valuemap.delete', [object_id], **kwargs)
|
||||
|
||||
if valuemap_delete:
|
||||
ret['result'] = True
|
||||
ret['comment'] = 'Zabbix Value map "{0}" deleted.'.format(name)
|
||||
ret['changes'] = {name: {'old': 'Zabbix Value map "{0}" existed.'.format(name),
|
||||
'new': 'Zabbix Value map "{0}" deleted.'.format(name)}}
|
||||
|
||||
return ret
|
@ -3,18 +3,97 @@
|
||||
:codeauthor: :email:`Christian McHugh <christian.mchugh@gmail.com>`
|
||||
'''
|
||||
|
||||
# Import python libs
|
||||
# Import Python Libs
|
||||
from __future__ import absolute_import
|
||||
from __future__ import unicode_literals
|
||||
import salt.modules.zabbix as zabbix
|
||||
|
||||
# Import Salt Testing libs
|
||||
# Import Salt Testing Libs
|
||||
from tests.support.mixins import LoaderModuleMockMixin
|
||||
from tests.support.unit import skipIf, TestCase
|
||||
from tests.support.mock import NO_MOCK, NO_MOCK_REASON, patch
|
||||
from tests.support.mock import (
|
||||
MagicMock,
|
||||
patch,
|
||||
NO_MOCK,
|
||||
NO_MOCK_REASON
|
||||
)
|
||||
|
||||
from salt.exceptions import SaltException
|
||||
|
||||
CONN_ARGS = {}
|
||||
CONN_ARGS['url'] = 'http://test.url'
|
||||
CONN_ARGS['auth'] = '1234'
|
||||
|
||||
GETID_QUERY_RESULT_OK = [{'internal': '0', 'flags': '0', 'groupid': '11', 'name': 'Databases'}]
|
||||
GETID_QUERY_RESULT_BAD = [{'internal': '0', 'flags': '0', 'groupid': '11', 'name': 'Databases'}, {'another': 'object'}]
|
||||
|
||||
DEFINED_PARAMS = {'name': 'beta',
|
||||
'eventsource': 2,
|
||||
'status': 0,
|
||||
'filter': {'evaltype': 2,
|
||||
'conditions': [{'conditiontype': 24,
|
||||
'operator': 2,
|
||||
'value': 'db'}]},
|
||||
'operations': [{'operationtype': 2},
|
||||
{'operationtype': 4,
|
||||
'opgroup': [{'groupid': {'query_object': 'hostgroup',
|
||||
'query_name': 'Databases'}}]}],
|
||||
'empty_list': []}
|
||||
|
||||
SUBSTITUTED_DEFINED_PARAMS = {'status': '0',
|
||||
'filter': {'evaltype': '2',
|
||||
'conditions': [{'operator': '2',
|
||||
'conditiontype': '24',
|
||||
'value': 'db'}]},
|
||||
'eventsource': '2',
|
||||
'name': 'beta',
|
||||
'operations': [{'operationtype': '2'},
|
||||
{'opgroup': [{'groupid': '11'}],
|
||||
'operationtype': '4'}],
|
||||
'empty_list': []}
|
||||
|
||||
EXISTING_OBJECT_PARAMS = {'status': '0',
|
||||
'operations': [{'operationtype': '2', 'esc_period': '0', 'evaltype': '0', 'opconditions': [],
|
||||
'esc_step_to': '1', 'actionid': '23', 'esc_step_from': '1',
|
||||
'operationid': '64'},
|
||||
{'operationtype': '4', 'esc_period': '0', 'evaltype': '0', 'opconditions': [],
|
||||
'esc_step_to': '1', 'actionid': '23', 'esc_step_from': '1',
|
||||
'opgroup': [{'groupid': '11',
|
||||
'operationid': '65'}],
|
||||
'operationid': '65'}],
|
||||
'def_shortdata': '',
|
||||
'name': 'beta',
|
||||
'esc_period': '0',
|
||||
'def_longdata': '',
|
||||
'filter': {'formula': '',
|
||||
'evaltype': '2',
|
||||
'conditions': [{'operator': '2',
|
||||
'conditiontype': '24',
|
||||
'formulaid': 'A',
|
||||
'value': 'DIFFERENT VALUE HERE'}],
|
||||
'eval_formula': 'A'},
|
||||
'eventsource': '2',
|
||||
'actionid': '23',
|
||||
'r_shortdata': '',
|
||||
'r_longdata': '',
|
||||
'recovery_msg': '0',
|
||||
'empty_list': [{'dict_key': 'dic_val'}]}
|
||||
|
||||
DIFF_PARAMS_RESULT = {'filter': {'evaltype': '2',
|
||||
'conditions': [{'operator': '2',
|
||||
'conditiontype': '24',
|
||||
'value': 'db'}]},
|
||||
'empty_list': []}
|
||||
|
||||
DIFF_PARAMS_RESULT_WITH_ROLLBACK = {'new': DIFF_PARAMS_RESULT,
|
||||
'old': {'filter': {'formula': '',
|
||||
'evaltype': '2',
|
||||
'conditions': [{'operator': '2',
|
||||
'conditiontype': '24',
|
||||
'formulaid': 'A',
|
||||
'value': 'DIFFERENT VALUE HERE'}],
|
||||
'eval_formula': 'A'},
|
||||
'empty_list': [{'dict_key': 'dic_val'}]}}
|
||||
|
||||
|
||||
@skipIf(NO_MOCK, NO_MOCK_REASON)
|
||||
@ -26,6 +105,53 @@ class ZabbixTestCase(TestCase, LoaderModuleMockMixin):
|
||||
def setup_loader_modules(self):
|
||||
return {zabbix: {'__salt__': {'cmd.which_bin': lambda _: 'zabbix_server'}}}
|
||||
|
||||
def test_get_object_id_by_params(self):
|
||||
'''
|
||||
Test get_object_id function with expected result from API call
|
||||
'''
|
||||
with patch('salt.modules.zabbix.run_query', MagicMock(return_value=GETID_QUERY_RESULT_OK)):
|
||||
self.assertEqual(zabbix.get_object_id_by_params('hostgroup', 'Databases'), '11')
|
||||
|
||||
def test_get_obj_id_by_params_fail(self):
|
||||
'''
|
||||
Test get_object_id function with unexpected result from API call
|
||||
'''
|
||||
with patch('salt.modules.zabbix.run_query', MagicMock(return_value=GETID_QUERY_RESULT_BAD)):
|
||||
self.assertRaises(SaltException, zabbix.get_object_id_by_params, 'hostgroup', 'Databases')
|
||||
|
||||
def test_substitute_params(self):
|
||||
'''
|
||||
Test proper parameter substitution for defined input
|
||||
'''
|
||||
with patch('salt.modules.zabbix.get_object_id_by_params', MagicMock(return_value='11')):
|
||||
self.assertEqual(zabbix.substitute_params(DEFINED_PARAMS), SUBSTITUTED_DEFINED_PARAMS)
|
||||
|
||||
def test_substitute_params_fail(self):
|
||||
'''
|
||||
Test proper parameter substitution if there is needed parameter missing
|
||||
'''
|
||||
self.assertRaises(SaltException, zabbix.substitute_params, {'groupid': {'query_object': 'hostgroup'}})
|
||||
|
||||
def test_compare_params(self):
|
||||
'''
|
||||
Test result comparison of two params structures
|
||||
'''
|
||||
self.assertEqual(zabbix.compare_params(SUBSTITUTED_DEFINED_PARAMS, EXISTING_OBJECT_PARAMS),
|
||||
DIFF_PARAMS_RESULT)
|
||||
|
||||
def test_compare_params_rollback(self):
|
||||
'''
|
||||
Test result comparison of two params structures with rollback return value option
|
||||
'''
|
||||
self.assertEqual(zabbix.compare_params(SUBSTITUTED_DEFINED_PARAMS, EXISTING_OBJECT_PARAMS, True),
|
||||
DIFF_PARAMS_RESULT_WITH_ROLLBACK)
|
||||
|
||||
def test_compare_params_fail(self):
|
||||
'''
|
||||
Test result comparison of two params structures where some data type mismatch exists
|
||||
'''
|
||||
self.assertRaises(SaltException, zabbix.compare_params, {'dict': 'val'}, {'dict': ['list']})
|
||||
|
||||
def test_apiiinfo_version(self):
|
||||
'''
|
||||
Test apiinfo_version
|
||||
|
187
tests/unit/states/test_zabbix_action.py
Normal file
187
tests/unit/states/test_zabbix_action.py
Normal file
@ -0,0 +1,187 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
:codeauthor: :email:`Jakub Sliva <jakub.sliva@ultimum.io>`
|
||||
'''
|
||||
|
||||
# Import Python Libs
|
||||
from __future__ import absolute_import
|
||||
from __future__ import unicode_literals
|
||||
|
||||
# Import Salt Testing Libs
|
||||
from tests.support.mixins import LoaderModuleMockMixin
|
||||
from tests.support.unit import TestCase, skipIf
|
||||
from tests.support.mock import (
|
||||
MagicMock,
|
||||
patch,
|
||||
NO_MOCK,
|
||||
NO_MOCK_REASON
|
||||
)
|
||||
|
||||
import salt.states.zabbix_action as zabbix_action
|
||||
|
||||
|
||||
INPUT_PARAMS = {
|
||||
'status': '0',
|
||||
'filter': {'evaltype': '2', 'conditions': [{'operator': '2', 'conditiontype': '24', 'value': 'database'}]},
|
||||
'eventsource': '2',
|
||||
'name': 'Auto registration Databases',
|
||||
'operations': [{'opgroup': [{'groupid': '6'}], 'operationtype': '4'}]}
|
||||
|
||||
EXISTING_OBJ = [{
|
||||
'status': '0',
|
||||
'operations': [{'operationtype': '4', 'esc_period': '0', 'evaltype': '0', 'opconditions': [],
|
||||
'esc_step_to': '1', 'actionid': '28', 'esc_step_from': '1',
|
||||
'opgroup': [{'groupid': '6', 'operationid': '92'}],
|
||||
'operationid': '92'}],
|
||||
'def_shortdata': '',
|
||||
'name': 'Auto registration Databases',
|
||||
'esc_period': '0',
|
||||
'def_longdata': '',
|
||||
'filter': {'formula': '', 'evaltype': '2', 'conditions': [{'operator': '2', 'conditiontype': '24',
|
||||
'formulaid': 'A', 'value': 'database'}],
|
||||
'eval_formula': 'A'},
|
||||
'eventsource': '2',
|
||||
'actionid': '28',
|
||||
'r_shortdata': '',
|
||||
'r_longdata': '',
|
||||
'recovery_msg': '0'}]
|
||||
|
||||
EXISTING_OBJ_DIFF = {
|
||||
'status': '0',
|
||||
'operations': [{'operationtype': '4', 'esc_period': '0', 'evaltype': '0', 'opconditions': [],
|
||||
'esc_step_to': '1', 'actionid': '28', 'esc_step_from': '1',
|
||||
'opgroup': [{'groupid': '6', 'operationid': '92'}],
|
||||
'operationid': '92'}],
|
||||
'def_shortdata': '',
|
||||
'name': 'Auto registration Databases',
|
||||
'esc_period': '0',
|
||||
'def_longdata': '',
|
||||
'filter': {'formula': '', 'evaltype': '2', 'conditions': [{'operator': '2', 'conditiontype': '24',
|
||||
'formulaid': 'A', 'value': 'SOME OTHER VALUE'}],
|
||||
'eval_formula': 'A'},
|
||||
'eventsource': '2',
|
||||
'actionid': '28',
|
||||
'r_shortdata': '',
|
||||
'r_longdata': '',
|
||||
'recovery_msg': '0'}
|
||||
|
||||
DIFF_PARAMS = {'filter': {'evaltype': '2',
|
||||
'conditions': [{'operator': '2', 'conditiontype': '24', 'value': 'virtual'}]},
|
||||
'actionid': '28'}
|
||||
|
||||
|
||||
@skipIf(NO_MOCK, NO_MOCK_REASON)
|
||||
class ZabbixActionTestCase(TestCase, LoaderModuleMockMixin):
|
||||
'''
|
||||
Test cases for salt.modules.zabbix
|
||||
'''
|
||||
def setup_loader_modules(self):
|
||||
return {zabbix_action: {}}
|
||||
|
||||
def test_present_create(self):
|
||||
'''
|
||||
Test to ensure that named action is created
|
||||
'''
|
||||
name = 'Auto registration Databases'
|
||||
ret = {'name': name, 'result': False, 'comment': '', 'changes': {}}
|
||||
|
||||
def side_effect_run_query(*args):
|
||||
'''
|
||||
Differentiate between __salt__ exec module function calls with different parameters.
|
||||
'''
|
||||
if args[0] == 'action.get':
|
||||
return False
|
||||
elif args[0] == 'action.create':
|
||||
return True
|
||||
|
||||
with patch.dict(zabbix_action.__opts__, {'test': False}):
|
||||
with patch.dict(zabbix_action.__salt__,
|
||||
{'zabbix.get_zabbix_id_mapper': MagicMock(return_value={'action': 'actionid'}),
|
||||
'zabbix.substitute_params': MagicMock(side_effect=[INPUT_PARAMS, False]),
|
||||
'zabbix.run_query': MagicMock(side_effect=side_effect_run_query),
|
||||
'zabbix.compare_params': MagicMock(return_value={})}):
|
||||
ret['result'] = True
|
||||
ret['comment'] = 'Zabbix Action "{0}" created.'.format(name)
|
||||
ret['changes'] = {name: {'old': 'Zabbix Action "{0}" did not exist.'.format(name),
|
||||
'new': 'Zabbix Action "{0}" created according definition.'.format(name)}}
|
||||
self.assertDictEqual(zabbix_action.present(name, {}), ret)
|
||||
|
||||
def test_present_exists(self):
|
||||
'''
|
||||
Test to ensure that named action is present and not changed
|
||||
'''
|
||||
name = 'Auto registration Databases'
|
||||
ret = {'name': name, 'result': False, 'comment': '', 'changes': {}}
|
||||
|
||||
with patch.dict(zabbix_action.__opts__, {'test': False}):
|
||||
with patch.dict(zabbix_action.__salt__,
|
||||
{'zabbix.get_zabbix_id_mapper': MagicMock(return_value={'action': 'actionid'}),
|
||||
'zabbix.substitute_params': MagicMock(side_effect=[INPUT_PARAMS, EXISTING_OBJ]),
|
||||
'zabbix.run_query': MagicMock(return_value=['length of result is 1']),
|
||||
'zabbix.compare_params': MagicMock(return_value={})}):
|
||||
ret['result'] = True
|
||||
ret['comment'] = 'Zabbix Action "{0}" already exists and corresponds to a definition.'.format(name)
|
||||
self.assertDictEqual(zabbix_action.present(name, {}), ret)
|
||||
|
||||
def test_present_update(self):
|
||||
'''
|
||||
Test to ensure that named action is present but must be updated
|
||||
'''
|
||||
name = 'Auto registration Databases'
|
||||
ret = {'name': name, 'result': False, 'comment': '', 'changes': {}}
|
||||
|
||||
def side_effect_run_query(*args):
|
||||
'''
|
||||
Differentiate between __salt__ exec module function calls with different parameters.
|
||||
'''
|
||||
if args[0] == 'action.get':
|
||||
return ['length of result is 1 = action exists']
|
||||
elif args[0] == 'action.update':
|
||||
return DIFF_PARAMS
|
||||
|
||||
with patch.dict(zabbix_action.__opts__, {'test': False}):
|
||||
with patch.dict(zabbix_action.__salt__,
|
||||
{'zabbix.get_zabbix_id_mapper': MagicMock(return_value={'action': 'actionid'}),
|
||||
'zabbix.substitute_params': MagicMock(side_effect=[INPUT_PARAMS, EXISTING_OBJ_DIFF]),
|
||||
'zabbix.run_query': MagicMock(side_effect=side_effect_run_query),
|
||||
'zabbix.compare_params': MagicMock(return_value=DIFF_PARAMS)}):
|
||||
ret['result'] = True
|
||||
ret['comment'] = 'Zabbix Action "{0}" updated.'.format(name)
|
||||
ret['changes'] = {name: {'old': 'Zabbix Action "{0}" differed '
|
||||
'in following parameters: {1}'.format(name, DIFF_PARAMS),
|
||||
'new': 'Zabbix Action "{0}" fixed.'.format(name)}}
|
||||
self.assertDictEqual(zabbix_action.present(name, {}), ret)
|
||||
|
||||
def test_absent_test_mode(self):
|
||||
'''
|
||||
Test to ensure that named action is absent in test mode
|
||||
'''
|
||||
name = 'Auto registration Databases'
|
||||
ret = {'name': name, 'result': False, 'comment': '', 'changes': {}}
|
||||
with patch.dict(zabbix_action.__opts__, {'test': True}):
|
||||
with patch.dict(zabbix_action.__salt__, {'zabbix.get_object_id_by_params': MagicMock(return_value=11)}):
|
||||
ret['result'] = True
|
||||
ret['comment'] = 'Zabbix Action "{0}" would be deleted.'.format(name)
|
||||
ret['changes'] = {name: {'old': 'Zabbix Action "{0}" exists.'.format(name),
|
||||
'new': 'Zabbix Action "{0}" would be deleted.'.format(name)}}
|
||||
self.assertDictEqual(zabbix_action.absent(name), ret)
|
||||
|
||||
def test_absent(self):
|
||||
'''
|
||||
Test to ensure that named action is absent
|
||||
'''
|
||||
name = 'Auto registration Databases'
|
||||
ret = {'name': name, 'result': False, 'comment': '', 'changes': {}}
|
||||
with patch.dict(zabbix_action.__opts__, {'test': False}):
|
||||
with patch.dict(zabbix_action.__salt__, {'zabbix.get_object_id_by_params': MagicMock(return_value=False)}):
|
||||
ret['result'] = True
|
||||
ret['comment'] = 'Zabbix Action "{0}" does not exist.'.format(name)
|
||||
self.assertDictEqual(zabbix_action.absent(name), ret)
|
||||
|
||||
with patch.dict(zabbix_action.__salt__, {'zabbix.get_object_id_by_params': MagicMock(return_value=11)}):
|
||||
with patch.dict(zabbix_action.__salt__, {'zabbix.run_query': MagicMock(return_value=True)}):
|
||||
ret['result'] = True
|
||||
ret['comment'] = 'Zabbix Action "{0}" deleted.'.format(name)
|
||||
ret['changes'] = {name: {'old': 'Zabbix Action "{0}" existed.'.format(name),
|
||||
'new': 'Zabbix Action "{0}" deleted.'.format(name)}}
|
||||
self.assertDictEqual(zabbix_action.absent(name), ret)
|
182
tests/unit/states/test_zabbix_template.py
Normal file
182
tests/unit/states/test_zabbix_template.py
Normal file
@ -0,0 +1,182 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
:codeauthor: :email:`Jakub Sliva <jakub.sliva@ultimum.io>`
|
||||
'''
|
||||
|
||||
# Import Python Libs
|
||||
from __future__ import absolute_import
|
||||
from __future__ import unicode_literals
|
||||
|
||||
# Import Salt Testing Libs
|
||||
from tests.support.mixins import LoaderModuleMockMixin
|
||||
from tests.support.unit import TestCase, skipIf
|
||||
from tests.support.mock import (
|
||||
MagicMock,
|
||||
patch,
|
||||
NO_MOCK,
|
||||
NO_MOCK_REASON
|
||||
)
|
||||
|
||||
import salt.states.zabbix_template as zabbix_template
|
||||
|
||||
|
||||
INPUT_PARAMS = {"applications": [{"name": "Ceph OSD"}]}
|
||||
|
||||
DEFINED_OBJ = {"macros": [{"macro": "{$CEPH_CLUSTER_NAME}", "value": "ceph"}], "host": "A Testing Template",
|
||||
"hosts": [{"hostid": "10112"}, {"hostid": "10113"}], "description": "Template for Ceph nodes",
|
||||
"groups": [{"groupid": "1"}]}
|
||||
|
||||
DEFINED_C_LIST_SUBS = {"applications": [{"name": "Ceph OSD"}], 'graphs': [], 'triggers': [], 'items': [],
|
||||
'httpTests': [], 'screens': [], 'gitems': [], 'discoveries': []}
|
||||
|
||||
SUBSTITUTE_PARAMS_CREATE = [DEFINED_OBJ, [], DEFINED_C_LIST_SUBS['applications'], [], [], [], [], [], [], [], []]
|
||||
|
||||
EXISTING_OBJ = [{"available": "0", "tls_connect": "1", "maintenance_type": "0", "groups": [{"groupid": "1"}],
|
||||
"macros": [{"macro": "{$CEPH_CLUSTER_NAME}", "hostmacroid": "60", "hostid": "10206", "value": "ceph"}],
|
||||
"hosts": [{"hostid": "10112"}, {"hostid": "10113"}], "status": "3",
|
||||
"description": "Template for Ceph nodes", "host": "A Testing Template", "disable_until": "0",
|
||||
"templateid": "10206", "name": "A Testing Template"}]
|
||||
|
||||
SUBSTITUTE_PARAMS_EXISTS = [DEFINED_OBJ, EXISTING_OBJ[0], [], [], [], [], [], [], [], []]
|
||||
|
||||
EXISTING_OBJ_DIFF = [{"groups": [{"groupid": "1"}], "macros": [{"macro": "{$CEPH_CLUSTER_NAME}", "hostmacroid": "60",
|
||||
"hostid": "10206", "value": "ceph"}],
|
||||
"hosts": [{"hostid": "10112"}, {"hostid": "10113"}], "status": "3", "templateid": "10206",
|
||||
"name": "A Testing Template"}]
|
||||
|
||||
SUBSTITUTE_PARAMS_UPDATE = [DEFINED_OBJ, EXISTING_OBJ_DIFF[0], [], [], [], [], [], [], [], []]
|
||||
|
||||
DIFF_PARAMS = {'old': {}, 'new': {'macros': [], 'templateid': '10206'}}
|
||||
|
||||
|
||||
@skipIf(NO_MOCK, NO_MOCK_REASON)
|
||||
class ZabbixTemplateTestCase(TestCase, LoaderModuleMockMixin):
|
||||
'''
|
||||
Test cases for salt.modules.zabbix
|
||||
'''
|
||||
def setup_loader_modules(self):
|
||||
return {zabbix_template: {}}
|
||||
|
||||
@patch('salt.states.zabbix_template.CHANGE_STACK', [])
|
||||
def test_present_create(self):
|
||||
'''
|
||||
Test to ensure that named template is created
|
||||
'''
|
||||
name = 'A Testing Template'
|
||||
ret = {'name': name, 'result': False, 'comment': '', 'changes': {}}
|
||||
|
||||
def side_effect_run_query(*args):
|
||||
'''
|
||||
Differentiate between __salt__ exec module function calls with different parameters.
|
||||
'''
|
||||
if args[0] == 'template.get':
|
||||
return []
|
||||
elif args[0] == 'template.create':
|
||||
return {'templateids': ['10206']}
|
||||
elif args[0] == 'application.get':
|
||||
return []
|
||||
elif args[0] == 'application.create':
|
||||
return {"applicationids": ["701"]}
|
||||
|
||||
with patch.dict(zabbix_template.__opts__, {'test': False}):
|
||||
with patch.dict(zabbix_template.__salt__,
|
||||
{'zabbix.get_zabbix_id_mapper': MagicMock(return_value={'template': 'templateid'}),
|
||||
'zabbix.substitute_params': MagicMock(side_effect=SUBSTITUTE_PARAMS_CREATE),
|
||||
'zabbix.run_query': MagicMock(side_effect=side_effect_run_query),
|
||||
'zabbix.compare_params': MagicMock(return_value={})}):
|
||||
ret['result'] = True
|
||||
ret['comment'] = 'Zabbix Template "{0}" created.'.format(name)
|
||||
ret['changes'] = {name: {'old': 'Zabbix Template "{0}" did not exist.'.format(name),
|
||||
'new': 'Zabbix Template "{0}" created according definition.'.format(name)}}
|
||||
self.assertDictEqual(zabbix_template.present(name, {}), ret)
|
||||
|
||||
@patch('salt.states.zabbix_template.CHANGE_STACK', [])
|
||||
def test_present_exists(self):
|
||||
'''
|
||||
Test to ensure that named template is present and not changed
|
||||
'''
|
||||
name = 'A Testing Template'
|
||||
ret = {'name': name, 'result': False, 'comment': '', 'changes': {}}
|
||||
|
||||
def side_effect_run_query(*args):
|
||||
'''
|
||||
Differentiate between __salt__ exec module function calls with different parameters.
|
||||
'''
|
||||
if args[0] == 'template.get':
|
||||
return EXISTING_OBJ
|
||||
elif args[0] == 'application.get':
|
||||
return ['non-empty']
|
||||
|
||||
with patch.dict(zabbix_template.__opts__, {'test': False}):
|
||||
with patch.dict(zabbix_template.__salt__,
|
||||
{'zabbix.get_zabbix_id_mapper': MagicMock(return_value={'template': 'templateid'}),
|
||||
'zabbix.substitute_params': MagicMock(side_effect=SUBSTITUTE_PARAMS_EXISTS),
|
||||
'zabbix.run_query': MagicMock(side_effect=side_effect_run_query),
|
||||
'zabbix.compare_params': MagicMock(return_value={'new': {}, 'old': {}})}):
|
||||
ret['result'] = True
|
||||
ret['comment'] = 'Zabbix Template "{0}" already exists and corresponds to a definition.'.format(name)
|
||||
self.assertDictEqual(zabbix_template.present(name, {}), ret)
|
||||
|
||||
@patch('salt.states.zabbix_template.CHANGE_STACK', [])
|
||||
def test_present_update(self):
|
||||
'''
|
||||
Test to ensure that named template is present but must be updated
|
||||
'''
|
||||
name = 'A Testing Template'
|
||||
ret = {'name': name, 'result': False, 'comment': '', 'changes': {}}
|
||||
|
||||
def side_effect_run_query(*args):
|
||||
'''
|
||||
Differentiate between __salt__ exec module function calls with different parameters.
|
||||
'''
|
||||
if args[0] == 'template.get':
|
||||
return ['length of result is 1 = template exists']
|
||||
elif args[0] == 'template.update':
|
||||
return DIFF_PARAMS
|
||||
|
||||
with patch.dict(zabbix_template.__opts__, {'test': False}):
|
||||
with patch.dict(zabbix_template.__salt__,
|
||||
{'zabbix.get_zabbix_id_mapper': MagicMock(return_value={'template': 'templateid'}),
|
||||
'zabbix.substitute_params': MagicMock(side_effect=SUBSTITUTE_PARAMS_UPDATE),
|
||||
'zabbix.run_query': MagicMock(side_effect=side_effect_run_query),
|
||||
'zabbix.compare_params': MagicMock(return_value=DIFF_PARAMS)}):
|
||||
ret['result'] = True
|
||||
ret['comment'] = 'Zabbix Template "{0}" updated.'.format(name)
|
||||
ret['changes'] = {name: {'old': 'Zabbix Template "{0}" differed.'.format(name),
|
||||
'new': 'Zabbix Template "{0}" updated according definition.'.format(name)}}
|
||||
self.assertDictEqual(zabbix_template.present(name, {}), ret)
|
||||
|
||||
def test_absent_test_mode(self):
|
||||
'''
|
||||
Test to ensure that named template is absent in test mode
|
||||
'''
|
||||
name = 'A Testing Template'
|
||||
ret = {'name': name, 'result': False, 'comment': '', 'changes': {}}
|
||||
with patch.dict(zabbix_template.__opts__, {'test': True}):
|
||||
with patch.dict(zabbix_template.__salt__, {'zabbix.get_object_id_by_params': MagicMock(return_value=11)}):
|
||||
ret['result'] = True
|
||||
ret['comment'] = 'Zabbix Template "{0}" would be deleted.'.format(name)
|
||||
ret['changes'] = {name: {'old': 'Zabbix Template "{0}" exists.'.format(name),
|
||||
'new': 'Zabbix Template "{0}" would be deleted.'.format(name)}}
|
||||
self.assertDictEqual(zabbix_template.absent(name), ret)
|
||||
|
||||
def test_absent(self):
|
||||
'''
|
||||
Test to ensure that named template is absent
|
||||
'''
|
||||
name = 'A Testing Template'
|
||||
ret = {'name': name, 'result': False, 'comment': '', 'changes': {}}
|
||||
with patch.dict(zabbix_template.__opts__, {'test': False}):
|
||||
with patch.dict(zabbix_template.__salt__,
|
||||
{'zabbix.get_object_id_by_params': MagicMock(return_value=False)}):
|
||||
ret['result'] = True
|
||||
ret['comment'] = 'Zabbix Template "{0}" does not exist.'.format(name)
|
||||
self.assertDictEqual(zabbix_template.absent(name), ret)
|
||||
|
||||
with patch.dict(zabbix_template.__salt__, {'zabbix.get_object_id_by_params': MagicMock(return_value=11)}):
|
||||
with patch.dict(zabbix_template.__salt__, {'zabbix.run_query': MagicMock(return_value=True)}):
|
||||
ret['result'] = True
|
||||
ret['comment'] = 'Zabbix Template "{0}" deleted.'.format(name)
|
||||
ret['changes'] = {name: {'old': 'Zabbix Template "{0}" existed.'.format(name),
|
||||
'new': 'Zabbix Template "{0}" deleted.'.format(name)}}
|
||||
self.assertDictEqual(zabbix_template.absent(name), ret)
|
152
tests/unit/states/test_zabbix_valuemap.py
Normal file
152
tests/unit/states/test_zabbix_valuemap.py
Normal file
@ -0,0 +1,152 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
'''
|
||||
:codeauthor: :email:`Jakub Sliva <jakub.sliva@ultimum.io>`
|
||||
'''
|
||||
|
||||
# Import Python Libs
|
||||
from __future__ import absolute_import
|
||||
from __future__ import unicode_literals
|
||||
|
||||
# Import Salt Testing Libs
|
||||
from tests.support.mixins import LoaderModuleMockMixin
|
||||
from tests.support.unit import TestCase, skipIf
|
||||
from tests.support.mock import (
|
||||
MagicMock,
|
||||
patch,
|
||||
NO_MOCK,
|
||||
NO_MOCK_REASON
|
||||
)
|
||||
|
||||
import salt.states.zabbix_valuemap as zabbix_valuemap
|
||||
|
||||
|
||||
INPUT_PARAMS = {'mappings': [{'newvalue': 'OK', 'value': '0h'}, {'newvalue': 'Failure', 'value': '1'}],
|
||||
'name': 'Server HP Health'}
|
||||
|
||||
EXISTING_OBJ = [{'valuemapid': '21', 'name': 'Server HP Health', 'mappings': [{'newvalue': 'OK', 'value': '0h'},
|
||||
{'newvalue': 'Failure', 'value': '1'}]}]
|
||||
|
||||
EXISTING_OBJ_DIFF = {'valuemapid': '21', 'name': 'Server HP Health', 'mappings': [{'newvalue': 'OK', 'value': '0h'},
|
||||
{'newvalue': 'Failure', 'value': '1'},
|
||||
{'newvalue': 'some', 'value': '2'}]}
|
||||
|
||||
DIFF_PARAMS = {'valuemapid': '21', 'mappings': [{'newvalue': 'OK', 'value': '0h'},
|
||||
{'newvalue': 'Failure', 'value': '1'}]}
|
||||
|
||||
|
||||
@skipIf(NO_MOCK, NO_MOCK_REASON)
|
||||
class ZabbixActionTestCase(TestCase, LoaderModuleMockMixin):
|
||||
'''
|
||||
Test cases for salt.modules.zabbix
|
||||
'''
|
||||
def setup_loader_modules(self):
|
||||
return {zabbix_valuemap: {}}
|
||||
|
||||
def test_present_create(self):
|
||||
'''
|
||||
Test to ensure that named value map is created
|
||||
'''
|
||||
name = 'Server HP Health'
|
||||
ret = {'name': name, 'result': False, 'comment': '', 'changes': {}}
|
||||
|
||||
def side_effect_run_query(*args):
|
||||
'''
|
||||
Differentiate between __salt__ exec module function calls with different parameters.
|
||||
'''
|
||||
if args[0] == 'valuemap.get':
|
||||
return False
|
||||
elif args[0] == 'valuemap.create':
|
||||
return True
|
||||
|
||||
with patch.dict(zabbix_valuemap.__opts__, {'test': False}):
|
||||
with patch.dict(zabbix_valuemap.__salt__,
|
||||
{'zabbix.get_zabbix_id_mapper': MagicMock(return_value={'valuemap': 'valuemapid'}),
|
||||
'zabbix.substitute_params': MagicMock(side_effect=[INPUT_PARAMS, False]),
|
||||
'zabbix.run_query': MagicMock(side_effect=side_effect_run_query),
|
||||
'zabbix.compare_params': MagicMock(return_value={})}):
|
||||
ret['result'] = True
|
||||
ret['comment'] = 'Zabbix Value map "{0}" created.'.format(name)
|
||||
ret['changes'] = {name: {'old': 'Zabbix Value map "{0}" did not exist.'.format(name),
|
||||
'new': 'Zabbix Value map "{0}" created according definition.'.format(name)}}
|
||||
self.assertDictEqual(zabbix_valuemap.present(name, {}), ret)
|
||||
|
||||
def test_present_exists(self):
|
||||
'''
|
||||
Test to ensure that named value map is present and not changed
|
||||
'''
|
||||
name = 'Server HP Health'
|
||||
ret = {'name': name, 'result': False, 'comment': '', 'changes': {}}
|
||||
|
||||
with patch.dict(zabbix_valuemap.__opts__, {'test': False}):
|
||||
with patch.dict(zabbix_valuemap.__salt__,
|
||||
{'zabbix.get_zabbix_id_mapper': MagicMock(return_value={'valuemap': 'valuemapid'}),
|
||||
'zabbix.substitute_params': MagicMock(side_effect=[INPUT_PARAMS, EXISTING_OBJ]),
|
||||
'zabbix.run_query': MagicMock(return_value=['length of result is 1']),
|
||||
'zabbix.compare_params': MagicMock(return_value={})}):
|
||||
ret['result'] = True
|
||||
ret['comment'] = 'Zabbix Value map "{0}" already exists and corresponds to a definition.'.format(name)
|
||||
self.assertDictEqual(zabbix_valuemap.present(name, {}), ret)
|
||||
|
||||
def test_present_update(self):
|
||||
'''
|
||||
Test to ensure that named value map is present but must be updated
|
||||
'''
|
||||
name = 'Server HP Health'
|
||||
ret = {'name': name, 'result': False, 'comment': '', 'changes': {}}
|
||||
|
||||
def side_effect_run_query(*args):
|
||||
'''
|
||||
Differentiate between __salt__ exec module function calls with different parameters.
|
||||
'''
|
||||
if args[0] == 'valuemap.get':
|
||||
return ['length of result is 1 = valuemap exists']
|
||||
elif args[0] == 'valuemap.update':
|
||||
return DIFF_PARAMS
|
||||
|
||||
with patch.dict(zabbix_valuemap.__opts__, {'test': False}):
|
||||
with patch.dict(zabbix_valuemap.__salt__,
|
||||
{'zabbix.get_zabbix_id_mapper': MagicMock(return_value={'valuemap': 'valuemapid'}),
|
||||
'zabbix.substitute_params': MagicMock(side_effect=[INPUT_PARAMS, EXISTING_OBJ_DIFF]),
|
||||
'zabbix.run_query': MagicMock(side_effect=side_effect_run_query),
|
||||
'zabbix.compare_params': MagicMock(return_value=DIFF_PARAMS)}):
|
||||
ret['result'] = True
|
||||
ret['comment'] = 'Zabbix Value map "{0}" updated.'.format(name)
|
||||
ret['changes'] = {name: {'old': 'Zabbix Value map "{0}" differed '
|
||||
'in following parameters: {1}'.format(name, DIFF_PARAMS),
|
||||
'new': 'Zabbix Value map "{0}" fixed.'.format(name)}}
|
||||
self.assertDictEqual(zabbix_valuemap.present(name, {}), ret)
|
||||
|
||||
def test_absent_test_mode(self):
|
||||
'''
|
||||
Test to ensure that named value map is absent in test mode
|
||||
'''
|
||||
name = 'Server HP Health'
|
||||
ret = {'name': name, 'result': False, 'comment': '', 'changes': {}}
|
||||
with patch.dict(zabbix_valuemap.__opts__, {'test': True}):
|
||||
with patch.dict(zabbix_valuemap.__salt__, {'zabbix.get_object_id_by_params': MagicMock(return_value=11)}):
|
||||
ret['result'] = True
|
||||
ret['comment'] = 'Zabbix Value map "{0}" would be deleted.'.format(name)
|
||||
ret['changes'] = {name: {'old': 'Zabbix Value map "{0}" exists.'.format(name),
|
||||
'new': 'Zabbix Value map "{0}" would be deleted.'.format(name)}}
|
||||
self.assertDictEqual(zabbix_valuemap.absent(name), ret)
|
||||
|
||||
def test_absent(self):
|
||||
'''
|
||||
Test to ensure that named value map is absent
|
||||
'''
|
||||
name = 'Server HP Health'
|
||||
ret = {'name': name, 'result': False, 'comment': '', 'changes': {}}
|
||||
with patch.dict(zabbix_valuemap.__opts__, {'test': False}):
|
||||
with patch.dict(zabbix_valuemap.__salt__,
|
||||
{'zabbix.get_object_id_by_params': MagicMock(return_value=False)}):
|
||||
ret['result'] = True
|
||||
ret['comment'] = 'Zabbix Value map "{0}" does not exist.'.format(name)
|
||||
self.assertDictEqual(zabbix_valuemap.absent(name), ret)
|
||||
|
||||
with patch.dict(zabbix_valuemap.__salt__, {'zabbix.get_object_id_by_params': MagicMock(return_value=11)}):
|
||||
with patch.dict(zabbix_valuemap.__salt__, {'zabbix.run_query': MagicMock(return_value=True)}):
|
||||
ret['result'] = True
|
||||
ret['comment'] = 'Zabbix Value map "{0}" deleted.'.format(name)
|
||||
ret['changes'] = {name: {'old': 'Zabbix Value map "{0}" existed.'.format(name),
|
||||
'new': 'Zabbix Value map "{0}" deleted.'.format(name)}}
|
||||
self.assertDictEqual(zabbix_valuemap.absent(name), ret)
|
Loading…
Reference in New Issue
Block a user