infra/diagram/main.py
Viktor Barzin 5a0b24f54e [docs] TrueNAS decommission cleanup — remove references from active docs
TrueNAS VM 9000 was operationally decommissioned 2026-04-13; NFS has been
served by Proxmox host (192.168.1.127) since. This commit scrubs remaining
references from active docs. VM 9000 itself remains on PVE in stopped state
pending user decision on deletion.

In-session cleanup already landed: reverse-proxy ingress + Cloudflare record
removed; Technitium DNS records deleted; Vault truenas_{api_key,ssh_private_key}
purged; homepage_credentials.reverse_proxy.truenas_token removed;
truenas_homepage_token variable + module deleted; Loki + Dashy cleaned;
config.tfvars deprecated DNS lines removed; historical-name comment added to
the nfs-truenas StorageClass (48 bound PVs, immutable name — kept).

Historical records (docs/plans/, docs/post-mortems/, .planning/) intentionally
untouched — they describe state at a point in time.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-19 16:55:43 +00:00

209 lines
7.9 KiB
Python

from unicodedata import name
from diagrams import Diagram, Cluster, Edge, Node
from diagrams.generic.compute import Rack
from diagrams.aws.compute import EC2
from diagrams.aws.database import RDS
from diagrams.k8s.network import Service, Ingress
from diagrams.k8s.compute import Pod
from diagrams.aws.network import ELB
from diagrams.onprem.network import Nginx, Pfsense
from diagrams.generic.network import Firewall, Router, Switch, VPN
from diagrams.generic.storage import Storage
from diagrams.generic.os import Windows, Raspbian, IOS
from diagrams.generic.device import Mobile
from diagrams.onprem.client import Client, Users
from diagrams.aws.iot import IotCamera, IotAnalyticsChannel
from kubernetes import client, config
vpn_clients: dict[str, Node] = {}
# namespaces_to_visualize = {
# "website", "vaultwarden", "uptime", "technitium", "reverse-proxy",
# "oauth2", "monitoring", "mailserver", "kms", "immich", "headscale",
# "frigate", "f1-stream", "excalidraw", "dashy", "calibre", "audiobookshelf"
# }
namespaces_to_not_visualize = {
"ytdlp", "wireguard", "webhook-handler", "url", "travel-blog", "registry",
"redis", "openid-help-page", "localai", "kubernetes-dashboard",
"headscale", "hackmd", "finance-app", "dbaas", "crowdsec",
"cloudflared", "city-guesser"
}
# docs for lib - https://diagrams.mingrammer.com/docs/nodes/k8s
def border_router(
name: str,
include_vpn_client: bool = False,
) -> tuple[Firewall, Router]:
with Cluster(name):
tp_link_fw = Firewall()
tp_link_router = Router()
tp_link_fw >> tp_link_router
if include_vpn_client:
vpn_client = VPN(f"{name} Tailscale Client")
vpn_clients[name] = vpn_client
return tp_link_fw, tp_link_router
def sofia():
with Cluster("Sofia"):
_, tp_link_router = border_router("Border Router")
ext_switch = Switch('Extension Switch')
tp_link_router >> ext_switch
with Cluster('R730'):
with Cluster("Pfsense"):
pfsense = Pfsense('Firewall')
vpn_client = VPN("Pfsense Tailscale Client")
vpn_clients["pfsense"] = vpn_client
with Cluster('Kubernetes Network'):
k8s_switch = Switch()
config.load_kube_config()
v1 = client.CoreV1Api()
network_api = client.NetworkingV1Api()
for namespace in v1.list_namespace(watch=False).items:
namespace_name = namespace.metadata.name
# if namespace_name not in namespaces_to_visualize:
# continue
if namespace_name in namespaces_to_not_visualize:
continue
with Cluster(namespace_name):
for ingress in network_api.list_namespaced_ingress(
namespace_name).items:
# for k8s_svc in v1.list_namespaced_service(
# namespace_name).items:
ingress = Ingress(ingress.spec.rules[0].host)
# service = Service(k8s_svc.metadata.name)
# k8s_switch >> service
k8s_switch >> ingress
pfsense >> k8s_switch
with Cluster('Management Network'):
mgt_switch = Switch()
# pxe server
pxe_server = Rack("PXE Server")
# HA
home_assistant = Rack("Home Assistant")
with Cluster("Devvm"):
devvm = Rack("Devvm")
devvm_vpn_client = VPN("Tailscale Client")
vpn_clients["devvm"] = devvm_vpn_client
mgt_switch >> pxe_server
mgt_switch >> home_assistant
mgt_switch >> devvm
pfsense >> mgt_switch
windows10 = Windows("Windows 10 Server")
tp_link_router >> windows10
ext_switch >> pfsense
nas = Storage('Synology NAS')
tp_link_router >> nas
def london():
with Cluster("London"):
_, openwrt = border_router("London OpenWRT", include_vpn_client=True)
rpi = Raspbian()
# client = Mobile()
# ios_client = IOS()
ip_cam = IotCamera("IP Camera")
users = Users()
openwrt >> rpi
# openwrt >> client
# openwrt >> ios_client
openwrt >> users
rpi >> Edge() << ip_cam
def valchedrym():
with Cluster("Valchedrym"):
_, openwrt = border_router("Valchedrym OpenWRT",
include_vpn_client=True)
users = Users()
ip_cam = IotCamera("Surveillance System")
alarm_system = IotAnalyticsChannel("Alarm System")
openwrt >> users
openwrt >> ip_cam
openwrt >> alarm_system
def mladost3():
with Cluster("Mladost 3"):
_, tp_link = border_router("Mladost 3 Router ")
laptop = Windows()
tp_link >> laptop
def outer_infra():
with Diagram("Home Infra", show=False, outformat="png", direction="LR"):
sofia()
london()
valchedrym()
mladost3()
with Cluster("Mobile VPN Clients"):
mobile_vpn_clients = VPN()
vpn_clients["mobile vpn users"] = mobile_vpn_clients
mobile_vpn_users = Users("headscale.viktorbarzin.me/manager")
mobile_vpn_clients >> mobile_vpn_users
# link all vpn clients
existing_links = set()
for vpn_client in vpn_clients.values():
for other_vpn_client in vpn_clients.values():
if other_vpn_client == vpn_client:
continue
key = vpn_client.label + other_vpn_client.label
reverse_key = other_vpn_client.label + vpn_client.label
if key in existing_links or reverse_key in existing_links:
continue
vpn_client >> Edge(color="darkgreen") << other_vpn_client
existing_links.add(key)
def k8s_network():
with Diagram("Kubernetes Network",
show=False,
outformat="png",
direction="LR"):
with Cluster("Kubernetes Network"):
k8s_switch = Switch()
config.load_kube_config()
v1 = client.CoreV1Api()
network_api = client.NetworkingV1Api()
for namespace in v1.list_namespace(watch=False).items:
namespace_name = namespace.metadata.name
# if namespace_name not in namespaces_to_visualize:
# continue
if namespace_name in namespaces_to_not_visualize or namespace_name == "monitoring":
continue
with Cluster(namespace_name):
for ingress in network_api.list_namespaced_ingress(
namespace_name).items:
ing = Ingress(ingress.spec.rules[0].host)
rule = ingress.spec.rules[0]
# for rule in ingress.spec.rules:
path = rule.http.paths[0]
# for path in rule.http.paths:
k8s_svc = path.backend.service
svc = Service(f"{k8s_svc.name}:{k8s_svc.port.number}")
ing >> svc
pods = v1.list_namespaced_pod(namespace_name)
for k8s_pod in pods.items:
if k8s_pod.status.phase != "Running":
continue
pod = Pod(k8s_pod.metadata.name)
svc >> pod
# service = Service(k8s_svc.metadata.name)
# k8s_switch >> service
k8s_switch >> ing
if __name__ == '__main__':
outer_infra()
k8s_network()