Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 65 additions & 34 deletions labctl/commands/devices.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
from datetime import datetime
from os import getcwd
from shutil import which
from subprocess import run, PIPE

import typer

from rich.table import Table

from labctl.core import Config, APIDriver, console
from labctl.core import cli_ready, wireguard
from labctl.core import cli_ready

app = typer.Typer()

def parse_datetime(date_str: str) -> str:
return datetime.fromisoformat(date_str.replace("Z", "+00:00")).strftime("%Y-%m-%d %H:%M:%S")

@app.command(name="list")
@cli_ready
def list_devices():
Expand All @@ -18,54 +24,79 @@ def list_devices():
config = Config()
devices = APIDriver().get("/devices/" + config.username).json()
table = Table(title=":computer: Devices")
table.add_column("ID", style="cyan")
table.add_column("Name", style="magenta")
table.add_column("ID", style="bold")
table.add_column("Name", style="cyan")
table.add_column("IPv4", style="green")
table.add_column("RX Bytes", style="blue")
table.add_column("TX Bytes", style="yellow")
table.add_column("Remote IP", style="red")
table.add_column("Created At", style="blue")
table.add_column("Expires At", style="red")
table.add_column("Last Seen", style="yellow")
table.add_column("Online", style="green")

for device in devices:
table.add_row(
device["id"],
device["name"],
device["ipv4"],
device["rx_bytes"],
device["tx_bytes"],
device["remote_ip"],
device["givenName"],
", ".join(device["ipAddresses"]),
parse_datetime(device["createdAt"]),
(device["expiry"] if device["expiry"] != "0001-01-01T00:00:00Z" else "Never"),
parse_datetime(device["lastSeen"]),
"Yes" if device["online"] else "No",
)
console.print(table)

@app.command(name="create")
@app.command(name="enroll")
@cli_ready
def create_device(name: str = typer.Argument(..., help="The device name")):
def enroll():
"""
Self enroll device to vpn
"""
# Todo: Check if tailscale cli is installed and Create preauth key with api and call tailscale cli to enroll device
bin = which("tailscale")
if not bin:
console.print("[red]TailScale cli not found in path please install it[/red]")
return
config = Config()
api_driver = APIDriver()
key_rsp = api_driver.get(f"/devices/{config.username}/preauthkey")
key = key_rsp.json().get("key")
print("Running tailscale login...")
cmd = [bin, "login", "--login-server", "https://gw.laboinfra.net", "--auth-key", key, "--accept-routes", "true"]
print("Execeuting: " + " ".join(cmd))
print("Output: " + run(cmd, stdout=PIPE).stdout.decode())

@app.command(name="logout")
def logout():
"""
Create a device
Self logout
"""
rsp = APIDriver().post(
f"/devices/{Config().username}",
json={"name": name},
additional_headers={"Content-Type": "application/json"}
)
if rsp.status_code >= 200 < 300:
console.print(f"Device {name} created :tada:")
data = rsp.json()
config_path = f"/{getcwd()}/{name}.conf"
wireguard.generate_config(data["device"], data["private_key"], config_path)
console.print(f"Configuration file saved to {config_path}")
bin = which("tailscale")
if not bin:
console.print("[red]TailScale cli not found in path please install it[/red]")
return
console.print(f"Error creating device {name} ({rsp.status_code})")
print("Running tailscale down...")
print("Output: " + run([bin, "down"], stdout=PIPE).stdout.decode())
print("Running tailscale logout...")
print("Output: " + run([bin, "logout"], stdout=PIPE).stdout.decode())


@app.command(name="delete")
@cli_ready
def delete_device(
device_id: str = typer.Argument(..., help="The device ID")
def delete(
name: str = typer.Argument(None, help="The device name (found on the list command)"),
):
"""
Delete a device
Self logout or logout specified device
"""
rsp = APIDriver().delete(f"/devices/{Config().username}/{device_id}")
if rsp.status_code == 200:
console.print(f"Device {device_id} deleted :fire:")
return
console.print(f"Error deleting device {device_id} ({rsp.status_code})")
# Todo : Check if tailscale cli is installed logout user shutdown tailscale and call api to delete device if asked
config = Config()
api_driver = APIDriver()

# delete http://localhost:8000/devices/admin/localhost-6uxdpldr
status = api_driver.delete(f"/devices/{config.username}/{name}").json()

# rsp "success": true, "msg": "Device deleted" }
if status.get("success"):
console.print(f"Device {name} has ben removed from the vpn server :fire:")
else:
console.print(f"Failed to remove device {name}")
console.print(f"Reason given by the server : {status.get("msg")}")
1 change: 0 additions & 1 deletion labctl/core/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,3 @@
from .api import APIDriver
from .console import console
from .decorators import cli_ready
from . import wireguard
16 changes: 0 additions & 16 deletions labctl/core/wireguard.py

This file was deleted.

2 changes: 0 additions & 2 deletions labctl/main.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
from typing import Annotated
from time import sleep
from os import environ
from json import dumps

import requests
import typer
from rich.tree import Tree

Expand Down