diff --git a/src/aleph/vm/conf.py b/src/aleph/vm/conf.py index 640d765e3..940f5b17c 100644 --- a/src/aleph/vm/conf.py +++ b/src/aleph/vm/conf.py @@ -127,6 +127,11 @@ class Settings(BaseSettings): WATCH_FOR_UPDATES = True API_SERVER = "https://official.aleph.cloud" + # Connect to the Quad9 VPN provider using their IPv4 and IPv6 addresses. + CONNECTIVITY_IPV4_URL = "https://9.9.9.9/" + CONNECTIVITY_IPV6_URL = "https://[2620:fe::fe]/" + CONNECTIVITY_DNS_HOSTNAME = "example.org" + USE_JAILER = True # System logs make boot ~2x slower PRINT_SYSTEM_LOGS = False diff --git a/src/aleph/vm/orchestrator/supervisor.py b/src/aleph/vm/orchestrator/supervisor.py index ed94ff150..5297c9199 100644 --- a/src/aleph/vm/orchestrator/supervisor.py +++ b/src/aleph/vm/orchestrator/supervisor.py @@ -28,6 +28,7 @@ run_code_from_hostname, run_code_from_path, status_check_fastapi, + status_check_host, status_check_version, status_public_config, update_allocations, @@ -90,6 +91,7 @@ async def allow_cors_on_endpoint(request: web.Request): allow_cors_on_endpoint, ), web.get("/status/check/fastapi", status_check_fastapi), + web.get("/status/check/host", status_check_host), web.get("/status/check/version", status_check_version), web.get("/status/config", status_public_config), web.static("/static", Path(__file__).parent / "views/static"), diff --git a/src/aleph/vm/orchestrator/views/__init__.py b/src/aleph/vm/orchestrator/views/__init__.py index 106aa27e8..11daae50f 100644 --- a/src/aleph/vm/orchestrator/views/__init__.py +++ b/src/aleph/vm/orchestrator/views/__init__.py @@ -26,6 +26,14 @@ from aleph.vm.orchestrator.pubsub import PubSub from aleph.vm.orchestrator.resources import Allocation from aleph.vm.orchestrator.run import run_code_on_request, start_persistent_vm +from aleph.vm.orchestrator.views.host_status import ( + check_dns_ipv4, + check_dns_ipv6, + check_domain_resolution_ipv4, + check_domain_resolution_ipv6, + check_host_egress_ipv4, + check_host_egress_ipv6, +) from aleph.vm.pool import VmPool from aleph.vm.utils import ( HostNotFoundError, @@ -169,6 +177,25 @@ async def status_check_fastapi(request: web.Request): return web.json_response(result, status=200 if all(result.values()) else 503) +async def status_check_host(request: web.Request): + """Check that the platform is supported and configured correctly""" + + result = { + "ipv4": { + "egress": await check_host_egress_ipv4(), + "dns": await check_dns_ipv4(), + "domain": await check_domain_resolution_ipv4(), + }, + "ipv6": { + "egress": await check_host_egress_ipv6(), + "dns": await check_dns_ipv6(), + "domain": await check_domain_resolution_ipv6(), + }, + } + result_status = 200 if all(result["ipv4"].values()) and all(result["ipv6"].values()) else 503 + return web.json_response(result, status=result_status) + + async def status_check_version(request: web.Request): """Check if the software is running a version equal or newer than the given one""" reference_str: Optional[str] = request.query.get("reference") diff --git a/src/aleph/vm/orchestrator/views/host_status.py b/src/aleph/vm/orchestrator/views/host_status.py new file mode 100644 index 000000000..7bea32604 --- /dev/null +++ b/src/aleph/vm/orchestrator/views/host_status.py @@ -0,0 +1,85 @@ +import logging +import socket +from typing import Any, Awaitable, Callable, Tuple + +import aiohttp + +from aleph.vm.conf import settings + +logger = logging.getLogger(__name__) + + +def return_false_on_timeout(func: Callable[..., Awaitable[Any]]) -> Callable[..., Awaitable[bool]]: + async def wrapper(*args: Any, **kwargs: Any) -> bool: + try: + return await func(*args, **kwargs) + except TimeoutError: + logger.warning(f"Timeout while checking {func.__name__}") + return False + + return wrapper + + +async def check_ip_connectivity(url: str, socket_family: socket.AddressFamily = socket.AF_UNSPEC) -> bool: + timeout = aiohttp.ClientTimeout(total=5) + async with aiohttp.ClientSession(connector=aiohttp.TCPConnector(), timeout=timeout) as session: + async with session.get(url, socket_family=socket_family) as resp: + # We expect the Quad9 endpoints to return a 404 error, but other endpoints may return a 200 + if resp.status not in (200, 404): + resp.raise_for_status() + return True + + +@return_false_on_timeout +async def check_host_egress_ipv4() -> bool: + """Check if the host has IPv4 connectivity.""" + return await check_ip_connectivity(settings.CONNECTIVITY_IPV4_URL) + + +@return_false_on_timeout +async def check_host_egress_ipv6() -> bool: + """Check if the host has IPv6 connectivity.""" + return await check_ip_connectivity(settings.CONNECTIVITY_IPV6_URL) + + +async def resolve_dns(hostname: str) -> Tuple[str, str]: + info_inet, info_inet6 = socket.getaddrinfo(hostname, 80, proto=socket.IPPROTO_TCP) + ipv4 = info_inet[4][0] + ipv6 = info_inet6[4][0] + return ipv4, ipv6 + + +async def check_dns_ipv4() -> bool: + """Check if DNS resolution is working via IPv4.""" + ipv4, _ = await resolve_dns(settings.CONNECTIVITY_DNS_HOSTNAME) + return bool(ipv4) + + +async def check_dns_ipv6() -> bool: + """Check if DNS resolution is working via IPv6.""" + _, ipv6 = await resolve_dns(settings.CONNECTIVITY_DNS_HOSTNAME) + return bool(ipv6) + + +async def check_domain_resolution_ipv4() -> bool: + """Check if the host's hostname resolves to an IPv4 address.""" + ipv4, _ = await resolve_dns(settings.DOMAIN_NAME) + return bool(ipv4) + + +async def check_domain_resolution_ipv6() -> bool: + """Check if the host's hostname resolves to an IPv6 address.""" + _, ipv6 = await resolve_dns(settings.DOMAIN_NAME) + return bool(ipv6) + + +@return_false_on_timeout +async def check_domain_ipv4() -> bool: + """Check if the host's hostname is accessible via IPv4.""" + return await check_ip_connectivity(settings.DOMAIN_NAME, socket.AF_INET) + + +@return_false_on_timeout +async def check_domain_ipv6() -> bool: + """Check if the host's hostname is accessible via IPv6.""" + return await check_ip_connectivity(settings.DOMAIN_NAME, socket.AF_INET6) diff --git a/src/aleph/vm/orchestrator/views/static/helpers.js b/src/aleph/vm/orchestrator/views/static/helpers.js index 8335e2faf..46d12e4b6 100644 --- a/src/aleph/vm/orchestrator/views/static/helpers.js +++ b/src/aleph/vm/orchestrator/views/static/helpers.js @@ -1,4 +1,4 @@ -async function fetchApiStatus () { +async function fetchFastapiCheckStatus () { const q = await fetch('/status/check/fastapi'); let res = { status: q.status, @@ -12,8 +12,10 @@ async function fetchApiStatus () { case 503: res.status = " is not working properly ❌"; res.details = await q.json(); + break; case 500: res.status = " ❌ Failed"; + break; default: res.status = q.status; } @@ -22,6 +24,36 @@ async function fetchApiStatus () { return res; } +async function fetchHostCheckStatus () { + const q = await fetch('/status/check/host'); + let res = { + status: q.status, + details: [] + } + if(q.ok){ + res.status = " is working properly ✅"; + } + else { + switch(Number(q.status)){ + case 503: + res.status = " is not working properly ❌"; + res.details = await q.json(); + break; + case 500: + res.status = " ❌ Failed"; + break; + default: + res.status = q.status; + } + } + + return res; +} + +function objectToString (obj) { + return Object.entries(obj).reduce((acc, [k, v]) => acc + `
- It executes user programs stored on the Aleph network in Virtual Machines. + It executes user programs stored on the aleph.im network in Virtual Machines.
- See the repository for more info. + See the source code repository for more info.
@@ -44,34 +44,109 @@
- Virtualization
-
- ...
-
-
-
-
+
+ Virtualization
+
+ ...
+
+
+
+
+
-
-
- Diagnostics checks |
- Open diagnostic VM
-
- Egress IPv6
-
- is ...
-
-
-
-
+
+ Host
+
+ ...
+
+
+
+
+
-
-
+ The aleph.im network measures the performance of all nodes in the network. New metrics are published
+ every 10 minutes.
+
+ 🔍 Browse the metrics in the explorer
+
+ VM Egress IPv6 is a test to check if virtual machines are able to connect to the IPv6 internet.
+ Enabling VM IPv6 Egress requires a specific configuration that is not applied automatically. It is not yet
+ required to run virtual machines.
+
+ VM Egress IPv6
+
+ is ...
+
+
+
+
+
+
+
+ Host status check API: /status/check/host
+
+
+ Virtualization check API: /status/check/fastapi
+
+
+ VM Egress IPv6: Virtualization
+
+
Host connectivity
+ IPv4
+
+
IPv6
+
+
ℹ️ More information
+
+ Latest metrics
+
+
VM Egress IPv6
+ APIs
+
+ /vm/$check_fastapi_vm_id/ip/6
+
+