Skip to content

Commit

Permalink
Fix: Diagnostic could be improved
Browse files Browse the repository at this point in the history
Solution:
- Add checks on the host connectivity, to help with nodes without IPv6 connectivity
- Add links to troubleshooting docs
- Move IPv6 Egress in details section, it was confusing to have it on the main page.
  • Loading branch information
hoh committed Dec 12, 2023
1 parent 11d1a4f commit 8ba504e
Show file tree
Hide file tree
Showing 7 changed files with 298 additions and 50 deletions.
5 changes: 5 additions & 0 deletions src/aleph/vm/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 2 additions & 0 deletions src/aleph/vm/orchestrator/supervisor.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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"),
Expand Down
27 changes: 27 additions & 0 deletions src/aleph/vm/orchestrator/views/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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")
Expand Down
91 changes: 91 additions & 0 deletions src/aleph/vm/orchestrator/views/host_status.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import socket
from typing import Literal, Union

import aiohttp

from aleph.vm.conf import settings


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) 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


async def check_host_egress_ipv4() -> bool:
"""Check if the host has IPv4 connectivity."""
try:
return await check_ip_connectivity(settings.CONNECTIVITY_IPV4_URL)
except TimeoutError as exc:
print(f"IPv4 connectivity test failed: {exc}")
return False


async def check_host_egress_ipv6() -> bool:
"""Check if the host has IPv6 connectivity."""
try:
return await check_ip_connectivity(settings.CONNECTIVITY_IPV6_URL)
except TimeoutError as exc:
print(f"IPv6 connectivity test failed: {exc}")
return False


async def resolve_dns(hostname: str) -> dict:
"""Resolve a hostname to an IP address."""
info_inet, info_inet6 = socket.getaddrinfo(hostname, 80, proto=socket.IPPROTO_TCP)
ipv4 = info_inet[4][0]
ipv6 = info_inet6[4][0]
return {
"ipv4": ipv4,
"ipv6": ipv6,
}


async def check_dns_ipv4() -> bool:
"""Check if DNS resolution is working via IPv4."""
resolution = await resolve_dns(settings.CONNECTIVITY_DNS_HOSTNAME)
ipv4 = resolution["ipv4"]
return bool(ipv4)


async def check_dns_ipv6() -> bool:
"""Check if DNS resolution is working via IPv6."""
resolution = await resolve_dns(settings.CONNECTIVITY_DNS_HOSTNAME)
ipv6 = resolution["ipv6"]
return bool(ipv6)


async def check_domain_resolution_ipv4() -> bool:
"""Check if the host's hostname resolves to an IPv4 address."""
resolution = await resolve_dns(settings.DOMAIN_NAME)
ipv4 = resolution["ipv4"]
return bool(ipv4)


async def check_domain_resolution_ipv6() -> bool:
"""Check if the host's hostname resolves to an IPv6 address."""
resolution = await resolve_dns(settings.DOMAIN_NAME)
ipv6 = resolution["ipv6"]
return False


async def check_domain_ipv4() -> bool:
"""Check if the host's hostname is accessible via IPv4."""
try:
return await check_ip_connectivity(settings.DOMAIN_NAME, socket.AF_INET)
except TimeoutError as exc:
print(f"IPv4 connectivity test failed: {exc}")
return False


async def check_domain_ipv6() -> bool:
"""Check if the host's hostname is accessible via IPv6."""
try:
return await check_ip_connectivity(settings.DOMAIN_NAME, socket.AF_INET6)
except TimeoutError as exc:
print(f"IPv6 connectivity test failed: {exc}")
return False
34 changes: 33 additions & 1 deletion src/aleph/vm/orchestrator/views/static/helpers.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
async function fetchApiStatus () {
async function fetchFastapiCheckStatus () {
const q = await fetch('/status/check/fastapi');
let res = {
status: q.status,
Expand All @@ -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;
}
Expand All @@ -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 + `<li>${k}: <span style="color: ${v ? 'green' : 'red'}">${v}</span></li>\n`, '');
}

const buildQueryParams = (params) => Object.entries(params).reduce((acc, [k, v]) => acc + `${k}=${v}&`, '?').slice(0, -1);

const isLatestRelease = async () => {
Expand Down
17 changes: 10 additions & 7 deletions src/aleph/vm/orchestrator/views/static/main.css
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
body {
font-family: IBM Plex Regular, monospace;
white-space: normal;
margin: auto;
max-width: 800px;
}

main {
width: 90vw;
margin: 2vh auto;
max-width: 800px;
}

progress {
Expand Down Expand Up @@ -36,29 +39,29 @@ progress {

@keyframes move {
0% {
height: 20px;
height: 10px;
}

50% {
height: 10px;
height: 5px;
}

100% {
height: 20px;
height: 10px;
}
}

@keyframes move2 {
0% {
height: 10px;
height: 5px;
}

50% {
height: 20px;
height: 10px;
}

100% {
height: 10px;
height: 5px;
}
}

Expand Down Expand Up @@ -97,4 +100,4 @@ progress {
footer{
font-size: 70%;
opacity: .75;
}
}
Loading

0 comments on commit 8ba504e

Please sign in to comment.