Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New plugin avelon cloud tickets #771

Open
wants to merge 15 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file.
106 changes: 106 additions & 0 deletions check-plugins/avelon-tickets/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
Check avelon-tickets
====================

Overview
--------

Check if there are any pending tickets (alarms) on the Avelon Cloud and whether they are still open, acknowledged, or closed.

What is the Avelon Cloud?
Avelon's products and services have been used in professional building operations for over 20 years. From commercial and industrial buildings to office buildings, airports, and railway facilities. The Avelon Cloud propels buildings into a professional era. Avelon takes care of security and maintenance throughout the entire usage period. With Avelon Cloud, operations become cheaper and more professional.

Notes:

To use this monitoring plugin, you need to have a REST API license from Avelon.
If you already have a license, log in to Avelon and click on Settings in the user menu at the top right. On the General tab, you will see a section called Public OAuth API Key. The Client ID and Client Secret displayed there are required to authenticate with our API (Monitoring-Plugin).

Links:

* `Avelon <https://avelon.com>`_
* `Avelon Cloud Platform <https://avelon.cloud>`_
* `Avelon Documentation <https://avelon.cloud/docs>`_
* `API Documentation <https://avelon.cloud/swagger/swagger-ui/index.html?urls.primaryName=Public%20API#>`_


Fact Sheet
----------

.. csv-table::
:widths: 30, 70

"Check Plugin Download", "https://github.com/Linuxfabrik/monitoring-plugins/tree/main/check-plugins/avelon-tickets"
"Check Interval Recommendation", "Once a minute"
"Can be called without parameters", "No"
"Compiled for", "Linux, Windows"


Help
----

.. code-block:: text

usage: avelon-tickets [-h] [-V] [--always-ok] --client-id CLIENT_ID --client-secret CLIENT_SECRET [--closed-ticket]
[-c [{ACKNOWLEDGED,ACKNOWLEDGED_AND_GONE,GONE,OPEN} ...]] [--insecure] [--no-proxy] --password PASSWORD --username
USERNAME [--test TEST] [--timeout TIMEOUT] [-w [{ACKNOWLEDGED,ACKNOWLEDGED_AND_GONE,GONE,OPEN} ...]]

The current tickets (alerts) of your Avelon Cloud are being reviewed, and depending on their status, critical alerts or warnings can be
triggered. You need a license to access the public API of the Avelon Cloud.

options:
-h, --help show this help message and exit
-V, --version show program's version number and exit
--always-ok Always returns OK.
--client-id CLIENT_ID
Avelon API client_id.
--client-secret CLIENT_SECRET
Avelon API client_secret.
--closed-ticket The option allows viewing the closed alarms as well. Default: False
-c [{ACKNOWLEDGED,ACKNOWLEDGED_AND_GONE,GONE,OPEN} ...], --critical [{ACKNOWLEDGED,ACKNOWLEDGED_AND_GONE,GONE,OPEN} ...]
Set the CRIT threshold as a status of the ticket (alarm). Default: >= []
--insecure This option explicitly allows to perform "insecure" SSL connections. Default: False
--no-proxy Do not use a proxy.Default: False
--password PASSWORD Avelon Cloud password.
--username USERNAME Avelon Cloud username.
--test TEST For unit tests. Needs "path-to-stdout-file,path-to-stderr-file,expected-retc".
--timeout TIMEOUT Network timeout in seconds. Default: 8 (seconds)
-w [{ACKNOWLEDGED,ACKNOWLEDGED_AND_GONE,GONE,OPEN} ...], --warning [{ACKNOWLEDGED,ACKNOWLEDGED_AND_GONE,GONE,OPEN} ...]
Set the WARN threshold as a status of the ticket (alarm). Default: >= ['ACKNOWLEDGED', 'ACKNOWLEDGED_AND_GONE',
'GONE', 'OPEN']


Usage Examples
--------------

.. code-block:: bash

./avelon-tickets --client-id CLIENT_ID --client-secret CLIENT_SECRET --username USER --password PASSWORD --critical ACKNOWLEDGED OPEN

Output:

.. code-block:: text

There are CRITICAL alarm ticket(s).

ID ! Timestamp ! Message ! State
---------+----------------------------------+-------------------------------------------------------------+-------------------------
13927572 ! 2024-06-18 19:46:56 (5D 14h ago) ! Abschaltend: 6102/5/22: Durchfluss Notkühlung FQ201 Störung ! OPEN [CRITICAL]
13927573 ! 2024-06-18 19:46:56 (5D 14h ago) ! Störung: 6102/5/0: Anlage Zustand Störung ! ACKNOWLEDGED [CRITICAL]


States
------

* WARN or CRIT if a ticket (alarm) status matches the defined values (ACKNOWLEDGED, ACKNOWLEDGED_AND_GONE, GONE and OPEN).


Perfdata / Metrics
------------------

There is no perfdata.


Credits, License
----------------

* Authors: `Linuxfabrik GmbH, Zurich <https://www.linuxfabrik.ch>`_
* License: The Unlicense, see `LICENSE file <https://unlicense.org/>`_.
260 changes: 260 additions & 0 deletions check-plugins/avelon-tickets/avelon-tickets
Original file line number Diff line number Diff line change
@@ -0,0 +1,260 @@
#!/usr/bin/env python3
# -*- coding: utf-8; py-indent-offset: 4 -*-
#
# Author: Linuxfabrik GmbH, Zurich, Switzerland
# Contact: info (at) linuxfabrik (dot) ch
# https://www.linuxfabrik.ch/
# License: The Unlicense, see LICENSE file.

# https://github.com/Linuxfabrik/monitoring-plugins/blob/main/CONTRIBUTING.rst

"""See the check's README for more details.
"""

import argparse # pylint: disable=C0413
import datetime # pylint: disable=C0413
import json # pylint: disable=C0413
import re # pylint: disable=C0413
import sys # pylint: disable=C0413

import lib.args # pylint: disable=C0413
import lib.base # pylint: disable=C0413
import lib.human # pylint: disable=C0413
import lib.avelon # pylint: disable=C0413
import lib.test # pylint: disable=C0413
import lib.time # pylint: disable=C0413
import lib.txt # pylint: disable=C0413
from lib.globals import (STATE_OK, STATE_UNKNOWN, # pylint: disable=C0413
STATE_WARN, STATE_CRIT)

__author__ = 'Linuxfabrik GmbH, Zurich/Switzerland'
__version__ = '2024072401'

DESCRIPTION = """The current tickets (alerts) of your Avelon Cloud are being reviewed,
and depending on their status, critical alerts or warnings can be triggered.
You need a license to access the public API of the Avelon Cloud."""

DEFAULT_CLOSED_TICKET = False
DEFAULT_CRIT = []
DEFAULT_INSECURE = False
DEFAULT_NO_PROXY = False
DEFAULT_TIMEOUT = 8
DEFAULT_WARN = ['ACKNOWLEDGED', 'ACKNOWLEDGED_AND_GONE', 'GONE', 'OPEN']

def parse_args():
"""Parse command line arguments using argparse.
"""
parser = argparse.ArgumentParser(description=DESCRIPTION)

# parameter alphabetisch
parser.add_argument(
'-V', '--version',
action='version',
version='%(prog)s: v{} by {}'.format(__version__, __author__)
)

parser.add_argument(
'--always-ok',
help='Always returns OK.',
dest='ALWAYS_OK',
action='store_true',
default=False,
)

parser.add_argument(
'--client-id',
help='Avelon API client_id.',
dest='CLIENT_ID',
required=True,
)

parser.add_argument(
'--client-secret',
help='Avelon API client_secret.',
dest='CLIENT_SECRET',
required=True,
)

parser.add_argument(
'--closed-ticket',
help='The option allows viewing the closed alarms as well. '
'Default: %(default)s',
dest='CLOSED_TICKET',
action='store_true',
default=DEFAULT_CLOSED_TICKET,
)

parser.add_argument(
'-c', '--critical',
help='Set the CRIT threshold as a status of the ticket (alarm). '
'Default: >= %(default)s',
nargs='*',
choices=['ACKNOWLEDGED', 'ACKNOWLEDGED_AND_GONE', 'GONE', 'OPEN'],
dest='CRIT',
default=DEFAULT_CRIT,
)

parser.add_argument(
'--insecure',
help='This option explicitly allows to perform "insecure" SSL connections. '
'Default: %(default)s',
dest='INSECURE',
action='store_true',
default=DEFAULT_INSECURE,
)

parser.add_argument(
'--no-proxy',
help='Do not use a proxy.'
'Default: %(default)s',
dest='NO_PROXY',
action='store_true',
default=DEFAULT_NO_PROXY,
)

parser.add_argument(
'--password',
help='Avelon Cloud password.',
dest='PASSWORD',
required=True,
)

parser.add_argument(
'--username',
help='Avelon Cloud username.',
dest='USERNAME',
required=True,
)

parser.add_argument(
'--test',
help='For unit tests. '
'Needs "path-to-stdout-file,path-to-stderr-file,expected-retc".',
dest='TEST',
type=lib.args.csv,
)

parser.add_argument(
'--timeout',
help='Network timeout in seconds. '
'Default: %(default)s (seconds)',
dest='TIMEOUT',
type=int,
default=DEFAULT_TIMEOUT,
)

parser.add_argument(
'-w', '--warning',
help='Set the WARN threshold as a status of the ticket (alarm). '
'Default: >= %(default)s',
nargs='*',
choices=['ACKNOWLEDGED', 'ACKNOWLEDGED_AND_GONE', 'GONE', 'OPEN'],
dest='WARN',
default=DEFAULT_WARN,
)

return parser.parse_args()


def main():
"""The main function.
"""
# parse the command line, exit with UNKNOWN if it fails
try:
args = parse_args()
except SystemExit:
sys.exit(STATE_UNKNOWN)

# init some vars
msg = ''
state = STATE_OK
ticket_table = []

if args.TEST is None:
# get token
success, token = lib.avelon.get_token(
client_id=args.CLIENT_ID,
client_secret=args.CLIENT_SECRET,
username=args.USERNAME,
password=args.PASSWORD,
insecure=args.INSECURE,
no_proxy=args.NO_PROXY,
timeout=args.TIMEOUT,
)
if not success:
lib.base.cu(token)

# get tickets
success, tickets = lib.avelon.get_tickets(
access_token=token['access_token'],
insecure=args.INSECURE,
no_proxy=args.NO_PROXY,
timeout=args.TIMEOUT,
)
if not success:
lib.base.cu(tickets)
else:
# do not call the command, put in test data
stdout, stderr, retc = lib.test.test(args.TEST)
tickets = json.loads(stdout)

# analyze tickets
for ticket in tickets:
if ticket['type'] == 'ALARM':
locked_state = STATE_OK
if ticket['status'] in args.CRIT:
locked_state = STATE_CRIT
elif ticket['status'] in args.WARN:
locked_state = STATE_WARN
state = lib.base.get_worst(state, locked_state)

# Format timestamp
timestamp = datetime.datetime.strptime(ticket['created'], "%Y-%m-%dT%H:%M:%S.%f%z").strftime("%Y-%m-%d %H:%M:%S")

# Ticket filter for CLOSED tickets
if ticket['status'] != 'CLOSED' or args.CLOSED_TICKET:
# build ticket_table
ticket_table.append({
'id': ticket['id'],
'timestamp': timestamp + ' (' + lib.human.seconds2human(lib.time.timestrdiff(lib.time.now(as_type='iso'), timestamp)) + ' ago)',
'msg': re.sub(r'^ALARM: ', '', ticket['message']),
'state': ticket['status'] + lib.base.state2str(locked_state, prefix=' '),
})

if state == STATE_CRIT:
msg = 'There are CRITICAL alarm ticket(s).\n\n' + msg
elif state == STATE_WARN:
msg = 'There are WARNING alarm ticket(s).\n\n' + msg
else:
msg = 'Everything is ok.\n\n' + msg

# build the message
if ticket_table:
keys = [
'id',
'timestamp',
'msg',
'state',
]
headers = [
'ID',
'Timestamp',
'Message',
'State',
]

msg += lib.base.get_table(
sorted(ticket_table, key=lambda d: d['id']),
keys,
header=headers,
)

# over and out
lib.base.oao(msg, state, always_ok=args.ALWAYS_OK)


if __name__ == '__main__':
try:
main()
except Exception: # pylint: disable=W0703
lib.base.cu()
Loading