Skip to content

Instantly share code, notes, and snippets.

@q1x
Created October 11, 2019 14:26
Show Gist options
  • Save q1x/d81641c82c791f75e6dbffeb85d35a8b to your computer and use it in GitHub Desktop.
Save q1x/d81641c82c791f75e6dbffeb85d35a8b to your computer and use it in GitHub Desktop.
Zabbix Item Cleanup
#!/usr/bin/env python
#
# import needed modules.
# pyzabbix is needed, see https://github.com/lukecyca/pyzabbix
#
import argparse
import ConfigParser
import os
import os.path
import time
import sys
import logging
import distutils.util
from pprint import pprint
from pyzabbix import ZabbixAPI
logger = logging.getLogger(__name__)
logger.addHandler(logging.StreamHandler())
logger.setLevel(logging.WARNING)
# define config helper function
def ConfigSectionMap(section):
dict1 = {}
options = Config.options(section)
for option in options:
try:
dict1[option] = Config.get(section, option)
if dict1[option] == -1:
DebugPrint("skip: %s" % option)
except:
print("exception on %s!" % option)
dict1[option] = None
return dict1
# set default vars
defconf = os.getenv("HOME") + "/.zbx.conf"
username = ""
password = ""
api = ""
noverify = ""
# Define commandline arguments
parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter,description='Disables polling items on hosts not responding to ping and re-enables polling items when they return.', epilog="""
This program can use .ini style configuration files to retrieve the needed API connection information.
To use this type of storage, create a conf file (the default is $HOME/.zbx.conf) that contains at least the [Zabbix API] section and any of the other parameters:
[Zabbix API]
username=johndoe
password=verysecretpassword
api=https://zabbix.mycompany.com/path/to/zabbix/frontend/
no_verify=true
""")
parser.add_argument('-u', '--username', help='User for the Zabbix api')
parser.add_argument('-p', '--password', help='Password for the Zabbix api user')
parser.add_argument('-a', '--api', help='Zabbix API URL')
parser.add_argument('--no-verify', help='Disables certificate validation when using a secure connection',action='store_true')
parser.add_argument('-c','--config', help='Config file location (defaults to $HOME/.zbx.conf)')
parser.add_argument('-v', '--verbose', help='Enables verbose output.',action='store_true')
parser.add_argument('-d', '--debug', help='Enables debug output.',action='store_true')
parser.add_argument('-n', '--noop', help='Verbose output will show what would be done, but no changes will be made.',action='store_true')
parser.add_argument('-t', '--trigger', help='Description of the trigger used to identify host online status, defaults to "Ping: {HOST.NAME} is not responding to requests"',default='Ping: {HOST.NAME} is not responding to requests',metavar='DESCRIPTION')
parser.add_argument('-A', '--alive', help='Alive period (in minutes) before a hosts polling items are enabled, defaults to 30.',metavar='[1-1440]', choices=range(1,1441),default=30, type=int)
parser.add_argument('-g', '--grace', help='Grace period (in minutes) before an offline hosts polling items are disabled, defaults to 60.',metavar='[1-1440]', choices=range(1,1441),default=60, type=int)
parser.add_argument('groups', help='List of groups to scan for hosts.', nargs='+')
args = parser.parse_args()
# load config module
Config = ConfigParser.ConfigParser()
Config
# if configuration argument is set, test the config file
if args.config:
if os.path.isfile(args.config) and os.access(args.config, os.R_OK):
Config.read(args.config)
# if not set, try default config file
else:
if os.path.isfile(defconf) and os.access(defconf, os.R_OK):
Config.read(defconf)
# try to load available settings from config file
try:
username=ConfigSectionMap("Zabbix API")['username']
password=ConfigSectionMap("Zabbix API")['password']
api=ConfigSectionMap("Zabbix API")['api']
noverify=bool(distutils.util.strtobool(ConfigSectionMap("Zabbix API")["no_verify"]))
except:
pass
# override settings if they are provided as arguments
if args.username:
username = args.username
if args.password:
password = args.password
if args.api:
api = args.api
if args.no_verify:
noverify = args.no_verify
# test for needed params
if not username:
sys.exit("Error: API User not set")
if not password:
sys.exit("Error: API Password not set")
if not api:
sys.exit("Error: API URL is not set")
if args.verbose:
logger.setLevel(logging.INFO)
if args.debug:
logger.setLevel(logging.DEBUG)
if args.noop:
logger.info(" *** Running in NOOP mode, no changes will be made *** ")
# Setup Zabbix API connection
zapi = ZabbixAPI(api)
if noverify is True:
zapi.session.verify = False
# Login to the Zabbix API
zapi.login(username, password)
zapi.timeout=900
##################################
# Start actual API logic
##################################
# Zabbix version
zversion=zapi.apiinfo.version()
logger.info("Zabbix API version: %s", str(zversion))
timestamp = time.time()
gracestamp = timestamp-(args.grace*60)
alivestamp = timestamp-(args.alive*60)
disable = []
enable = []
item_types= [0,1,4,6,9,10,11,13,14,16]
# Find hostgroups to inspect
groups = zapi.hostgroup.get(**{"output": ["groupid","name"],"filter": {"name": args.groups }})
if groups:
logger.debug("Found the following hostgroups: %s", groups)
groupids = [group['groupid'] for group in groups if 'groupid' in group] # we need a list of groupids
# Fetch matching hosts
hosts = zapi.host.get(**{"output" : "extend",
"groupids": groupids,
"selectGroups": "extend",
"selectTriggers": ['description','lastchange','state','status','value'],
"monitored_hosts": True,
"with_monitored_triggers": True
})
if hosts:
logger.info("Inspecting %s host(s)...", len(hosts))
for host in hosts:
trigger = [t for t in host['triggers'] if t['description'] == args.trigger] # Find trigger properties for the host status trigger
if trigger:
# If host has been down longer than the grace period, add it to the disable list.
if ( int(trigger[0]['status']) == 0 and
int(trigger[0]['state']) == 0 and
int(trigger[0]['value']) == 1 and
float(trigger[0]['lastchange']) < gracestamp):
logger.debug('%s (%s): Has been offline more than %s minute(s), disabling polling items.', host['host'], host['name'], args.grace)
disable.append(host['hostid'])
# If the host has been active for more than the alive threshold, add it to the enable list.
elif ( int(trigger[0]['status']) == 0 and
int(trigger[0]['state']) == 0 and
int(trigger[0]['value']) == 0 and
float(trigger[0]['lastchange']) < alivestamp):
logger.debug('%s (%s): Has been online more than %s minute(s), enabling polling items.', host['host'], host['name'], args.alive)
enable.append(host['hostid'])
else:
logger.info('%s (%s): Could not find trigger "%s" on this host!!!', host['host'], host['name'], args.trigger)
if len(disable) > 0:
# Disable items on offline hosts
items = []
items = zapi.item.get(**{'hostids': disable, 'monitored': True, 'output': ['itemid','type'],'filter': {'status': u'0'}})
disable_itemids = [item['itemid'] for item in items if int(item['type']) in item_types]
logger.info('Disabling %s item(s) on %s host(s).', len(disable_itemids), len(disable))
if not args.noop:
for itemid in disable_itemids:
try:
response = zapi.item.update(**{'itemid': itemid, 'status': 1})
except:
logger.error('Something went wrong trying to disable itemid %s".', itemid)
if len(enable) > 0:
# Enable items on online hosts
items = []
items = zapi.item.get(**{'hostids': enable, 'output': ['itemid','type'],'filter': {'status': u'1'}})
enable_itemids = [item['itemid'] for item in items if int(item['type']) in item_types]
logger.info('Enabling %s item(s) on %s host(s).', len(enable_itemids), len(enable))
if not args.noop:
for itemid in enable_itemids:
try:
response = zapi.item.update(**{'itemid': itemid, 'status': 0})
except:
logger.error('Something went wrong trying to enable itemid %s".', itemid)
if len(enable) == 0 and len(disable) == 0:
logger.info("No hosts found that need to be updated.")
else:
logger.info("No hosts found.")
else:
logger.error("Could not find any matching hostgroups.")
sys.exit(2)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment