Created
February 3, 2021 08:14
-
-
Save q1x/3407b35b69485a7b35937b444b6fc94f to your computer and use it in GitHub Desktop.
Zabbix automaintenance
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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='Creates Zabbix trigger dependencies for on-site equipment to the local default gateway.', 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('-P', '--predeploy', help='Name of the predeployment group, defaults to "Predeployment"',default='Predeployment',metavar='HOSTGROUP') | |
parser.add_argument('-A', '--alive', help='Alive period (in hours) before a host is removed from the predeployment group, defaults to 8. 0 only checks last 5 mins.',metavar='[0-24]', choices=range(0,25),default=8, type=int) | |
parser.add_argument('-O', '--offline', help='Name of the Offline group, defaults to "Offline"',default='Offline',metavar='HOSTGROUP') | |
parser.add_argument('-g', '--grace', help='Grace period (in days) before a host is moved to the offline group, defaults to 7.',metavar='[1-31]', choices=range(1,32),default=7, 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*86400) | |
if args.alive == 0: | |
alivestamp = timestamp-300 | |
else: | |
alivestamp = timestamp-(args.alive*3600) | |
set_offline = [] | |
remove_offline = [] | |
remove_predeploy = [] | |
# Find group properties for offline maintenance group | |
offlinegrp = zapi.hostgroup.get(**{"output": ["groupid","name"],"filter": {"name": args.offline }}) | |
if offlinegrp: | |
logger.debug("Found the following offline maintenance group: %s", offlinegrp[0]) | |
else: | |
logger.error('Could not find offline maintenance group "%s".', args.offline) | |
sys.exit(1) | |
# Find group properties for predeployment maintenance group | |
predeploygrp = zapi.hostgroup.get(**{"output": ["groupid","name"],"filter": {"name": args.predeploy }}) | |
if predeploygrp: | |
logger.debug("Found the following predeployment maintenance group: %s", predeploygrp[0]) | |
else: | |
logger.error('Could not find predeployment maintenance group "%s".', args.predeploy) | |
sys.exit(1) | |
# 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 is not in maintenance but has been down longer than the grace period, add it to the set_offline list. | |
if ( int(host['maintenance_status']) == 0 and | |
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 day(s), marking host for offline maintenance.', host['host'], host['name'], args.grace) | |
set_offline.append({'hostid': host['hostid']}) | |
# If the host is in the predeployment maintenance group and it has been active for more than the alive threshold, add it to the remove_predeploy list | |
elif ( args.predeploy in [g['name'] for g in host['groups']] and | |
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 hours(s), unmarking host from predeployment.', host['host'], host['name'], args.grace) | |
remove_predeploy.append(host['hostid']) | |
zapi.host.update(hostid=host['hostid'], inventory={"date_hw_install": trigger[0]['lastchange']}) | |
# If the host is in the offline maintenance group and it has been active for more than the alive threshold, add it to the remove_offline list | |
elif ( args.offline in [g['name'] for g in host['groups']] and | |
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 hours(s), unmarking host from offline maintenance.', host['host'], host['name'], args.grace) | |
remove_offline.append(host['hostid']) | |
else: | |
logger.info('%s (%s): Could not find trigger "%s" on this host!!!', host['host'], host['name'], args.trigger) | |
if len(set_offline) > 0: | |
# Set offline hosts in maintenance | |
logger.info('Adding %s host(s) to offline maintenance group "%s".', len(set_offline), args.offline) | |
if not args.noop: | |
try: | |
response = zapi.hostgroup.massadd(**{'groups': offlinegrp, 'hosts': set_offline}) | |
except: | |
logger.error('Something went wrong trying to add %s host(s) to offline maintenance group "%s".',len(set_offline), args.offline) | |
if len(remove_offline) > 0: | |
# Remove online hosts from offline maintenance | |
logger.info('Removing %s host(s) from offline maintenance group "%s".', len(remove_offline), args.offline) | |
if not args.noop: | |
try: | |
response = zapi.hostgroup.massremove(**{'groupids': offlinegrp[0]['groupid'], 'hostids': remove_offline}) | |
except: | |
logger.error('Something went wrong trying to remove %s host(s) from offline maintenance group "%s".',len(remove_offline), args.offline) | |
if len(remove_predeploy) > 0: | |
# Remove online hosts from predeployment maintenance | |
logger.info('Removing %s host(s) from predeployment maintenance group "%s".', len(remove_predeploy), args.predeploy) | |
if not args.noop: | |
try: | |
response = zapi.hostgroup.massremove(**{'groupids': predeploygrp[0]['groupid'], 'hostids': remove_predeploy}) | |
except: | |
logger.error('Something went wrong trying to remove %s host(s) from predeployment maintenance group "%s".',len(remove_offline), args.offline) | |
if len(set_offline) == 0 and len(remove_offline) ==0 and len(remove_predeploy) == 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