mirror of
https://github.com/MacRimi/ProxMenux.git
synced 2026-10-09 23:16:41 +00:00
89 lines
5.2 KiB
Python
89 lines
5.2 KiB
Python
"""Inert infrastructure for actual endpoint/manual/queued/SQLite release seams."""
|
|
import datetime
|
|
import sqlite3
|
|
import tempfile
|
|
import threading
|
|
import time
|
|
import types
|
|
import typing
|
|
from html.parser import HTMLParser
|
|
from pathlib import Path
|
|
from notification_fixture import templates, SCRIPTS, EmailChannel, extract
|
|
|
|
|
|
def visible(markup):
|
|
class Text(HTMLParser):
|
|
def __init__(self): super().__init__(); self.parts = []; self.tags = []
|
|
def handle_data(self, data): self.parts.append(data)
|
|
def handle_starttag(self, tag, attrs): self.tags.append(tag)
|
|
parser = Text(); parser.feed(markup)
|
|
return '\n'.join(p.strip() for p in parser.parts if p.strip()), parser.tags
|
|
|
|
|
|
def restore_event(warnings):
|
|
events = []
|
|
ns = {'request': types.SimpleNamespace(remote_addr='127.0.0.1', get_json=lambda **kw: {
|
|
'hostname':'node-a', 'guests':'3', 'stubs':'1', 'stale_nodes':'0',
|
|
'components':'2', 'duration':'2m', 'warnings':warnings}),
|
|
'notification_manager':types.SimpleNamespace(emit_event=lambda **kw:events.append(kw)),
|
|
'jsonify':lambda value:value}
|
|
handler = extract(SCRIPTS/'flask_notification_routes.py', 'internal_restore_event', None, ns)
|
|
response, status = handler()
|
|
assert status == 200 and len(events) == 1
|
|
return events[0]
|
|
|
|
|
|
def deliver(event_type, data, severity='INFO', language='en', manual=False, quiet=False, quiet_before=()):
|
|
"""Execute real dispatch/manual and optional real SQLite buffer+flush."""
|
|
captured = []
|
|
channel = object.__new__(EmailChannel); channel.subject_prefix = '[ProxMenux]'
|
|
def sink(title, body, severity, data):
|
|
markup = channel._format_html(title, body, severity, data)
|
|
text, tags = visible(markup)
|
|
captured.append(dict(title=title, body=body, severity=severity, data=dict(data), html=markup, text=text, tags=tags))
|
|
return {'success':True}
|
|
manager = types.SimpleNamespace(_config={'email.rich_format':'true'}, _lock=threading.RLock(),
|
|
_channels={'email':types.SimpleNamespace(send=sink)},
|
|
_group_limiter=types.SimpleNamespace(allow=lambda group:True),
|
|
_claim_delivery=lambda event:'inert', _finish_delivery_claim=lambda *a,**kw:None,
|
|
_notification_language=lambda:language, _build_ai_config=lambda:{'ai_enabled':'false'},
|
|
_in_quiet_hours=lambda channel:quiet, _should_buffer_for_digest=lambda *a:False,
|
|
_record_history=lambda *a:None, _stats={'total_sent':0,'total_errors':0},
|
|
is_event_enabled=lambda event:True)
|
|
ns = dict(vars(typing), NotificationEvent=types.SimpleNamespace, TEMPLATES=templates.TEMPLATES, render_template=templates.render_template,
|
|
resolve_notification_hostname=lambda host, config:host or 'node-a',
|
|
enrich_with_emojis=templates.enrich_with_emojis, datetime=datetime.datetime,
|
|
_should_bypass_ai=lambda event:True, _AI_BYPASS_EVENTS=frozenset({'backup_complete','backup_fail'}))
|
|
for name in ('_dispatch_to_channels', '_dispatch_event', 'send_notification'):
|
|
setattr(manager, name, types.MethodType(extract(SCRIPTS/'notification_manager.py', name, 'NotificationManager', ns), manager))
|
|
with tempfile.TemporaryDirectory(prefix='notification-final-') as scratch:
|
|
db = Path(scratch)/'pending.sqlite'
|
|
rows = []
|
|
if quiet:
|
|
conn = sqlite3.connect(db)
|
|
conn.execute('CREATE TABLE quiet_pending (id INTEGER PRIMARY KEY, channel TEXT, event_type TEXT, event_group TEXT, severity TEXT, ts INTEGER, title TEXT, body TEXT)')
|
|
conn.commit(); conn.close()
|
|
qns = dict(vars(typing), sqlite3=sqlite3, DB_PATH=db, time=time, datetime=datetime.datetime,
|
|
_resolve_display_hostname=lambda config:'node-a', runtime_message=templates.runtime_message,
|
|
EVENT_EMOJI=templates.EVENT_EMOJI, CATEGORY_EMOJI=templates.CATEGORY_EMOJI)
|
|
for name in ('_buffer_quiet_event', '_flush_quiet_for_channel', '_compose_digest_body'):
|
|
setattr(manager,name,types.MethodType(extract(SCRIPTS/'notification_manager.py',name,'NotificationManager',qns),manager))
|
|
for earlier in quiet_before:
|
|
manager._dispatch_event(types.SimpleNamespace(**earlier,source='inert',entity_type='node',entity_id='',event_id='inert',fingerprint='inert'))
|
|
if manual:
|
|
assert not quiet
|
|
result = manager.send_notification(event_type, severity, '', '', dict(data))
|
|
assert result['success']
|
|
else:
|
|
event = types.SimpleNamespace(event_type=event_type, severity=severity, data=dict(data), source='inert', entity_type='node', entity_id='', event_id='inert', fingerprint='inert')
|
|
manager._dispatch_event(event)
|
|
if quiet:
|
|
conn = sqlite3.connect(db); rows = conn.execute('SELECT event_type,severity,title,body FROM quiet_pending').fetchall(); conn.close()
|
|
assert len(rows)==1+len(quiet_before) and not captured
|
|
manager._flush_quiet_for_channel('email', manager._channels['email'])
|
|
conn=sqlite3.connect(db); remaining=conn.execute('SELECT count(*) FROM quiet_pending').fetchone()[0]; conn.close()
|
|
assert remaining==0
|
|
assert len(captured)==1
|
|
if quiet: captured[0]['buffered']=rows
|
|
return captured[0]
|