Plugins selbst entwickeln
Um die Entwicklung von Plugins zu vereinfachen, gibt es das von Bloonix entwickelte Modul bloonix.plugin für Python 3. Das Modul ist verfügbar mit der Installation des Paketes bloonix-core. Es bietet allerlei Hilfestellung, z.B. beim Parsen der Skript-Argumente, der Definition von Metriken und Charts sowie der Prüfung von Schwellwerten. Ohne das Modul kann die Entwicklung von Plugins recht komplex werden, vorallem wenn es um die Integration in der WebGUI geht. Auch hier bietet das Modul direkte Abhilfe, denn alle wichtigen Funktionen werden durch das Modul bereitgestellt.
Beispielskript
Im folgenden wird an einem ausführlichen Beispielskript gezeigt, wie das Modul funktioniert.
#!/usr/bin/python3 # Module import import http.client import re import time from bloonix.plugin.base import Check # When calling the class Check(), various values are set # that describe the plugin. # # version - The version of the plugin. # service - A short note on what exactly the plugin does. # category - Concise categories that apply to the plugin. # E.g. Linux, Windows, SNMP, System, Mail, etc. # abstract - A concise description of the plugin. # This value is used as the name when setting up services # in the WebGUI. # description - A detailed description of the plugin. check = Check( version="1.0", service="Check URL via HTTP/HTTPS", category="HTTP", abstract="Check HTTP", description="Check the response time of a http request.") # The method has_option() is used to define the script parameters. # These are displayed in the WebGUI as Input, Textarea, or Checkboxes. # # name - A human-readable name # option - The name of the option, e.g. host for --host # description - A description of the option # optional - Is the parameter optional? # Default: True # multiple - Can the parameter be specified multiple times? # Default: False # default - The default value if not set. # datatype - The data type. Possible data types are: # bool - just a flag # int - an integer # float - a floating-point number # str - displayed as an input field # lstr - displayed as a textarea # json-kv - displayed with two input fields (key-value) side by side # json-raw - displayed as a textarea # string_condition - [key,op,value] # metric_condition - [key,op,value] # json_condition - [key,op,value] # hint - A short description for the value # regex - A regex for parsing the value # example - A short description of how the parameter is used # command_line_only - This option can only be passed as an argument # via the command line and not via STDIN. # Default: 0 check.has_option( name="URL to request", option="url", hint="url-to-check", datatype="str", optional=False, description="The URL to check.") check.has_option( name="Expected status code", option="status", hint="code", datatype="int", default=200, description="The expected http status code.") check.has_option( name="Set header", option="header", hint="key: value", datatype="str", multiple=True, regex=r"^[a-zA-Z0-9\-]+:.+", description="Set HTTP header for the request.") # The method has_chart() is used to define the charts that can be selected # in the WebGUI for displaying metrics. It is important that the chart ID is unique. # The chart ID should have the format "check-name-num". # # chart_id - A unique chart id in format "check-name-num". # title - The chart title. # ylabel - The label for the y-axis. # units - The unit of the value. Possible units are: # default - no unit # bytes - bytes # chart_type - line or area. Default: line # stack - Stack the series. Default: False # series # check.has_chart( chart_id="check-example-1", title="Request time", ylabel="time", chart_type="area", stack=True, series=[ {"color": "#ffb244", "name": "time_connect"}, {"color": "#e9644a", "name": "time_request"}, ]) # The method has_metric() is used to define all the metrics that are output # when the check is executed. Additionally, the method check_metrics() can # be used later to check the metrics against thresholds. # # # key - The key of the metric # item_id - A item id. This ID must be unique within the check # and may never be changed. # name - A human-readable name. # description - A description of the metric. # datatype - int or float # unit - none, bytes, percent, ms, seconds, temperature check.has_metric( key="time_connect", item_id=1, name="Time to connect", description="The time to connect to the host.") check.has_metric( key="time_request", item_id=2, name="Time to request", description="The time to request.") check.has_metric( key="time_total", item_id=3, name="Total time", description="The total time to request.") # The method has_string() is used to define text strings to check. # Additionally, the method check_matches() can be used later to check # the texts for specific content. check.has_string( key="header", name="Check HTTP header", description="Check the HTTP header with a Python RegEx. Header data are in lowercase.") check.has_string( key="content", name="Check HTTP content", description="Check the HTTP content with a Python RegEx.") # Parse the command line arguments and options which are passed to STDIN. opt = check.parse_options() # Main. header = {} if isinstance(opt["header"], list): for pair in opt["header"]: m = re.search(r"^(.+?):\s*(.+)", pair) header[m.group(1)] = m.group(2) m = re.search(r"^(http|https)://([^/]+)(.*)", opt["url"]) scheme = m.group(1) host = m.group(2) path = m.group(3) port = 80 if scheme == "http" else 443 m = re.search(r"^(.+):(\d+)\Z", host) if m: host = m.group(1) port = m.group(2) if scheme == "http": conn = http.client.HTTPConnection(host=host, port=port) else: conn = http.client.HTTPSConnection(host=host, port=port) time_connect = time.time() conn.connect() time_connect = float(format(time.time() - time_connect, ".3f")) time_request = time.time() conn.request("GET", path, headers=header) res = conn.getresponse() time_request = float(format(time.time() - time_request, ".3f")) status = res.status res_content = res.read() res_header = "" for k, v in res.getheaders(): res_header += "%s: %s\n" % (k.lower(), v.lower()) metrics = { "time_connect": time_connect, "time_request": time_request, "time_total": float(format(time_connect + time_request, ".3f")), } if status != opt["status"]: # Internally the highest exit status is safed. check.set_exit_status("CRITICAL") # Internally all messages are safed and will be printed on exit() check.add_exit_message("http response status %s, expected %s" % (status, opt["status"])) # The method check_matches() is used to check text strings for specific content. check.check_matches( data={ "header": res_header, "content": res_content, }) # The method check_metrics() is used to check the thresholds of the metrics. check.check_metrics( stats=metrics, upshot_keys=["time_total"]) # Exit the check with the method exit(). # # status - Exit with the following status code: # OK, NOTICE, WARNING, ALERT, CRITICAL, UNKNOWN # Default: OK # When using check_metrics() or check_matches(), the highest # status is stored internally and will be returned. # message - The exit message. The method check_metrics sets a message # that includes the keys specified with upshot_keys. # stats - The metrics as a dict. check.exit(stats=metrics)
Teste das Skript
Check Skripte müssen mit dem Prefix `check-` beginnen und dürfen nur die Zeichen `a-z`, `0-9` und das Minuszeichen enthalten. Also zum Beispiel:
check-was-prueft-das-skript
Speichere das Skript unter den Namen check-url und führe es beispielhaft wie folgt aus:
./check-url --help
./check-url --plugin-info --pretty
./check-url --url https://domain.example/ --pretty
Import
Als nächstes muss das Skript unter dem Pfad der Bloonix-Plugins liegen. Der Standardpfad ist wie folgt:
/usr/local/lib/bloonix/plugins
Das Plugin kann dann über folgenden Befehl in die WebGUI importiert werden:
bloonix-import-plugins-into-db