Hi everyone, I found this page for monitoring PRTG using Nagios/Icinga, and I'd love to try and get this working with PRTG.
It includes a bunch of Python scripts, and as luck would have it, PRTG has a Python sensor. Great! I dropped the files into the Python custom sensors folder and I was able to select check_mailstore.py from the dropdown when creating the sensor.
My question is, how do you specify the parameters? It does not give any examples of how to correctly structure the Additional Parameters field. Would it be like param1=value1,param2=value2,param3=value3, and so on? The python script is looking for arguments starting with two dashes, so I made the Additional Parameters string like this:
--host=mailstore.domain.com,--username=admin,--password=MAILSTOREPASS,--start=since:12H,--warning=10,--critical=10 |
However I am getting a PE231 error:
XML: Structural error in xml file, 1 open items. -- JSON: The returned JSON does not match the expected structure (Invalid JSON.). (code: PE231) |
How do I see what the script is returning, so I can work on debugging this? I'm not really finding anything useful in the various C:\programdata\paessler\PRTG Network Monitor\ log folders.
The check_mailstore.py script is below:
#!/usr/bin/env python3 # check_mailstore.py - Nagios/Icinga plugin that checks MailStore worker results # # Copyright (c) 2012 - 2018 MailStore Software GmbH # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. import sys import time import datetime import mailstore import argparse def convertRange(value): """Convert value into timestamp expected by the API""" # If a range was given do some maths if value.startswith("since:"): value = value[len("since:"):] t = datetime.datetime(*list(time.localtime())[:6]) # Years if "Y" in value: t -= datetime.timedelta(days=365*int(value[:value.find("Y")])) value = value.split("Y")[1] if value and value[0] == "-": value = value[1:] # Months if "m" in value: t -= datetime.timedelta(days=30*int(value[:value.find("m")])) value = value.split("m")[1] if value and value[0] == "-": value = value[1:] # Days if "d" in value: t -= datetime.timedelta(days=int(value[:value.find("d")])) value = value.split("d")[1] if value and value[0] == "T": value = value[1:] # Hours if "H" in value: t -= datetime.timedelta(hours=int(value[:value.find("H")])) value = value.split("H")[1] if value and value[0] == ":": value = value[1:] # Minutes if "M" in value: t -= datetime.timedelta(minutes=int(value[:value.find("M")])) value = value.split("M")[1] if value and value[0] == ":": value = value[1:] # Seconds if "S" in value: t -= datetime.timedelta(seconds=int(value[:value.find("S")])) timestamp = t.strftime("%Y-%m-%dT%H:%M:%S") else: timestamp = value return timestamp def filterResults(results, machinename=None, status="succeeded"): """Filter results by status and machine name machinename: Hostname of the machine where profiles have been executed status: Comma seperated list of executions statuses (default: succeeded) """ out = [] # Iterate over all results for result in results["result"]: found = False # Filter results by status statuses = status.split(",") for status in statuses: if result["result"] == status: found = True break if status.startswith("#"): if result["result"] != status.strip("#"): found = True break if not found: continue # Filter by machine name if machinename is not None: if result["machineName"] != machinename: continue # Append found results to list out.append(result) return out def main(): cmp = lambda a, b: ((a > b) - (a < b)) res = {"le": [0, -1], "ge": [0, 1], "lt": [-1], "gt": [1], "eq": [0]} # Initialize arguments parser parser = argparse.ArgumentParser(description="Checks the recent results of a profile in the given period.", formatter_class=argparse.RawTextHelpFormatter) parser.add_argument("--host", help="Hostname or IP address of MailStore Server") parser.add_argument("--port", default=8463, type=int, help="Port on which MailStore Server provides API access") parser.add_argument("--username", "-user", help="MailStore user name for accessing API") parser.add_argument("--password", "-pass", help="MailStore password for accessing API") parser.add_argument("--start", "-s", required=True, help="""Timestamp to begin the search. Exact timestamp (%%Y-%%m-%%dT%%H:%%M:%%S) or periods (since:[%%Y-][%%m-][%%dT][%%H:][%%M:][%%S]) can be specified. Examples: 2014-01-01T00:00:00 returns profiles since January 1st 2014 since:1Y returns profiles since 1 year ago, since:12H returns profiles since 12 hours ago) since:12:00:00 same as above Note: When using 'since:x', one month counts as 30 days""") parser.add_argument("--end", "-e", default=time.strftime("%Y-%m-%dT%H:%M:%S"), help="Timestamp to end search") parser.add_argument("--timezone", default="$Local", help="Time zone in which the API should return timestamps") parser.add_argument("--machinename", default=None, help="Filter results by specified machine name") parser.add_argument("--profileid", default=None, help="Filter results by specified profile ID") parser.add_argument("--filteruser", default=None, help="Filter results by specified user name") parser.add_argument("--status", default="succeeded", help="Filter results by specified status. Only useful for profiles.") parser.add_argument("--search", default="profiles", help="Filter results either by 'profiles' or 'emails' (default: profiles)") parser.add_argument("--compare", default="le", help="Method for comparing result against value of --warning/--critical [le, ge, eq, gt, lt]") parser.add_argument("--warning", "-w", required=True, type=int, help="Warning threshold") parser.add_argument("--critical", "-c", required=True, type=int, help="Critical threshold") parser.add_argument("--DEBUG", action="store_true", help="Enable debugging") v = parser.parse_args() # Some generic checking of argument values, that would lead # to unexpected results when evaluating result of GetWorkerResults. if v.compare not in res.keys(): parser.print_help() raise Exception("--compare argument not acceptable.") if v.search not in ["profiles", "emails"]: parser.print_help() raise Exception("--search argument is not in [profiles, emails].") # Convert definition of start and end ranges to API compatible timestamp v.start = convertRange(v.start) v.end = convertRange(v.end) # Get the worker results api = mailstore.server.Client(username=v.username, password=v.password, host=v.host, port=v.port, ignoreInvalidSSLCerts=True) results = api.GetWorkerResults(fromIncluding=v.start, toExcluding=v.end, timeZoneID=v.timezone, profileID=v.profileid, userName=v.filteruser) if results["statusCode"] == "succeeded": # Apply machine name and status filter to received worker results # as API itself does not support this type of filters. results = filterResults(results, v.machinename, v.status) else: print("WARNING: GetWorkerResult error:" + results["error"]["message"]) sys.exit(1) if v.DEBUG: for result in sorted(results, key=lambda k: k['completeTime']): print(result) # Determine number of found items... if v.search == "profiles": num = len(results) elif v.search == "emails": num = sum(list(map(lambda x: x["itemsArchived"], results))) else: print("UNKNOWN: invalid 'search' parameter {0} |'{0}'=-1".format(v.search)) sys.exit(3) # ...and find out which state should be returned to Nagios/Icinga. if cmp(num, v.critical) in res[v.compare]: print("CRITICAL: amount of {0} not in range|'{0}'={1}".format(v.search, num)) sys.exit(2) elif cmp(num, v.warning) in res[v.compare]: print("WARNING: amount of {0} not in range|'{0}'={1}".format(v.search, num)) sys.exit(1) else: print("OK: {1} {0}|'{0}'={1}".format(v.search, num)) sys.exit(0) if __name__ == '__main__': main()
Add comment