Developing your own plugins
To simplify the development of plugins, Bloonix provides the bloonix.plugin module for Python 3. The module is available with the installation of the bloonix-core package. It offers various support, such as parsing script arguments, defining metrics and charts, and checking thresholds. Without this module, developing plugins can become quite complex, especially when it comes to integration into the WebGUI. Here, too, the module provides direct assistance, as all important functions are made available through it.
Example script
In the following, a detailed example script demonstrates how the module works.
#!/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)
Test the script
Check scripts must begin with the prefix `check-` and may only contain the characters `a-z`, `0-9`, and the minus sign. For example:
check-what-does-the-script-check
Save the script under the name `check-url` and run it as follows:
./check-url --help
./check-url --plugin-info --pretty
./check-url --url https://domain.example/ --pretty
Import
Next, the script must be located in the path of the Bloonix plugins. The default path is as follows:
/usr/local/lib/bloonix/plugins
The plugin can then be imported into the WebGUI using the following command:
bloonix-import-plugins-into-db