omdnotificationforwarder 2.6.2.2__tar.gz → 2.7__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. {omdnotificationforwarder-2.6.2.2 → omdnotificationforwarder-2.7}/PKG-INFO +1 -1
  2. {omdnotificationforwarder-2.6.2.2 → omdnotificationforwarder-2.7}/bin/notificationforwarder +1 -1
  3. {omdnotificationforwarder-2.6.2.2 → omdnotificationforwarder-2.7}/pyproject.toml +1 -1
  4. {omdnotificationforwarder-2.6.2.2 → omdnotificationforwarder-2.7}/src/notificationforwarder/baseclass.py +61 -21
  5. {omdnotificationforwarder-2.6.2.2 → omdnotificationforwarder-2.7}/.gitignore +0 -0
  6. {omdnotificationforwarder-2.6.2.2 → omdnotificationforwarder-2.7}/README.md +0 -0
  7. {omdnotificationforwarder-2.6.2.2 → omdnotificationforwarder-2.7}/src/notificationforwarder/email/formatter.py +0 -0
  8. {omdnotificationforwarder-2.6.2.2 → omdnotificationforwarder-2.7}/src/notificationforwarder/email/forwarder.py +0 -0
  9. {omdnotificationforwarder-2.6.2.2 → omdnotificationforwarder-2.7}/src/notificationforwarder/example/formatter.py +0 -0
  10. {omdnotificationforwarder-2.6.2.2 → omdnotificationforwarder-2.7}/src/notificationforwarder/example/forwarder.py +0 -0
  11. {omdnotificationforwarder-2.6.2.2 → omdnotificationforwarder-2.7}/src/notificationforwarder/naemonlog/reporter.py +0 -0
  12. {omdnotificationforwarder-2.6.2.2 → omdnotificationforwarder-2.7}/src/notificationforwarder/rabbitmq/formatter.py +0 -0
  13. {omdnotificationforwarder-2.6.2.2 → omdnotificationforwarder-2.7}/src/notificationforwarder/rabbitmq/forwarder.py +0 -0
  14. {omdnotificationforwarder-2.6.2.2 → omdnotificationforwarder-2.7}/src/notificationforwarder/syslog/formatter.py +0 -0
  15. {omdnotificationforwarder-2.6.2.2 → omdnotificationforwarder-2.7}/src/notificationforwarder/syslog/forwarder.py +0 -0
  16. {omdnotificationforwarder-2.6.2.2 → omdnotificationforwarder-2.7}/src/notificationforwarder/telegram/forwarder.py +0 -0
  17. {omdnotificationforwarder-2.6.2.2 → omdnotificationforwarder-2.7}/src/notificationforwarder/webhook/forwarder.py +0 -0
  18. {omdnotificationforwarder-2.6.2.2 → omdnotificationforwarder-2.7}/tests/pythonpath/lib/python/notificationforwarder/split1/forwarder.py +0 -0
  19. {omdnotificationforwarder-2.6.2.2 → omdnotificationforwarder-2.7}/tests/pythonpath/lib/python/notificationforwarder/split2/formatter.py +0 -0
  20. {omdnotificationforwarder-2.6.2.2 → omdnotificationforwarder-2.7}/tests/pythonpath/lib/python/notificationforwarder/split2/forwarder.py +0 -0
  21. {omdnotificationforwarder-2.6.2.2 → omdnotificationforwarder-2.7}/tests/pythonpath/lib/python/notificationforwarder/split3/formatter.py +0 -0
  22. {omdnotificationforwarder-2.6.2.2 → omdnotificationforwarder-2.7}/tests/pythonpath/lib/python/notificationforwarder/split3/forwarder.py +0 -0
  23. {omdnotificationforwarder-2.6.2.2 → omdnotificationforwarder-2.7}/tests/pythonpath/local/lib/python/notificationforwarder/alertmanager_servicenow/formatter.py +0 -0
  24. {omdnotificationforwarder-2.6.2.2 → omdnotificationforwarder-2.7}/tests/pythonpath/local/lib/python/notificationforwarder/bayern/formatter.py +0 -0
  25. {omdnotificationforwarder-2.6.2.2 → omdnotificationforwarder-2.7}/tests/pythonpath/local/lib/python/notificationforwarder/discard/formatter.py +0 -0
  26. {omdnotificationforwarder-2.6.2.2 → omdnotificationforwarder-2.7}/tests/pythonpath/local/lib/python/notificationforwarder/split1/formatter.py +0 -0
  27. {omdnotificationforwarder-2.6.2.2 → omdnotificationforwarder-2.7}/tests/pythonpath/local/lib/python/notificationforwarder/split2/forwarder.py +0 -0
  28. {omdnotificationforwarder-2.6.2.2 → omdnotificationforwarder-2.7}/tests/pythonpath/local/lib/python/notificationforwarder/split3/formatter.py +0 -0
  29. {omdnotificationforwarder-2.6.2.2 → omdnotificationforwarder-2.7}/tests/pythonpath/local/lib/python/notificationforwarder/split3/forwarder.py +0 -0
  30. {omdnotificationforwarder-2.6.2.2 → omdnotificationforwarder-2.7}/tests/pythonpath/local/lib/python/notificationforwarder/split4/formatter.py +0 -0
  31. {omdnotificationforwarder-2.6.2.2 → omdnotificationforwarder-2.7}/tests/pythonpath/local/lib/python/notificationforwarder/split4/forwarder.py +0 -0
  32. {omdnotificationforwarder-2.6.2.2 → omdnotificationforwarder-2.7}/tests/pythonpath/local/lib/python/notificationforwarder/vong/formatter.py +0 -0
  33. {omdnotificationforwarder-2.6.2.2 → omdnotificationforwarder-2.7}/tests/test_alertmanager.py +0 -0
  34. {omdnotificationforwarder-2.6.2.2 → omdnotificationforwarder-2.7}/tests/test_classes.py +0 -0
  35. {omdnotificationforwarder-2.6.2.2 → omdnotificationforwarder-2.7}/tests/test_discard.py +0 -0
  36. {omdnotificationforwarder-2.6.2.2 → omdnotificationforwarder-2.7}/tests/test_formatter.py +0 -0
  37. {omdnotificationforwarder-2.6.2.2 → omdnotificationforwarder-2.7}/tests/test_package.py +0 -0
  38. {omdnotificationforwarder-2.6.2.2 → omdnotificationforwarder-2.7}/tests/test_paths.py +0 -0
  39. {omdnotificationforwarder-2.6.2.2 → omdnotificationforwarder-2.7}/tests/test_reporter.py +0 -0
  40. {omdnotificationforwarder-2.6.2.2 → omdnotificationforwarder-2.7}/tests/test_webhook.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: omdnotificationforwarder
3
- Version: 2.6.2.2
3
+ Version: 2.7
4
4
  Summary: A framework for notification scripts for OMD
5
5
  Project-URL: Homepage, https://github.com/lausser/noteventificationforhandlerwarder
6
6
  Project-URL: Bug Tracker, https://github.com/lausser/noteventificationforhandlerwarder/issues
@@ -74,7 +74,7 @@ Example for an HTTP-based reporter:
74
74
  help='Increase the log level to DEBUG',
75
75
  default=False)
76
76
  parser.add_argument('--version', action='version',
77
- version=f'%(prog)s 2.6.2.2')
77
+ version=f'%(prog)s 2.7')
78
78
 
79
79
  args = parser.parse_args()
80
80
  if not hasattr(args, 'formatter'):
@@ -21,7 +21,7 @@ packages = ["src/notificationforwarder"]
21
21
 
22
22
  [project]
23
23
  name = "omdnotificationforwarder"
24
- version = "2.6.2.2"
24
+ version = "2.7"
25
25
  authors = [
26
26
  { name="Gerhard Lausser", email="lausser@yahoo.com" },
27
27
  ]
@@ -5,9 +5,11 @@ import socket
5
5
  import traceback
6
6
  import signal
7
7
  import functools
8
+ import threading
8
9
  import errno
9
10
  import fcntl
10
11
  import time
12
+ import random
11
13
  import re
12
14
  try:
13
15
  import simplejson as json
@@ -96,21 +98,50 @@ class ForwarderTimeoutError(Exception):
96
98
  class ReporterTimeoutError(Exception):
97
99
  pass
98
100
 
101
+ # this is my old implementation, which does not work
102
+ # in multi-threaded environments (e.g. a webserver based on
103
+ # bottle+waitress which listens for events and uses
104
+ # the notificationforwarder to deliver them to a ticketing tool.
105
+ #def timeout(seconds, error_message="Timeout"):
106
+ # def decorator(func):
107
+ # @functools.wraps(func)
108
+ # def wrapper(*args, **kwargs):
109
+ # def handler(signum, frame):
110
+ # raise ForwarderTimeoutError(error_message)
111
+ #
112
+ # original_handler = signal.signal(signal.SIGALRM, handler)
113
+ # signal.alarm(seconds)
114
+ # try:
115
+ # result = func(*args, **kwargs)
116
+ # finally:
117
+ # signal.signal(signal.SIGALRM, original_handler)
118
+ # signal.alarm(0)
119
+ # return result
120
+ # return wrapper
121
+ # return decorator
122
+
123
+ # this is the new implementation, which starts a second thread
124
+ # which keeps an eye on the clock
99
125
  def timeout(seconds, error_message="Timeout"):
100
126
  def decorator(func):
101
127
  @functools.wraps(func)
102
128
  def wrapper(*args, **kwargs):
103
- def handler(signum, frame):
104
- raise ForwarderTimeoutError(error_message)
129
+ result = [ForwarderTimeoutError(error_message)]
130
+ def target():
131
+ try:
132
+ result[0] = func(*args, **kwargs)
133
+ except Exception as e:
134
+ result[0] = e
105
135
 
106
- original_handler = signal.signal(signal.SIGALRM, handler)
107
- signal.alarm(seconds)
108
- try:
109
- result = func(*args, **kwargs)
110
- finally:
111
- signal.signal(signal.SIGALRM, original_handler)
112
- signal.alarm(0)
113
- return result
136
+ thread = threading.Thread(target=target)
137
+ thread.daemon = True
138
+ thread.start()
139
+ thread.join(seconds)
140
+ if thread.is_alive():
141
+ raise ForwarderTimeoutError(error_message)
142
+ if isinstance(result[0], Exception):
143
+ raise result[0]
144
+ return result[0]
114
145
  return wrapper
115
146
  return decorator
116
147
 
@@ -139,7 +170,7 @@ class NotificationForwarder(object):
139
170
  timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL
140
171
  )"""
141
172
  try:
142
- self.dbconn = sqlite3.connect(self.db_file)
173
+ self.dbconn = sqlite3.connect(self.db_file, check_same_thread=False)
143
174
  self.dbcurs = self.dbconn.cursor()
144
175
  self.dbcurs.execute(sql_create)
145
176
  self.dbconn.commit()
@@ -186,8 +217,10 @@ class NotificationForwarder(object):
186
217
  After failed attempts, when there are spooled events in the database,
187
218
  a call to probe() can tell the forwarder that the events now can
188
219
  be flushed.
220
+ By default it returns False, so that no unnecessary flush attempts
221
+ are made by forwarders without their own probe().
189
222
  """
190
- return True
223
+ return False
191
224
 
192
225
  def format_event(self, raw_event):
193
226
  instance = self.new_formatter()
@@ -320,19 +353,26 @@ class NotificationForwarder(object):
320
353
  logger.critical("database error "+str(e))
321
354
  logger.info(raw_event)
322
355
 
356
+ def acquire_lock_with_retry(self, lock_file, max_attempts=3, base_delay=0.1):
357
+ for attempt in range(max_attempts):
358
+ try:
359
+ fcntl.lockf(lock_file, fcntl.LOCK_EX | fcntl.LOCK_NB)
360
+ logger.debug("flush lock set")
361
+ return True
362
+ except IOError as e:
363
+ logger.debug(f"flush lock failed (attempt {attempt + 1}): {str(e)}")
364
+ if attempt < max_attempts - 1:
365
+ delay = base_delay * (2 ** attempt) + random.uniform(0, 0.1)
366
+ time.sleep(delay)
367
+ return False
368
+
323
369
  def flush(self):
324
370
  sql_delete = "DELETE FROM "+self.table_name+" WHERE CAST(STRFTIME('%s', timestamp) AS INTEGER) < ?"
325
371
  sql_count = "SELECT COUNT(*) FROM "+self.table_name
326
372
  sql_select = "SELECT id, payload FROM "+self.table_name+" ORDER BY id LIMIT 10"
327
373
  sql_delete_id = "DELETE FROM "+self.table_name+" WHERE id = ?"
328
374
  with open(self.db_lock_file, "w") as lock_file:
329
- try:
330
- fcntl.lockf(lock_file, fcntl.LOCK_EX | fcntl.LOCK_NB)
331
- logger.debug("flush lock set")
332
- locked = True
333
- except IOError as e:
334
- logger.debug("flush lock failed: "+str(e))
335
- locked = False
375
+ locked = acquire_lock_with_retry(lock_file)
336
376
  if locked:
337
377
  try:
338
378
  outdated = int(time.time() - 60*self.max_spool_minutes)
@@ -375,8 +415,8 @@ class NotificationForwarder(object):
375
415
  last_events_to_flush = events_to_flush
376
416
  self.dbconn.commit()
377
417
  except Exception as e:
378
- logger.critical("database flush failed")
379
- logger.critical(e)
418
+ logger.critical(f"database flush+resubmit failed: {e}")
419
+ fcntl.lockf(lock_file, fcntl.LOCK_UN)
380
420
  else:
381
421
  logger.debug("missed the flush lock")
382
422