#!/usr/bin/env python
# (c) 2013, Greg Buehler
# (c) 2018, Filippo Ferrazini
#
# 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 .
######################################################################
"""
Zabbix Server external inventory script.
========================================
Returns hosts and hostgroups from Zabbix Server.
If you want to run with --limit against a host group with space in the
name, use asterisk. For example --limit="Linux*servers".
Configuration is read from `zabbix.ini`.
Tested with Zabbix Server 2.0.6, 3.2.3 and 3.4.
"""
from __future__ import print_function
import os
import sys
import argparse
import json
import atexit
from ansible.module_utils.six.moves import configparser
from ansible.module_utils.compat.version import LooseVersion
from ansible.module_utils.urls import Request
from ansible.module_utils.six.moves.urllib.error import URLError, HTTPError
class ZabbixInventory(object):
def read_settings(self):
config = configparser.ConfigParser()
conf_path = "./zabbix.ini"
if not os.path.exists(conf_path):
conf_path = os.path.dirname(os.path.realpath(__file__)) + "/zabbix.ini"
if os.path.exists(conf_path):
config.read(conf_path)
# server
if config.has_option("zabbix", "server"):
self.zabbix_server = config.get("zabbix", "server")
# login
if config.has_option("zabbix", "username"):
self.zabbix_username = config.get("zabbix", "username")
if config.has_option("zabbix", "password"):
self.zabbix_password = config.get("zabbix", "password")
if config.has_option("zabbix", "auth_token"):
self.auth_token = config.get("zabbix", "auth_token")
# ssl certs
if config.has_option("zabbix", "validate_certs"):
if config.get("zabbix", "validate_certs") in ["false", "False", False]:
self.validate_certs = False
# timeout
if config.has_option("zabbix", "timeout"):
self.timeout = config.get("zabbix", "timeout")
# host inventory
if config.has_option("zabbix", "read_host_inventory"):
if config.get("zabbix", "read_host_inventory") in ["true", "True", True]:
self.read_host_inventory = True
# host interface
if config.has_option("zabbix", "use_host_interface"):
if config.get("zabbix", "use_host_interface") in ["false", "False", False]:
self.use_host_interface = False
def read_cli(self):
parser = argparse.ArgumentParser()
parser.add_argument("--host")
parser.add_argument("--list", action="store_true")
self.options = parser.parse_args()
def hoststub(self):
return {
"hosts": []
}
def api_request(self, method, params=None):
server_url = self.zabbix_server
validate_certs = self.validate_certs
timeout = self.timeout
headers = {"Content-Type": "application/json-rpc"}
payload = {
"jsonrpc": "2.0",
"method": method,
"id": "1"
}
if params is None:
payload["params"] = {}
else:
payload["params"] = params
if self.auth != "":
if (LooseVersion(self.zabbix_version) >= LooseVersion("6.4")):
headers["Authorization"] = "Bearer " + self.auth
else:
payload["auth"] = self.auth
api_url = server_url + "/api_jsonrpc.php"
req = Request(
headers=headers,
timeout=timeout,
validate_certs=validate_certs
)
try:
response = req.post(api_url, data=json.dumps(payload))
except ValueError:
print("Error: Something went wrong with JSON loading.", file=sys.stderr)
sys.exit(1)
except (URLError, HTTPError) as error:
print(error, file=sys.stderr)
return response
def get_version(self):
response = self.api_request(
"apiinfo.version"
)
res = json.load(response)
self.zabbix_version = res["result"]
def login_zabbix(self):
auth_token = self.auth_token
if auth_token:
self.auth = auth_token
return
atexit.register(self.logout_zabbix)
login_user = self.zabbix_username
login_password = self.zabbix_password
response = self.api_request(
"user.login",
{
"username": login_user,
"password": login_password
}
)
res = json.load(response)
self.auth = res["result"]
def logout_zabbix(self):
self.api_request(
"user.logout",
[]
)
def get_host(self, name):
api_query = {"output": "extend", "selectGroups": "extend", "filter": {"host": [name]}}
if self.use_host_interface:
api_query["selectInterfaces"] = ["useip", "ip", "dns"]
if self.read_host_inventory:
api_query["selectInventory"] = "extend"
data = {"ansible_ssh_host": name}
if self.use_host_interface or self.read_host_inventory:
response = self.api_request("host.get", api_query)
response_obj = json.load(response)
if len(response_obj['result']) > 0:
host_data = response_obj['result'][0]
# check if zabbix api returned interfaces element
if "interfaces" in host_data:
# check for a interfaces list that contains at least interface
if len(host_data["interfaces"]) >= 1:
# use first interface only
if host_data["interfaces"][0]["useip"] == '0':
data["ansible_ssh_host"] = host_data["interfaces"][0]["dns"]
else:
data["ansible_ssh_host"] = host_data["interfaces"][0]["ip"]
if ("inventory" in host_data) and (host_data["inventory"]):
data.update(host_data["inventory"])
return data
def get_list(self):
api_query = {"output": "extend", "selectGroups": "extend"}
if self.use_host_interface:
api_query["selectInterfaces"] = ["useip", "ip", "dns"]
if self.read_host_inventory:
api_query["selectInventory"] = "extend"
response = self.api_request("host.get", api_query)
hosts_data = json.load(response)["result"]
data = {"_meta": {"hostvars": {}}}
data[self.defaultgroup] = self.hoststub()
for host in hosts_data:
hostname = host["name"]
hostvars = dict()
data[self.defaultgroup]["hosts"].append(hostname)
for group in host["groups"]:
groupname = group["name"]
if groupname not in data:
data[groupname] = self.hoststub()
data[groupname]["hosts"].append(hostname)
# check if zabbix api returned a interfaces element
if "interfaces" in host:
# check for a interfaces list that contains at least interface
if len(host["interfaces"]) >= 1:
# use first interface only
if host["interfaces"][0]["useip"] == 0:
hostvars["ansible_ssh_host"] = host["interfaces"][0]["dns"]
else:
hostvars["ansible_ssh_host"] = host["interfaces"][0]["ip"]
if ("inventory" in host) and (host["inventory"]):
hostvars.update(host["inventory"])
data["_meta"]["hostvars"][hostname] = hostvars
return data
def __init__(self):
self.defaultgroup = "group_all"
self.zabbix_server = None
self.zabbix_username = None
self.zabbix_password = None
self.auth_token = None
self.auth = ""
self.validate_certs = True
self.timeout = 30
self.read_host_inventory = False
self.use_host_interface = True
self.zabbix_version = ""
self.meta = {}
self.read_settings()
self.read_cli()
if self.zabbix_server and self.zabbix_username:
try:
self.get_version()
self.login_zabbix()
# zabbix_api tries to exit if it cannot parse what the zabbix server returned
# so we have to use SystemExit here
except (Exception, SystemExit) as e:
print("Error: got the exception '%s'. Check your zabbix.ini." % e, file=sys.stderr)
sys.exit(1)
if self.options.host:
data = self.get_host(self.options.host)
print(json.dumps(data, indent=2))
elif self.options.list:
data = self.get_list()
print(json.dumps(data, indent=2))
else:
print("usage: --list ..OR.. --host ", file=sys.stderr)
sys.exit(1)
else:
print("Error: Configuration of server and credentials are required. See zabbix.ini.", file=sys.stderr)
sys.exit(1)
ZabbixInventory()