Merge pull request 'feat: add zerotier to network cli' (#5178) from Qubasa/clan-core:zerotier_network into main
Reviewed-on: https://git.clan.lol/clan/clan-core/pulls/5178
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -52,3 +52,5 @@ pkgs/clan-app/ui/.fonts
|
|||||||
*.gif
|
*.gif
|
||||||
*.mp4
|
*.mp4
|
||||||
*.mkv
|
*.mkv
|
||||||
|
|
||||||
|
.jj
|
||||||
|
|||||||
@@ -8,8 +8,25 @@
|
|||||||
|
|
||||||
roles.peer = {
|
roles.peer = {
|
||||||
perInstance =
|
perInstance =
|
||||||
{ instanceName, roles, ... }:
|
|
||||||
{
|
{
|
||||||
|
instanceName,
|
||||||
|
roles,
|
||||||
|
lib,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
{
|
||||||
|
exports.networking = {
|
||||||
|
priority = lib.mkDefault 900;
|
||||||
|
# TODO add user space network support to clan-cli
|
||||||
|
module = "clan_lib.network.zerotier";
|
||||||
|
peers = lib.mapAttrs (name: _machine: {
|
||||||
|
host.var = {
|
||||||
|
machine = name;
|
||||||
|
generator = "zerotier";
|
||||||
|
file = "zerotier-ip";
|
||||||
|
};
|
||||||
|
}) roles.peer.machines;
|
||||||
|
};
|
||||||
nixosModule =
|
nixosModule =
|
||||||
{
|
{
|
||||||
config,
|
config,
|
||||||
|
|||||||
@@ -29,10 +29,10 @@ def get_machine_var(machine: Machine, var_id: str) -> Var:
|
|||||||
if var.id.startswith(var_id):
|
if var.id.startswith(var_id):
|
||||||
results.append(var)
|
results.append(var)
|
||||||
if len(results) == 0:
|
if len(results) == 0:
|
||||||
msg = f"No var found for search string: {var_id}"
|
msg = f"Couldn't find var: {var_id} for machine: {machine}"
|
||||||
raise ClanError(msg)
|
raise ClanError(msg)
|
||||||
if len(results) > 1:
|
if len(results) > 1:
|
||||||
error = f"Found multiple vars for {var_id}:\n - " + "\n - ".join(
|
error = f"Found multiple vars in {machine} for {var_id}:\n - " + "\n - ".join(
|
||||||
[str(var) for var in results],
|
[str(var) for var in results],
|
||||||
)
|
)
|
||||||
raise ClanError(error)
|
raise ClanError(error)
|
||||||
|
|||||||
43
pkgs/clan-cli/clan_lib/network/zerotier/__init__.py
Normal file
43
pkgs/clan-cli/clan_lib/network/zerotier/__init__.py
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import logging
|
||||||
|
import time
|
||||||
|
from collections.abc import Iterator
|
||||||
|
from contextlib import contextmanager
|
||||||
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
from clan_lib.errors import ClanError
|
||||||
|
from clan_lib.network import Network, NetworkTechnologyBase, Peer
|
||||||
|
from clan_lib.network.zerotier.lib import check_zerotier_running
|
||||||
|
from clan_lib.ssh.remote import Remote
|
||||||
|
|
||||||
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class NetworkTechnology(NetworkTechnologyBase):
|
||||||
|
def is_running(self) -> bool:
|
||||||
|
return check_zerotier_running()
|
||||||
|
|
||||||
|
def ping(self, remote: Remote) -> None | float:
|
||||||
|
if self.is_running():
|
||||||
|
try:
|
||||||
|
# Use the existing SSH reachability check
|
||||||
|
now = time.time()
|
||||||
|
remote.check_machine_ssh_reachable()
|
||||||
|
|
||||||
|
return (time.time() - now) * 1000
|
||||||
|
|
||||||
|
except ClanError as e:
|
||||||
|
log.debug(f"Error checking peer {remote}: {e}")
|
||||||
|
return None
|
||||||
|
return None
|
||||||
|
|
||||||
|
@contextmanager
|
||||||
|
def connection(self, network: Network) -> Iterator[Network]:
|
||||||
|
# TODO: Implement userspace ZeroTier service start/stop
|
||||||
|
yield network
|
||||||
|
|
||||||
|
def remote(self, peer: Peer) -> "Remote":
|
||||||
|
return Remote(
|
||||||
|
address=peer.host,
|
||||||
|
command_prefix=peer.name,
|
||||||
|
)
|
||||||
27
pkgs/clan-cli/clan_lib/network/zerotier/lib.py
Normal file
27
pkgs/clan-cli/clan_lib/network/zerotier/lib.py
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import contextlib
|
||||||
|
import json
|
||||||
|
import urllib.request
|
||||||
|
from typing import TypedDict
|
||||||
|
|
||||||
|
|
||||||
|
class ZeroTierConfig(TypedDict):
|
||||||
|
clock: int
|
||||||
|
online: bool
|
||||||
|
version: str
|
||||||
|
versionBuild: int
|
||||||
|
versionMajor: int
|
||||||
|
versionMinor: int
|
||||||
|
versionRev: int
|
||||||
|
|
||||||
|
|
||||||
|
def get_zerotier_health() -> ZeroTierConfig:
|
||||||
|
# Placeholder for actual ZeroTier running check
|
||||||
|
res = urllib.request.urlopen("http://localhost:9993/health")
|
||||||
|
return json.load(res)
|
||||||
|
|
||||||
|
|
||||||
|
def check_zerotier_running() -> bool:
|
||||||
|
with contextlib.suppress(urllib.error.URLError):
|
||||||
|
get_zerotier_health()
|
||||||
|
return True
|
||||||
|
return False
|
||||||
Reference in New Issue
Block a user