diff --git a/fablednetmon/app.py b/fablednetmon/app.py index fbbfe87..ce865af 100644 --- a/fablednetmon/app.py +++ b/fablednetmon/app.py @@ -23,6 +23,7 @@ def create_app( PLUGINS=cfg.get("plugins", {}), SMTP=cfg.get("smtp", {}), WEBHOOK=cfg.get("webhook", {}), + ANSIBLE=cfg.get("ansible", {}), TESTING=testing, _RAW_CFG=cfg, ) @@ -43,11 +44,13 @@ def create_app( from .dashboard.routes import dashboard_bp from .hosts.routes import hosts_bp from .alerts.routes import alerts_bp + from .ansible.routes import ansible_bp app.register_blueprint(auth_bp) app.register_blueprint(dashboard_bp) app.register_blueprint(hosts_bp) app.register_blueprint(alerts_bp) + app.register_blueprint(ansible_bp) # Build task registry app._task_registry = [] @@ -64,6 +67,7 @@ def create_app( @app.before_serving async def startup(): if not testing: + await _mark_interrupted_runs(app) asyncio.create_task(_run_scheduler(app)) return app @@ -130,6 +134,45 @@ def _register_core_tasks(app: Quart, cfg: dict) -> None: ), ]) + # Register git-pull tasks for each git source + ansible_cfg = cfg.get("ansible", {}) + from .ansible.sources import get_sources + for source in get_sources(ansible_cfg): + if source["type"] != "git": + continue + _source = source # capture for closure + + async def run_git_pull(src=_source): + from .ansible.sources import git_pull + await git_pull(src) + + app._task_registry.append( + ScheduledTask( + name=f"ansible_git_pull_{_source['name']}", + coro_factory=run_git_pull, + interval_seconds=_source["pull_interval_seconds"], + run_on_startup=True, + ) + ) + + +async def _mark_interrupted_runs(app: Quart) -> None: + """Mark any runs still in 'running' state as 'interrupted' (process restart).""" + from sqlalchemy import update + from .models.ansible import AnsibleRun, AnsibleRunStatus + from datetime import datetime, timezone + + async with app.db_sessionmaker() as session: + async with session.begin(): + await session.execute( + update(AnsibleRun) + .where(AnsibleRun.status == AnsibleRunStatus.running) + .values( + status=AnsibleRunStatus.interrupted, + finished_at=datetime.now(timezone.utc), + ) + ) + async def _run_scheduler(app: Quart) -> None: from .core.scheduler import start_scheduler diff --git a/fablednetmon/core/cleanup.py b/fablednetmon/core/cleanup.py index 07340a7..8aac89e 100644 --- a/fablednetmon/core/cleanup.py +++ b/fablednetmon/core/cleanup.py @@ -7,6 +7,7 @@ from sqlalchemy import delete from fablednetmon.models.monitors import DnsResult, PingResult from fablednetmon.models.metrics import PluginMetric +from fablednetmon.models.ansible import AnsibleRun if TYPE_CHECKING: from quart import Quart @@ -25,6 +26,7 @@ async def run_cleanup(app: "Quart") -> None: (PingResult, PingResult.probed_at), (DnsResult, DnsResult.resolved_at), (PluginMetric, PluginMetric.recorded_at), + (AnsibleRun, AnsibleRun.started_at), ]: result = await session.execute( delete(model).where(ts_col < cutoff) diff --git a/fablednetmon/templates/base.html b/fablednetmon/templates/base.html index f314ae7..9ca6090 100644 --- a/fablednetmon/templates/base.html +++ b/fablednetmon/templates/base.html @@ -36,6 +36,7 @@ Dashboard Hosts Alerts + Ansible Logout ({{ session.username }}) {% endif %} diff --git a/fablednetmon/templates/dashboard/index.html b/fablednetmon/templates/dashboard/index.html index a60ce9a..255b8f8 100644 --- a/fablednetmon/templates/dashboard/index.html +++ b/fablednetmon/templates/dashboard/index.html @@ -23,7 +23,10 @@
{% endblock %}