#!/usr/bin/python3
"""Serve diagnostics of the router's first setup on PORT, with the Python standard library alone.

Run by vpn-router-bootstrap.service, which vpn-router-setup starts until a setup has succeeded and
then stops. Every request on any address answers, as plain text, the output of COMMANDS run at that
moment: the state of the last setup run, the state of the services, the addresses, and the end of
the logs.
"""

import subprocess
import sys
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer

PORT = 17081

#: The title of each section and its command.
COMMANDS = (
    ('Setup state', ['cat', '/run/vpn-router/setup.json']),
    ('Services', ['systemctl', 'is-active', 'vpn-router-setup', 'vpn-router-web', 'slapd', 'postgresql',
                  'strongswan', 'openvpn-server@vpn-router', 'wg-quick@wg0']),
    ('Failed units', ['systemctl', '--failed', '--no-legend', '--plain']),
    ('Addresses', ['ip', '-brief', 'address']),
    ('Routes', ['ip', 'route']),
    ('cloud-init', ['cloud-init', 'status', '--long']),
    ('vpn-router-setup log', ['journalctl', '--unit', 'vpn-router-setup', '--lines', '60', '--no-pager', '--output', 'short-iso']),
    ('vpn-router-web log', ['journalctl', '--unit', 'vpn-router-web', '--lines', '30', '--no-pager', '--output', 'short-iso']),
)


def report():
    """The output of every command of COMMANDS under its title."""
    sections = []
    for title, command in COMMANDS:
        try:
            result = subprocess.run(command, capture_output=True, text=True, timeout=10)
            text = (result.stdout + result.stderr).rstrip()
        except (OSError, subprocess.TimeoutExpired) as exc:
            text = f'cannot run {command[0]}: {exc}'
        sections.append(f'== {title} ==\n{text}\n')
    return '\n'.join(sections)


class Handler(BaseHTTPRequestHandler):
    def do_GET(self):
        body = report().encode()
        self.send_response(200)
        self.send_header('Content-Type', 'text/plain; charset=utf-8')
        self.send_header('Content-Length', str(len(body)))
        self.send_header('Cache-Control', 'no-store')
        self.send_header('Refresh', '10')
        self.end_headers()
        self.wfile.write(body)


def failed(request, client_address):
    """Log a failed request in one line, and nothing when the client went away."""
    failure = sys.exc_info()[1]
    if not isinstance(failure, ConnectionError):
        print(f'a request from {client_address[0]} failed: {type(failure).__name__}: {failure}', file=sys.stderr, flush=True)


if __name__ == '__main__':
    sys.excepthook = lambda kind, value, _: print(f'failed: {kind.__name__}: {value}', file=sys.stderr, flush=True)
    server = ThreadingHTTPServer(('', PORT), Handler)
    server.handle_error = failed
    server.serve_forever()
