2022-12-03 16:36:44 +01:00
def main ( ) :
2023-06-18 21:11:37 +02:00
## const
version = " 0.8 "
2022-12-03 16:36:44 +01:00
## setup translation for the first time
translation_initialize ( " en " , env_get_language ( ) )
## args
argumentparser = _argparse . ArgumentParser (
description = translation_get ( " help.title " ) ,
formatter_class = _argparse . ArgumentDefaultsHelpFormatter
)
argumentparser . add_argument (
2023-06-18 21:11:37 +02:00
nargs = " ? " ,
2022-12-03 16:36:44 +01:00
type = str ,
default = " monitoring.hmdl.json " ,
2023-04-28 17:30:51 +02:00
dest = " order_path " ,
metavar = " <order-path> " ,
help = translation_get ( " help.args.order_path " ) ,
2022-12-03 16:36:44 +01:00
)
argumentparser . add_argument (
2023-06-18 21:11:37 +02:00
" -v " ,
" --version " ,
2023-04-28 17:30:51 +02:00
action = " store_true " ,
default = False ,
2023-06-18 21:11:37 +02:00
dest = " show_version " ,
help = translation_get ( " help.args.show_version " ) ,
2023-04-28 17:30:51 +02:00
)
argumentparser . add_argument (
" -s " ,
" --show-schema " ,
action = " store_true " ,
default = False ,
dest = " show_schema " ,
help = translation_get ( " help.args.show_schema " ) ,
)
argumentparser . add_argument (
" -e " ,
" --expose-full-order " ,
action = " store_true " ,
default = False ,
dest = " expose_full_order " ,
help = translation_get ( " help.args.expose_full_order " ) ,
)
2023-06-18 21:11:37 +02:00
argumentparser . add_argument (
" -x " ,
" --erase-state " ,
action = " store_true " ,
default = False ,
dest = " erase_state " ,
help = translation_get ( " help.args.erase_state " ) ,
)
2023-04-28 17:30:51 +02:00
### v conf stuff v
argumentparser . add_argument (
" -d " ,
" --database-path " ,
2022-12-03 16:36:44 +01:00
type = str ,
default = None ,
2023-04-28 17:30:51 +02:00
dest = " database_path " ,
metavar = " <database-path> " ,
help = translation_get ( " help.args.database_path " ) ,
2022-12-03 16:36:44 +01:00
)
2023-03-22 11:06:32 +01:00
argumentparser . add_argument (
" -m " ,
" --mutex-path " ,
type = str ,
2023-04-28 17:30:51 +02:00
default = " /tmp/heimdall.lock " ,
2023-03-22 11:06:32 +01:00
dest = " mutex_path " ,
metavar = " <mutex-path> " ,
help = translation_get ( " help.args.mutex_path " ) ,
)
2022-12-03 16:36:44 +01:00
argumentparser . add_argument (
" -y " ,
" --send-ok-notifications " ,
action = " store_true " ,
default = False ,
dest = " send_ok_notifications " ,
help = translation_get ( " help.args.send_ok_notifications " , { " condition_name " : translation_get ( " conditions.ok " ) } ) ,
)
argumentparser . add_argument (
" -l " ,
" --language " ,
type = str ,
choices = localization_data . keys ( ) ,
default = None ,
dest = " language " ,
metavar = " <language> " ,
help = translation_get ( " help.args.language " ) ,
)
argumentparser . add_argument (
2023-04-28 17:30:51 +02:00
" -t " ,
" --time-to-live " ,
type = int ,
default = ( 60 * 60 * 24 * 7 ) ,
dest = " time_to_live " ,
metavar = " <time-to-live> " ,
help = translation_get ( " help.args.time_to_live " ) ,
2022-12-03 16:36:44 +01:00
)
args = argumentparser . parse_args ( )
## exec
2023-06-18 21:11:37 +02:00
if ( args . show_version ) :
_sys . stdout . write ( version + " \n " )
2022-12-03 16:36:44 +01:00
else :
2023-06-18 21:11:37 +02:00
### setup translation for the second time
if ( args . language is not None ) :
translation_initialize ( " en " , args . language )
2023-03-04 15:51:26 +01:00
2023-06-18 21:11:37 +02:00
### load check kind implementations
check_kind_implementations = {
" script " : implementation_check_kind_script ( ) ,
" file_state " : implementation_check_kind_file_state ( ) ,
" tls_certificate " : implementation_check_kind_tls_certificate ( ) ,
" http_request " : implementation_check_kind_http_request ( ) ,
" generic_remote " : implementation_check_kind_generic_remote ( ) ,
}
### load notification channel implementations
notification_channel_implementations = {
" console " : implementation_notification_channel_console ( ) ,
" email " : implementation_notification_channel_email ( ) ,
" libnotify " : implementation_notification_channel_libnotify ( ) ,
}
if ( args . show_schema ) :
_sys . stdout . write (
_json . dumps (
order_schema_root (
check_kind_implementations ,
notification_channel_implementations
) ,
indent = " \t "
2023-03-04 15:51:26 +01:00
)
2023-06-18 21:11:37 +02:00
+
" \n "
2023-03-04 15:51:26 +01:00
)
2023-06-18 21:11:37 +02:00
else :
### vars
if ( not _os . path . exists ( args . order_path ) ) :
2023-03-22 11:06:32 +01:00
_sys . stderr . write (
string_coin (
2023-06-18 21:11:37 +02:00
" {{ message}} \n " ,
2023-03-22 11:06:32 +01:00
{
2023-06-18 21:11:37 +02:00
" message " : translation_get (
" misc.order_file_not_found " ,
{
" path " : args . order_path ,
}
) ,
2023-03-22 11:06:32 +01:00
}
)
)
2023-06-18 21:11:37 +02:00
_sys . exit ( 1 )
2022-12-03 16:36:44 +01:00
else :
2023-06-18 21:11:37 +02:00
database_path = (
args . database_path
if ( args . database_path is not None ) else
_os . path . join (
_tempfile . gettempdir ( ) ,
string_coin (
" monitoring-state- {{ id}}.sqlite " ,
{
" id " : _hashlib . sha256 ( _os . path . abspath ( args . order_path ) . encode ( " ascii " ) ) . hexdigest ( ) [ : 8 ]
}
)
)
2023-04-28 17:30:51 +02:00
)
2023-03-22 11:06:32 +01:00
2023-06-18 21:11:37 +02:00
### get order data
order = order_load (
check_kind_implementations ,
notification_channel_implementations ,
_os . path . abspath ( args . order_path )
2023-04-28 17:30:51 +02:00
)
2023-06-18 21:11:37 +02:00
if ( args . expose_full_order ) :
_sys . stdout . write ( _json . dumps ( order , indent = " \t " ) + " \n " )
_sys . exit ( 1 )
else :
_sys . stderr . write (
string_coin (
" [info] {{ label}}: {{ path}} \n " ,
{
" label " : translation_get ( " misc.state_file_path " ) ,
" path " : database_path ,
}
)
)
### mutex check
if ( _os . path . exists ( args . mutex_path ) ) :
_sys . stderr . write (
string_coin (
" [error] {{ message}} ( {{ path}}) \n " ,
2023-04-28 17:30:51 +02:00
{
2023-06-18 21:11:37 +02:00
" message " : translation_get ( " misc.still_running " ) ,
" path " : args . mutex_path ,
2023-04-28 17:30:51 +02:00
}
2023-06-18 21:11:37 +02:00
)
)
_sys . exit ( 2 )
2022-12-03 16:36:44 +01:00
else :
2023-06-18 21:11:37 +02:00
### setup database
sqlite_query_set (
database_path ,
" CREATE TABLE IF NOT EXISTS results(check_name TEXT NOT NULL, timestamp INTEGER NOT NULL, condition TEXT NOT NULL, notification_sent BOOLEAN NOT NULL, info TEXT NOT NULL); " ,
{ }
)
### clean database
result = sqlite_query_put (
2023-04-28 17:30:51 +02:00
database_path ,
2023-06-18 21:11:37 +02:00
" DELETE FROM results WHERE ((timestamp < :timestamp_min) OR :erase_state); " ,
2023-04-28 17:30:51 +02:00
{
2023-06-18 21:11:37 +02:00
" timestamp_min " : ( get_current_timestamp ( ) - args . time_to_live ) ,
" erase_state " : args . erase_state ,
2023-04-28 17:30:51 +02:00
}
2023-03-22 11:06:32 +01:00
)
2023-06-18 21:11:37 +02:00
_sys . stderr . write (
string_coin (
" [info] {{ text}} \n " ,
{
" text " : translation_get (
" misc.cleanup_info " ,
{
" count " : ( " %u " % result . rowcount ) ,
}
) ,
}
2022-12-03 16:36:44 +01:00
)
)
2023-06-18 21:11:37 +02:00
file_write ( args . mutex_path , " " , { " append " : True } )
### iterate through checks
for check_data in order [ " checks " ] :
if ( not check_data [ " active " ] ) :
pass
else :
### get old state and examine whether the check shall be executed
rows = sqlite_query_get (
database_path ,
" SELECT timestamp, condition, notification_sent FROM results WHERE (check_name = :check_name) ORDER BY timestamp DESC LIMIT :limit; " ,
2023-03-22 11:06:32 +01:00
{
" check_name " : check_data [ " name " ] ,
2023-06-18 21:11:37 +02:00
" limit " : ( check_data [ " threshold " ] + 1 ) ,
2023-03-22 11:06:32 +01:00
}
)
2023-06-18 21:11:37 +02:00
if ( len ( rows ) < = 0 ) :
old_item_state = None
else :
last_notification_timestamp = None
count = 1
for row in rows [ 1 : ] :
if ( row [ 1 ] == rows [ 0 ] [ 1 ] ) :
count + = 1
else :
break
if ( count > check_data [ " threshold " ] ) :
count = None
else :
pass
for row in rows :
if ( row [ 2 ] ) :
last_notification_timestamp = row [ 0 ]
break
else :
pass
old_item_state = {
" timestamp " : rows [ 0 ] [ 0 ] ,
" condition " : condition_decode ( rows [ 0 ] [ 1 ] ) ,
" count " : count ,
" last_notification_timestamp " : last_notification_timestamp ,
}
timestamp = get_current_timestamp ( )
due = (
2023-03-22 11:24:13 +01:00
( old_item_state is None )
or
2023-06-18 21:11:37 +02:00
( old_item_state [ " condition " ] != enum_condition . ok )
or
( ( timestamp - old_item_state [ " timestamp " ] ) > = check_data [ " schedule " ] [ " regular_interval " ] )
or
(
2023-03-22 11:24:13 +01:00
( old_item_state [ " count " ] is not None )
and
2023-06-18 21:11:37 +02:00
( ( timestamp - old_item_state [ " timestamp " ] ) > = check_data [ " schedule " ] [ " attentive_interval " ] )
)
2023-03-22 11:24:13 +01:00
)
2023-06-18 21:11:37 +02:00
if ( not due ) :
pass
else :
_sys . stderr . write (
string_coin (
" -- {{ check_name}} \n " ,
{
" check_name " : check_data [ " name " ] ,
}
)
2023-03-22 11:06:32 +01:00
)
2023-06-18 21:11:37 +02:00
### execute check and set new state
try :
result = check_kind_implementations [ check_data [ " kind " ] ] . run ( check_data [ " parameters " ] )
except Exception as error :
result = {
" condition " : enum_condition . unknown ,
" info " : {
" cause " : translation_get ( " misc.check_procedure_failed " ) ,
" error " : str ( error ) ,
} ,
}
count = (
1
if (
( old_item_state is None )
or
( old_item_state [ " condition " ] != result [ " condition " ] )
) else
(
( old_item_state [ " count " ] + 1 )
if (
( old_item_state [ " count " ] is not None )
and
( ( old_item_state [ " count " ] + 1 ) < = check_data [ " threshold " ] )
) else
None
)
2023-03-22 11:06:32 +01:00
)
2023-06-18 21:11:37 +02:00
shall_send_notification = (
2023-03-22 11:40:28 +01:00
(
(
2023-06-18 21:11:37 +02:00
( count is not None )
and
( count == check_data [ " threshold " ] )
)
or
(
( count is None )
and
check_data [ " annoy " ]
)
or
(
( count is None )
and
(
( old_item_state is not None )
and
( old_item_state [ " last_notification_timestamp " ] is not None )
and
( check_data [ " schedule " ] [ " reminding_interval " ] is not None )
and
(
( timestamp - old_item_state [ " last_notification_timestamp " ] )
> =
check_data [ " schedule " ] [ " reminding_interval " ]
)
)
2023-03-22 11:40:28 +01:00
)
)
2023-06-18 21:11:37 +02:00
and
(
( result [ " condition " ] != enum_condition . ok )
or
args . send_ok_notifications
)
2023-03-22 11:40:28 +01:00
)
2023-06-18 21:11:37 +02:00
new_item_state = {
" timestamp " : timestamp ,
" condition " : result [ " condition " ] ,
" count " : count ,
" last_notification_timestamp " : (
timestamp
if shall_send_notification else
2023-04-26 17:27:47 +02:00
(
2023-06-18 21:11:37 +02:00
None
if ( old_item_state is None ) else
old_item_state [ " last_notification_timestamp " ]
)
) ,
}
sqlite_query_put (
database_path ,
" INSERT INTO results(check_name, timestamp, condition, notification_sent, info) VALUES (:check_name, :timestamp, :condition, :notification_sent, :info); " ,
{
" check_name " : check_data [ " name " ] ,
" timestamp " : timestamp ,
" condition " : condition_encode ( result [ " condition " ] ) ,
" notification_sent " : shall_send_notification ,
" info " : _json . dumps ( result [ " info " ] ) ,
}
2023-03-22 11:06:32 +01:00
)
2023-06-18 21:11:37 +02:00
### send notifications
if ( not shall_send_notification ) :
pass
else :
for notification in check_data [ " notifications " ] :
notification_channel_implementations [ notification [ " kind " ] ] . notify (
notification [ " parameters " ] ,
check_data [ " name " ] ,
check_data ,
new_item_state ,
dict_merge (
(
{ }
if ( check_data [ " custom " ] is None ) else
{ " custom " : check_data [ " custom " ] }
) ,
result [ " info " ]
)
)
if ( not _os . exists ( args . mutex_path ) ) :
pass
else :
_os . remove ( args . mutex_path )
2022-12-03 16:36:44 +01:00
main ( )