mirror of
https://github.com/ansible-collections/community.general.git
synced 2025-06-06 00:09:10 -07:00
* Add idempotency and import/export support to zabbix_template Adds idempotency to the template update functions and check mode, also adds the ability to dump and import json template configurations. * Fix issue clearing groups from template When an empty list is provided for group names, all groups associations should be cleared from the template. Previous behavior caused the template to be associated to all existing groups if an empty list was provided. * Fix undefined variable references * Add example importing template from ansible variable Document a sample template import with bare minimum structure. No items or graphs are added, only 1 application is added to the template.
547 lines
19 KiB
Python
547 lines
19 KiB
Python
#!/usr/bin/python
|
|
# -*- coding: utf-8 -*-
|
|
# (c) 2017, sookido
|
|
#
|
|
# This file is part of Ansible
|
|
#
|
|
# Ansible is free software: you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation, either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# Ansible is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
|
from __future__ import absolute_import, division, print_function
|
|
__metaclass__ = type
|
|
|
|
|
|
ANSIBLE_METADATA = {'metadata_version': '1.1',
|
|
'status': ['preview'],
|
|
'supported_by': 'community'}
|
|
|
|
|
|
DOCUMENTATION = '''
|
|
|
|
module: zabbix_template
|
|
short_description: create/delete/dump zabbix template
|
|
description:
|
|
- create/delete/dump zabbix template
|
|
version_added: "2.5"
|
|
author:
|
|
- "@sookido"
|
|
- "Logan Vig (@logan2211)"
|
|
requirements:
|
|
- "python >= 2.6"
|
|
- "zabbix-api >= 0.5.3"
|
|
options:
|
|
template_name:
|
|
description:
|
|
- Name of zabbix template
|
|
required: true
|
|
template_json:
|
|
description:
|
|
- JSON dump of template to import
|
|
required: false
|
|
template_groups:
|
|
description:
|
|
- List of template groups to create or delete.
|
|
required: false
|
|
link_templates:
|
|
description:
|
|
- List of templates linked to the template.
|
|
required: false
|
|
clear_templates:
|
|
description:
|
|
- List of templates cleared from the template.
|
|
- see templates_clear in https://www.zabbix.com/documentation/3.0/manual/api/reference/template/update
|
|
required: false
|
|
macros:
|
|
description:
|
|
- List of templates macro
|
|
required: false
|
|
state:
|
|
description:
|
|
- state present create/update template, absent delete template
|
|
required: false
|
|
choices: [present, absent, dump]
|
|
default: "present"
|
|
|
|
extends_documentation_fragment:
|
|
- zabbix
|
|
'''
|
|
|
|
EXAMPLES = '''
|
|
---
|
|
# Creates a new zabbix template from linked template
|
|
- name: Create Zabbix template using linked template
|
|
local_action:
|
|
module: zabbix_template
|
|
server_url: http://127.0.0.1
|
|
login_user: username
|
|
login_password: password
|
|
template_name: ExampleHost
|
|
template_json: "{'zabbix_export': {}}"
|
|
template_groups:
|
|
- Role
|
|
- Role2
|
|
link_templates:
|
|
- Example template1
|
|
- Example template2
|
|
clear_templates:
|
|
- Example template3
|
|
- Example template4
|
|
macros:
|
|
- macro: '{$EXAMPLE_MACRO1}'
|
|
value: 30000
|
|
- macro: '{$EXAMPLE_MACRO2}'
|
|
value: 3
|
|
- macro: '{$EXAMPLE_MACRO3}'
|
|
value: 'Example'
|
|
state: present
|
|
|
|
# Create a new template from a json config definition
|
|
- name: Import Zabbix json template configuration
|
|
local_action:
|
|
module: zabbix_template
|
|
server_url: http://127.0.0.1
|
|
login_user: username
|
|
login_password: password
|
|
template_name: Apache2
|
|
template_json: "{{ lookup('file', 'zabbix_apache2.json') }}"
|
|
template_groups:
|
|
- Webservers
|
|
state: present
|
|
|
|
# Import a template from Ansible variable dict
|
|
- name: Import Zabbix Template
|
|
zabbix_template:
|
|
login_user: username
|
|
login_password: password
|
|
server_url: http://127.0.0.1
|
|
template_name: Test Template
|
|
template_json:
|
|
zabbix_export:
|
|
version: '3.2'
|
|
templates:
|
|
- name: Template for Testing
|
|
description: 'Testing template import'
|
|
template: Test Template
|
|
groups:
|
|
- name: Templates
|
|
applications:
|
|
- name: Test Application
|
|
template_groups: Templates
|
|
state: present
|
|
|
|
# Add a macro to a template
|
|
- name: Set a macro on the Zabbix template
|
|
local_action:
|
|
module: zabbix_template
|
|
server_url: http://127.0.0.1
|
|
login_user: username
|
|
login_password: password
|
|
template_name: Template
|
|
macros:
|
|
- macro: '{$TEST_MACRO}'
|
|
value: 'Example'
|
|
state: present
|
|
|
|
# Remove a template
|
|
- name: Delete Zabbix template
|
|
local_action:
|
|
module: zabbix_template
|
|
server_url: http://127.0.0.1
|
|
login_user: username
|
|
login_password: password
|
|
template_name: Template
|
|
state: absent
|
|
|
|
# Export template json definition
|
|
- name: Dump Zabbix template
|
|
local_action:
|
|
module: zabbix_template
|
|
server_url: http://127.0.0.1
|
|
login_user: username
|
|
login_password: password
|
|
template_name: Template
|
|
state: dump
|
|
register: template_dump
|
|
'''
|
|
|
|
RETURN = '''
|
|
template_json:
|
|
description: The JSON dump of the template
|
|
returned: when state is dump
|
|
type: string
|
|
sample: {
|
|
"zabbix_export":{
|
|
"date":"2017-11-29T16:37:24Z",
|
|
"templates":[{
|
|
"templates":[],
|
|
"description":"",
|
|
"httptests":[],
|
|
"screens":[],
|
|
"applications":[],
|
|
"discovery_rules":[],
|
|
"groups":[{"name":"Templates"}],
|
|
"name":"Test Template",
|
|
"items":[],
|
|
"macros":[],
|
|
"template":"test"
|
|
}],
|
|
"version":"3.2",
|
|
"groups":[{
|
|
"name":"Templates"
|
|
}]
|
|
}
|
|
}
|
|
'''
|
|
|
|
|
|
from ansible.module_utils.basic import AnsibleModule
|
|
from ansible.module_utils._text import to_native
|
|
import json
|
|
import traceback
|
|
|
|
|
|
try:
|
|
from zabbix_api import ZabbixAPI, ZabbixAPIException
|
|
|
|
HAS_ZABBIX_API = True
|
|
except ImportError:
|
|
HAS_ZABBIX_API = False
|
|
|
|
|
|
class Template(object):
|
|
def __init__(self, module, zbx):
|
|
self._module = module
|
|
self._zapi = zbx
|
|
|
|
# check if host group exists
|
|
def check_host_group_exist(self, group_names):
|
|
for group_name in group_names:
|
|
result = self._zapi.hostgroup.get({'filter': {'name': group_name}})
|
|
if not result:
|
|
self._module.fail_json(msg="Hostgroup not found: %s" %
|
|
group_name)
|
|
return True
|
|
|
|
# get group ids by group names
|
|
def get_group_ids_by_group_names(self, group_names):
|
|
group_ids = []
|
|
if group_names is None or len(group_names) == 0:
|
|
return group_ids
|
|
if self.check_host_group_exist(group_names):
|
|
group_list = self._zapi.hostgroup.get(
|
|
{'output': 'extend',
|
|
'filter': {'name': group_names}})
|
|
for group in group_list:
|
|
group_id = group['groupid']
|
|
group_ids.append({'groupid': group_id})
|
|
return group_ids
|
|
|
|
def get_template_ids(self, template_list):
|
|
template_ids = []
|
|
if template_list is None or len(template_list) == 0:
|
|
return template_ids
|
|
for template in template_list:
|
|
template_list = self._zapi.template.get(
|
|
{'output': 'extend',
|
|
'filter': {'host': template}})
|
|
if len(template_list) < 1:
|
|
continue
|
|
else:
|
|
template_id = template_list[0]['templateid']
|
|
template_ids.append(template_id)
|
|
return template_ids
|
|
|
|
def add_template(self, template_name, template_json, group_ids,
|
|
child_template_ids, macros):
|
|
if self._module.check_mode:
|
|
self._module.exit_json(changed=True)
|
|
self._zapi.template.create({'host': template_name,
|
|
'groups': group_ids,
|
|
'templates': child_template_ids,
|
|
'macros': macros})
|
|
if template_json:
|
|
self.import_template(template_json, template_name)
|
|
|
|
def update_template(self, templateids, template_json,
|
|
group_ids, child_template_ids,
|
|
clear_template_ids, macros,
|
|
existing_template_json=None):
|
|
changed = False
|
|
template_changes = {}
|
|
if group_ids is not None:
|
|
template_changes.update({'groups': group_ids})
|
|
changed = True
|
|
if child_template_ids is not None:
|
|
template_changes.update({'templates': child_template_ids})
|
|
changed = True
|
|
if macros is not None:
|
|
template_changes.update({'macros': macros})
|
|
changed = True
|
|
do_import = False
|
|
if template_json:
|
|
parsed_template_json = self.load_json_template(template_json)
|
|
if self.diff_template(parsed_template_json,
|
|
existing_template_json):
|
|
do_import = True
|
|
changed = True
|
|
|
|
if self._module.check_mode:
|
|
self._module.exit_json(changed=changed)
|
|
|
|
if template_changes:
|
|
template_changes.update({
|
|
'templateid': templateids,
|
|
'templates_clear': clear_template_ids
|
|
})
|
|
self._zapi.template.update(template_changes)
|
|
|
|
if do_import:
|
|
self.import_template(template_json,
|
|
existing_template_json['zabbix_export']['templates'][0]['template'])
|
|
|
|
return changed
|
|
|
|
def delete_template(self, templateids):
|
|
if self._module.check_mode:
|
|
self._module.exit_json(changed=True)
|
|
self._zapi.template.delete(templateids)
|
|
|
|
def ordered_json(self, obj):
|
|
# Deep sort json dicts for comparison
|
|
if isinstance(obj, dict):
|
|
return sorted((k, self.ordered_json(v)) for k, v in obj.items())
|
|
if isinstance(obj, list):
|
|
return sorted(self.ordered_json(x) for x in obj)
|
|
else:
|
|
return obj
|
|
|
|
def dump_template(self, template_ids):
|
|
if self._module.check_mode:
|
|
self._module.exit_json(changed=True)
|
|
try:
|
|
dump = self._zapi.configuration.export({
|
|
'format': 'json',
|
|
'options': {'templates': template_ids}
|
|
})
|
|
return self.load_json_template(dump)
|
|
except ZabbixAPIException as e:
|
|
self._module.fail_json(msg='Unable to export template: %s' % e)
|
|
|
|
def diff_template(self, template_json_a, template_json_b):
|
|
# Compare 2 zabbix templates and return True if they differ.
|
|
template_json_a = self.filter_template(template_json_a)
|
|
template_json_b = self.filter_template(template_json_b)
|
|
if self.ordered_json(template_json_a) == self.ordered_json(template_json_b):
|
|
return False
|
|
return True
|
|
|
|
def filter_template(self, template_json):
|
|
# Filter the template json to contain only the keys we will update
|
|
keep_keys = set(['graphs', 'templates', 'triggers', 'value_maps'])
|
|
unwanted_keys = set(template_json['zabbix_export']) - keep_keys
|
|
for unwanted_key in unwanted_keys:
|
|
del template_json['zabbix_export'][unwanted_key]
|
|
return template_json
|
|
|
|
def load_json_template(self, template_json):
|
|
try:
|
|
return json.loads(template_json)
|
|
except ValueError as e:
|
|
self._module.fail_json(
|
|
msg='Invalid JSON provided',
|
|
details=to_native(e),
|
|
exception=traceback.format_exc()
|
|
)
|
|
|
|
def import_template(self, template_json, template_name=None):
|
|
parsed_template_json = self.load_json_template(template_json)
|
|
if template_name != parsed_template_json['zabbix_export']['templates'][0]['template']:
|
|
self._module.fail_json(msg='JSON template name does not match presented name')
|
|
|
|
try:
|
|
self._zapi.configuration.import_({
|
|
'format': 'json',
|
|
'source': template_json,
|
|
'rules': {
|
|
'applications': {
|
|
'createMissing': True,
|
|
'updateExisting': True,
|
|
'deleteMissing': True
|
|
},
|
|
'discoveryRules': {
|
|
'createMissing': True,
|
|
'updateExisting': True,
|
|
'deleteMissing': True
|
|
},
|
|
'graphs': {
|
|
'createMissing': True,
|
|
'updateExisting': True,
|
|
'deleteMissing': True
|
|
},
|
|
'httptests': {
|
|
'createMissing': True,
|
|
'updateExisting': True,
|
|
'deleteMissing': True
|
|
},
|
|
'items': {
|
|
'createMissing': True,
|
|
'updateExisting': True,
|
|
'deleteMissing': True
|
|
},
|
|
'templates': {
|
|
'createMissing': True,
|
|
'updateExisting': True
|
|
},
|
|
'templateScreens': {
|
|
'createMissing': True,
|
|
'updateExisting': True,
|
|
'deleteMissing': True
|
|
},
|
|
'triggers': {
|
|
'createMissing': True,
|
|
'updateExisting': True,
|
|
'deleteMissing': True
|
|
},
|
|
'valueMaps': {
|
|
'createMissing': True,
|
|
'updateExisting': True
|
|
}
|
|
}
|
|
})
|
|
except ZabbixAPIException as e:
|
|
self._module.fail_json(
|
|
msg='Unable to import JSON template',
|
|
details=to_native(e),
|
|
exception=traceback.format_exc()
|
|
)
|
|
|
|
|
|
def main():
|
|
module = AnsibleModule(
|
|
argument_spec=dict(
|
|
server_url=dict(type='str', required=True, aliases=['url']),
|
|
login_user=dict(type='str', required=True),
|
|
login_password=dict(type='str', required=True, no_log=True),
|
|
http_login_user=dict(type='str', required=False, default=None),
|
|
http_login_password=dict(type='str', required=False,
|
|
default=None, no_log=True),
|
|
validate_certs=dict(type='bool', required=False, default=True),
|
|
template_name=dict(type='str', required=True),
|
|
template_json=dict(type='json', required=False),
|
|
template_groups=dict(type='list', required=False),
|
|
link_templates=dict(type='list', required=False),
|
|
clear_templates=dict(type='list', required=False),
|
|
macros=dict(type='list', required=False),
|
|
state=dict(default="present", choices=['present', 'absent',
|
|
'dump']),
|
|
timeout=dict(type='int', default=10)
|
|
),
|
|
supports_check_mode=True
|
|
)
|
|
|
|
if not HAS_ZABBIX_API:
|
|
module.fail_json(msg="Missing required zabbix-api module " +
|
|
"(check docs or install with: " +
|
|
"pip install zabbix-api)")
|
|
|
|
server_url = module.params['server_url']
|
|
login_user = module.params['login_user']
|
|
login_password = module.params['login_password']
|
|
http_login_user = module.params['http_login_user']
|
|
http_login_password = module.params['http_login_password']
|
|
validate_certs = module.params['validate_certs']
|
|
template_name = module.params['template_name']
|
|
template_json = module.params['template_json']
|
|
template_groups = module.params['template_groups']
|
|
link_templates = module.params['link_templates']
|
|
clear_templates = module.params['clear_templates']
|
|
template_macros = module.params['macros']
|
|
state = module.params['state']
|
|
timeout = module.params['timeout']
|
|
|
|
zbx = None
|
|
|
|
# login to zabbix
|
|
try:
|
|
zbx = ZabbixAPI(server_url, timeout=timeout,
|
|
user=http_login_user, passwd=http_login_password, validate_certs=validate_certs)
|
|
zbx.login(login_user, login_password)
|
|
except ZabbixAPIException as e:
|
|
module.fail_json(msg="Failed to connect to Zabbix server: %s" % e)
|
|
|
|
template = Template(module, zbx)
|
|
template_ids = template.get_template_ids([template_name])
|
|
existing_template_json = None
|
|
if template_ids:
|
|
existing_template_json = template.dump_template(template_ids)
|
|
|
|
# delete template
|
|
if state == "absent":
|
|
# if template not found. no change, no fail
|
|
if not template_ids:
|
|
module.exit_json(changed=False,
|
|
msg="Template not found. " +
|
|
"No changed: %s" % template_name)
|
|
template.delete_template(template_ids)
|
|
module.exit_json(changed=True,
|
|
result="Successfully delete template %s" %
|
|
template_name)
|
|
|
|
elif state == "dump":
|
|
if not template_ids:
|
|
module.fail_json(msg='Template not found: %s' % template_name)
|
|
module.exit_json(changed=False, template_json=existing_template_json)
|
|
|
|
elif state == "present":
|
|
child_template_ids = None
|
|
if link_templates is not None:
|
|
child_template_ids = template.get_template_ids(link_templates)
|
|
|
|
clear_template_ids = []
|
|
if clear_templates is not None:
|
|
clear_template_ids = template.get_template_ids(clear_templates)
|
|
|
|
group_ids = None
|
|
if template_groups is not None:
|
|
# If the template exists, compare the already set groups
|
|
existing_groups = None
|
|
if existing_template_json:
|
|
existing_groups = set(list(group['name'] for group in existing_template_json['zabbix_export']['groups']))
|
|
if not existing_groups or set(template_groups) != existing_groups:
|
|
group_ids = template.get_group_ids_by_group_names(template_groups)
|
|
|
|
macros = None
|
|
if template_macros is not None:
|
|
existing_macros = None
|
|
if existing_template_json:
|
|
existing_macros = set(existing_template_json['zabbix_export']['templates'][0]['macros'])
|
|
if not existing_macros or set(template_macros) != existing_macros:
|
|
macros = template_macros
|
|
|
|
if not template_ids:
|
|
template.add_template(template_name, template_json, group_ids,
|
|
child_template_ids, macros)
|
|
module.exit_json(changed=True,
|
|
result="Successfully added template: %s" %
|
|
template_name)
|
|
else:
|
|
changed = template.update_template(template_ids[0], template_json,
|
|
group_ids, child_template_ids,
|
|
clear_template_ids, macros,
|
|
existing_template_json)
|
|
module.exit_json(changed=changed,
|
|
result="Successfully updateed template: %s" %
|
|
template_name)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|