feat: initial commit

This commit is contained in:
nikola
2026-05-19 14:53:37 +02:00
commit 959af6a664
10 changed files with 18587 additions and 0 deletions
+7
View File
@@ -0,0 +1,7 @@
# AGENTS.md
Scope: `/home/nikola/codex-cli/projects/incus-topology-map`
- Keep generated topology snapshots in this project folder.
- Prefer read-only discovery commands for infrastructure mapping.
- Store human-readable outputs as Markdown.
+37
View File
@@ -0,0 +1,37 @@
# Incus Topology Corporate Document
- Diagram file: `incus-topology-corporate.drawio`
- Data snapshot: `data/incus-snapshot-20260409-132237.json`
- Generated on: 2026-04-09
## Scope
This document maps all reachable Incus remotes configured on the workstation at generation time:
- `hetzner-incus2`
- `kuber`
- `local`
- `virgo-incus`
## Visual Standards Applied
- Corporate color coding by environment scope:
- Hetzner remote: orange palette
- LAN remotes: blue palette
- Local remote: neutral gray palette
- Status semantics:
- Running VM: green card
- Stopped VM: red card
- Hierarchy:
- Remote -> Node -> VM
- Per VM metadata:
- `project/name`
- type
- runtime status
- primary IPv4
## Notes
- At generation time, all remotes report `clustered=false`.
- Primary IP excludes bridge-only `172.16.0.0/12` addresses where possible.
## Refresh Procedure
1. Update snapshot via read-only Incus queries.
2. Regenerate `.drawio` using `generate_drawio.py`.
3. Re-open the updated diagram in draw.io / diagrams.net.
Binary file not shown.
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+397
View File
@@ -0,0 +1,397 @@
#!/usr/bin/env python3
import json
import subprocess
import uuid
from datetime import datetime, timezone
from pathlib import Path
import xml.etree.ElementTree as ET
from xml.sax.saxutils import escape as xml_escape
SNAPSHOT = Path('/home/nikola/codex-cli/projects/incus-topology-map/data/incus-snapshot-20260409-132237.json')
OUT = Path('/home/nikola/codex-cli/projects/incus-topology-map/incus-topology-corporate.drawio')
def safe_id(prefix: str) -> str:
return f"{prefix}_{uuid.uuid4().hex[:8]}"
def is_private_bridge_ip(ip: str) -> bool:
if not ip:
return False
octets = ip.split('.')
if len(octets) != 4:
return False
try:
a, b = int(octets[0]), int(octets[1])
except ValueError:
return False
return a == 172 and 16 <= b <= 31
def extract_ips(instance: dict) -> list[str]:
out = []
net = ((instance.get('state') or {}).get('network') or {})
for iface in net.values():
for addr in iface.get('addresses', []):
if addr.get('family') == 'inet' and addr.get('scope') == 'global':
ip = addr.get('address')
if ip and ip not in out:
out.append(ip)
return out
def primary_ip(ips: list[str]) -> str:
for ip in ips:
if not is_private_bridge_ip(ip):
return ip
return ips[0] if ips else 'n/a'
def add_vertex(root, _id, value, style, x, y, w, h, parent='1'):
cell = ET.SubElement(root, 'mxCell', {
'id': _id,
'value': value,
'style': style,
'vertex': '1',
'parent': parent,
})
ET.SubElement(cell, 'mxGeometry', {
'x': str(x), 'y': str(y), 'width': str(w), 'height': str(h), 'as': 'geometry'
})
return cell
def add_edge(root, _id, source, target, style, parent='1'):
cell = ET.SubElement(root, 'mxCell', {
'id': _id,
'edge': '1',
'parent': parent,
'source': source,
'target': target,
'style': style,
})
ET.SubElement(cell, 'mxGeometry', {'relative': '1', 'as': 'geometry'})
return cell
def html(text: str) -> str:
return xml_escape(str(text))
def compact_endpoint(addr: str) -> str:
if not addr or addr == 'n/a':
return 'n/a'
return addr.replace('https://', '').replace('http://', '')
def remote_palette(name: str) -> tuple[str, str, str]:
if name.startswith('hetzner'):
return ('#FFF4E8', '#E38B1A', '#7A3E00')
if name == 'local':
return ('#F5F7FA', '#8B97A8', '#273244')
return ('#ECF3FF', '#3E7BDA', '#123765')
def remote_label(name: str, server_name: str, clustered: bool, addr: str, vm_count: int, node_count: int) -> str:
endpoint = compact_endpoint(addr)
endpoint_line = f'🌍 {html(endpoint)}<br>' if endpoint != 'n/a' else ''
return (
f'<div style="line-height:1.35;">'
f'<span style="font-size:17px;"><b>{html(name)}</b></span><br>'
f'🖥 <b>{html(server_name)}</b> | cluster: <b>{str(clustered).lower()}</b><br>'
f'{endpoint_line}'
f'📦 nodes: <b>{node_count}</b> | vms: <b>{vm_count}</b>'
f'</div>'
)
def classify_vm(vm_name: str) -> tuple[str, str, str, str]:
n = (vm_name or '').lower()
if any(k in n for k in ('gateway', 'proxy', 'ingress', 'route', 'vpn', 'wacli')):
return ('network', 'NET', '🌐', '#1D4ED8')
if any(k in n for k in ('postgres', 'mysql', 'mongo', 'redis', 'cassandra', 'supabase', 'db', 'pg-')):
return ('database', 'DB', '🗄', '#7C3AED')
if any(k in n for k in ('jenkins', 'runner', 'ci', 'build', 'deploy', 'harness')):
return ('cicd', 'CI', '', '#B45309')
if any(k in n for k in ('grafana', 'prometheus', 'loki', 'elk', 'monitor', 'uptime', 'alert')):
return ('observability', 'OBS', '📈', '#0F766E')
if any(k in n for k in ('auth', 'vault', 'keycloak', 'infisical', 'secret')):
return ('security', 'SEC', '🔐', '#BE123C')
if any(k in n for k in ('shell', 'console', 'test', 'ubuntu', 'stage', 'airstrip', 'fileserver')):
return ('utility', 'UTIL', '🧰', '#475569')
return ('application', 'APP', '🧩', '#166534')
def node_category_summary(vms: list[dict]) -> str:
counts: dict[str, int] = {}
labels: dict[str, str] = {}
for vm in vms:
key, short, _, _ = classify_vm(vm.get('name', ''))
counts[key] = counts.get(key, 0) + 1
labels[key] = short
ordered = sorted(counts.items(), key=lambda item: (-item[1], item[0]))
parts = [f'{labels[key]} {value}' for key, value in ordered[:4]]
return ' | '.join(parts) if parts else 'n/a'
def node_label(node_name: str, vm_count: int, running: int, stopped: int, categories: str) -> str:
return (
f'<div style="line-height:1.35;">'
f'<b>Node: {html(node_name)}</b><br>'
f'VMs: <b>{vm_count}</b> | 🟢 {running} | 🔴 {stopped}<br>'
f'roles: {html(categories)}'
f'</div>'
)
def vm_label(project: str, vm_name: str, status: str, pip: str) -> str:
is_running = str(status).lower() == 'running'
state_text = 'RUNNING' if is_running else 'STOPPED'
state_dot = '🟢' if is_running else '🔴'
display_name = f'{project}/{vm_name}' if project != 'default' else vm_name
if len(display_name) > 26:
display_name = display_name[:23] + '...'
return (
f'<div style="line-height:1.3;">'
f'<span style="font-size:13px;"><b>{html(display_name)}</b></span><br>'
f'{state_dot} <b>{state_text}</b> | {html(pip)}'
f'</div>'
)
def main():
data = json.loads(SNAPSHOT.read_text())
remotes = [r for r in data['remotes'] if r.get('name') != 'local']
try:
remotes_meta = json.loads(
subprocess.check_output(["incus", "remote", "list", "--format", "json"], text=True)
)
except Exception:
remotes_meta = {}
mxfile = ET.Element('mxfile', {
'host': 'app.diagrams.net',
'modified': datetime.now(timezone.utc).isoformat(),
'agent': 'codex-gpt-5',
'version': '24.7.17',
'type': 'device',
})
diagram = ET.SubElement(mxfile, 'diagram', {'id': uuid.uuid4().hex[:12], 'name': 'Incus Corporate Topology'})
model = ET.SubElement(diagram, 'mxGraphModel', {
'dx': '2000', 'dy': '1200', 'grid': '1', 'gridSize': '10', 'guides': '1',
'tooltips': '1', 'connect': '1', 'arrows': '1', 'fold': '1', 'page': '1',
'pageScale': '1', 'pageWidth': '2200', 'pageHeight': '1300', 'math': '0', 'shadow': '0'
})
root = ET.SubElement(model, 'root')
ET.SubElement(root, 'mxCell', {'id': '0'})
ET.SubElement(root, 'mxCell', {'id': '1', 'parent': '0'})
title_style = (
'rounded=1;whiteSpace=wrap;html=1;align=left;verticalAlign=middle;'
'fontSize=22;fontStyle=1;fontFamily=Inter;fillColor=#F8FAFC;fontColor=#0F172A;strokeColor=#CBD5E1;'
'spacingLeft=20;arcSize=10;'
)
generated_at = data.get('generated_at', 'n/a')
add_vertex(
root,
safe_id('title'),
f'<div><b>Incus Infrastructure Topology</b><br>Generated: {html(generated_at)}</div>',
title_style,
20,
20,
2160,
78,
)
remote_x = 20
remote_y = 165
remote_w = 525
remote_gap = 20
max_cols = 4
# Keep a fixed generous header area so metadata never gets covered by node cards.
remote_header_h = 106
remote_style_base = (
'rounded=1;whiteSpace=wrap;html=1;align=left;verticalAlign=top;'
'fontSize=13;fontStyle=1;fontFamily=Inter;spacingTop=12;spacingLeft=14;arcSize=10;'
)
node_style = (
'rounded=1;whiteSpace=wrap;html=1;align=left;verticalAlign=top;'
'fontSize=12;fontStyle=1;fontFamily=Inter;fillColor=#F8FAFC;strokeColor=#CBD5E1;fontColor=#0F172A;'
'spacingTop=8;spacingLeft=10;arcSize=8;'
)
vm_running_style = (
'rounded=1;whiteSpace=wrap;html=1;align=left;verticalAlign=top;'
'fontSize=11;fontFamily=Inter;fontColor=#0B1F14;fillColor=#ECFDF3;strokeColor=#86EFAC;'
'spacingTop=10;spacingLeft=10;arcSize=6;'
)
vm_stopped_style = (
'rounded=1;whiteSpace=wrap;html=1;align=left;verticalAlign=top;'
'fontSize=11;fontFamily=Inter;fontColor=#3A1414;fillColor=#FEF2F2;strokeColor=#FCA5A5;'
'spacingTop=10;spacingLeft=10;arcSize=6;'
)
vm_badge_style_base = (
'shape=ellipse;whiteSpace=wrap;html=1;align=center;verticalAlign=middle;'
'fontSize=9;fontStyle=1;fontFamily=Inter;strokeWidth=0;'
)
edge_style = (
'endArrow=none;rounded=1;strokeColor=#94A3B8;strokeWidth=1;opacity=20;dashed=1;'
'orthogonalLoop=1;jettySize=auto;orthogonal=1;'
)
remotes_sorted = sorted(
remotes,
key=lambda r: (
0 if r['name'] == 'local' else 1 if not r['name'].startswith('hetzner') else 2,
r['name'],
),
)
total_nodes = 0
total_vms = 0
total_running = 0
total_stopped = 0
for remote in remotes_sorted:
instances = remote.get('instances', [])
total_vms += len(instances)
total_running += sum(1 for vm in instances if str(vm.get('status', '')).lower() == 'running')
total_stopped += sum(1 for vm in instances if str(vm.get('status', '')).lower() != 'running')
server = remote.get('server', {})
env = server.get('environment', {})
server_name = env.get('server_name', 'unknown')
nodes = {}
for inst in instances:
loc = inst.get('location') or 'none'
node_name = server_name if loc in ('none', '') else loc
nodes.setdefault(node_name, []).append(inst)
if not nodes:
nodes = {server_name: []}
total_nodes += len(nodes)
summary_style = (
'rounded=1;whiteSpace=wrap;html=1;align=left;verticalAlign=middle;'
'fontSize=13;fontStyle=1;fontFamily=Inter;fillColor=#FFFFFF;strokeColor=#CBD5E1;arcSize=8;spacingLeft=12;'
)
chip_w = 210
chip_h = 36
chip_y = 115
add_vertex(root, safe_id('s1'), f'Remotes: <b>{len(remotes_sorted)}</b>', summary_style, 20, chip_y, chip_w, chip_h)
add_vertex(root, safe_id('s2'), f'Nodes: <b>{total_nodes}</b>', summary_style, 240, chip_y, chip_w, chip_h)
add_vertex(root, safe_id('s3'), f'VMs: <b>{total_vms}</b>', summary_style, 460, chip_y, chip_w, chip_h)
add_vertex(root, safe_id('s4'), f'Running: <b>{total_running}</b>', vm_running_style, 680, chip_y, chip_w, chip_h)
add_vertex(root, safe_id('s5'), f'Stopped: <b>{total_stopped}</b>', vm_stopped_style, 900, chip_y, chip_w, chip_h)
max_remote_bottom = 0
row_bottom = remote_y
for idx_remote, remote in enumerate(remotes_sorted):
name = remote['name']
server = remote.get('server', {})
env = server.get('environment', {})
server_name = env.get('server_name', 'unknown')
clustered = env.get('server_clustered', False)
addr = (remotes_meta.get(name) or {}).get('Addr') or 'n/a'
instances = remote.get('instances', [])
nodes = {}
for inst in instances:
loc = inst.get('location') or 'none'
node_name = server_name if loc in ('none', '') else loc
nodes.setdefault(node_name, []).append(inst)
if not nodes:
nodes = {server_name: []}
node_heights = []
for node_name, vms in nodes.items():
rows = max(1, (len(vms) + 1) // 2)
node_h = 50 + rows * 74 + 16
node_heights.append(node_h)
remote_h = remote_header_h + sum(node_heights) + (len(node_heights) - 1) * 16 + 20
fill, stroke, font = remote_palette(name)
remote_style = remote_style_base + f'fillColor={fill};strokeColor={stroke};fontColor={font};'
remote_id = safe_id('remote')
remote_label_value = remote_label(name, server_name, clustered, addr if addr else 'n/a', len(instances), len(nodes))
add_vertex(root, remote_id, remote_label_value, remote_style, remote_x, remote_y, remote_w, remote_h)
current_y = remote_y + remote_header_h
for node_name, vms in nodes.items():
rows = max(1, (len(vms) + 1) // 2)
node_h = 50 + rows * 74 + 16
node_id = safe_id('node')
running = sum(1 for vm in vms if str(vm.get('status', '')).lower() == 'running')
stopped = len(vms) - running
categories = node_category_summary(vms)
add_vertex(
root,
node_id,
node_label(node_name, len(vms), running, stopped, categories),
node_style,
remote_x + 12,
current_y,
remote_w - 24,
node_h,
)
vm_w = (remote_w - 24 - 18) / 2
for idx, vm in enumerate(vms):
col = idx % 2
row = idx // 2
vm_x = remote_x + 20 + col * (vm_w + 10)
vm_y = current_y + 46 + row * 74
status = vm.get('status', 'Unknown')
vm_name = vm.get('name', 'unknown')
project = vm.get('project', 'default')
ips = extract_ips(vm)
pip = primary_ip(ips)
_, short_role, role_icon, role_color = classify_vm(vm_name)
vm_style = vm_running_style if str(status).lower() == 'running' else vm_stopped_style
vm_style += 'strokeWidth=1.2;'
vm_id = safe_id('vm')
add_vertex(root, vm_id, vm_label(project, vm_name, status, pip), vm_style, vm_x, vm_y, vm_w, 66)
add_vertex(
root,
safe_id('role'),
role_icon,
vm_badge_style_base + f'fillColor={role_color};strokeColor={role_color};fontColor=#FFFFFF;',
vm_x + vm_w - 26,
vm_y + 5,
18,
18,
)
current_y += node_h + 16
max_remote_bottom = max(max_remote_bottom, remote_y + remote_h)
row_bottom = max(row_bottom, remote_y + remote_h)
if (idx_remote + 1) % max_cols == 0:
remote_x = 20
remote_y = row_bottom + 20
row_bottom = remote_y
else:
remote_x += remote_w + remote_gap
legend_y = max_remote_bottom + 20
legend_style = (
'rounded=1;whiteSpace=wrap;html=1;align=left;verticalAlign=middle;'
'fontSize=12;fontStyle=1;fontFamily=Inter;fillColor=#FFFFFF;strokeColor=#CBD5E1;fontColor=#0F172A;arcSize=8;'
)
add_vertex(root, safe_id('legend_title'), 'Legend', legend_style, 20, legend_y, 140, 36)
add_vertex(root, safe_id('lg1'), 'Running VM', vm_running_style, 170, legend_y, 180, 36)
add_vertex(root, safe_id('lg2'), 'Stopped VM', vm_stopped_style, 360, legend_y, 180, 36)
add_vertex(root, safe_id('lg5'), 'Role badges: 🌐 🗄 ⚙ 📈 🔐 🧰 🧩', legend_style, 550, legend_y, 340, 36)
ET.indent(mxfile, space=' ')
OUT.write_text(ET.tostring(mxfile, encoding='unicode'))
print(str(OUT))
if __name__ == '__main__':
main()
+190
View File
@@ -0,0 +1,190 @@
<mxfile host="app.diagrams.net" modified="2026-04-09T11:58:51.653223+00:00" agent="codex-gpt-5" version="24.7.17" type="device">
<diagram id="70f5a7db7c81" name="Incus Corporate Topology">
<mxGraphModel dx="2000" dy="1200" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="2200" pageHeight="1300" math="0" shadow="0">
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
<mxCell id="title_74850cde" value="&lt;div&gt;&lt;b&gt;Incus Infrastructure Topology&lt;/b&gt;&lt;br&gt;Generated: 2026-04-09T13:22:37+02:00&lt;/div&gt;" style="rounded=1;whiteSpace=wrap;html=1;align=left;verticalAlign=middle;fontSize=22;fontStyle=1;fontFamily=Inter;fillColor=#F8FAFC;fontColor=#0F172A;strokeColor=#CBD5E1;spacingLeft=20;arcSize=10;" vertex="1" parent="1">
<mxGeometry x="20" y="20" width="2160" height="78" as="geometry" />
</mxCell>
<mxCell id="s1_ce4710a9" value="Remotes: &lt;b&gt;3&lt;/b&gt;" style="rounded=1;whiteSpace=wrap;html=1;align=left;verticalAlign=middle;fontSize=13;fontStyle=1;fontFamily=Inter;fillColor=#FFFFFF;strokeColor=#CBD5E1;arcSize=8;spacingLeft=12;" vertex="1" parent="1">
<mxGeometry x="20" y="115" width="210" height="36" as="geometry" />
</mxCell>
<mxCell id="s2_468d3274" value="Nodes: &lt;b&gt;3&lt;/b&gt;" style="rounded=1;whiteSpace=wrap;html=1;align=left;verticalAlign=middle;fontSize=13;fontStyle=1;fontFamily=Inter;fillColor=#FFFFFF;strokeColor=#CBD5E1;arcSize=8;spacingLeft=12;" vertex="1" parent="1">
<mxGeometry x="240" y="115" width="210" height="36" as="geometry" />
</mxCell>
<mxCell id="s3_b52be8f4" value="VMs: &lt;b&gt;22&lt;/b&gt;" style="rounded=1;whiteSpace=wrap;html=1;align=left;verticalAlign=middle;fontSize=13;fontStyle=1;fontFamily=Inter;fillColor=#FFFFFF;strokeColor=#CBD5E1;arcSize=8;spacingLeft=12;" vertex="1" parent="1">
<mxGeometry x="460" y="115" width="210" height="36" as="geometry" />
</mxCell>
<mxCell id="s4_29c797ce" value="Running: &lt;b&gt;17&lt;/b&gt;" style="rounded=1;whiteSpace=wrap;html=1;align=left;verticalAlign=top;fontSize=11;fontFamily=Inter;fontColor=#0B1F14;fillColor=#ECFDF3;strokeColor=#86EFAC;spacingTop=10;spacingLeft=10;arcSize=6;" vertex="1" parent="1">
<mxGeometry x="680" y="115" width="210" height="36" as="geometry" />
</mxCell>
<mxCell id="s5_919275f4" value="Stopped: &lt;b&gt;5&lt;/b&gt;" style="rounded=1;whiteSpace=wrap;html=1;align=left;verticalAlign=top;fontSize=11;fontFamily=Inter;fontColor=#3A1414;fillColor=#FEF2F2;strokeColor=#FCA5A5;spacingTop=10;spacingLeft=10;arcSize=6;" vertex="1" parent="1">
<mxGeometry x="900" y="115" width="210" height="36" as="geometry" />
</mxCell>
<mxCell id="remote_94130581" value="&lt;div style=&quot;line-height:1.35;&quot;&gt;&lt;span style=&quot;font-size:17px;&quot;&gt;&lt;b&gt;kuber&lt;/b&gt;&lt;/span&gt;&lt;br&gt;🖥 &lt;b&gt;kuber&lt;/b&gt; | cluster: &lt;b&gt;false&lt;/b&gt;&lt;br&gt;🌍 10.0.50.79:8443&lt;br&gt;📦 nodes: &lt;b&gt;1&lt;/b&gt; | vms: &lt;b&gt;8&lt;/b&gt;&lt;/div&gt;" style="rounded=1;whiteSpace=wrap;html=1;align=left;verticalAlign=top;fontSize=13;fontStyle=1;fontFamily=Inter;spacingTop=12;spacingLeft=14;arcSize=10;fillColor=#ECF3FF;strokeColor=#3E7BDA;fontColor=#123765;" vertex="1" parent="1">
<mxGeometry x="20" y="165" width="525" height="488" as="geometry" />
</mxCell>
<mxCell id="node_0fae4ba7" value="&lt;div style=&quot;line-height:1.35;&quot;&gt;&lt;b&gt;Node: kuber&lt;/b&gt;&lt;br&gt;VMs: &lt;b&gt;8&lt;/b&gt; | 🟢 5 | 🔴 3&lt;br&gt;roles: UTIL 6 | CI 1 | DB 1&lt;/div&gt;" style="rounded=1;whiteSpace=wrap;html=1;align=left;verticalAlign=top;fontSize=12;fontStyle=1;fontFamily=Inter;fillColor=#F8FAFC;strokeColor=#CBD5E1;fontColor=#0F172A;spacingTop=8;spacingLeft=10;arcSize=8;" vertex="1" parent="1">
<mxGeometry x="32" y="271" width="501" height="362" as="geometry" />
</mxCell>
<mxCell id="vm_1bcbc1bd" value="&lt;div style=&quot;line-height:1.3;&quot;&gt;&lt;span style=&quot;font-size:13px;&quot;&gt;&lt;b&gt;cassandra&lt;/b&gt;&lt;/span&gt;&lt;br&gt;🟢 &lt;b&gt;RUNNING&lt;/b&gt; | 10.0.50.164&lt;/div&gt;" style="rounded=1;whiteSpace=wrap;html=1;align=left;verticalAlign=top;fontSize=11;fontFamily=Inter;fontColor=#0B1F14;fillColor=#ECFDF3;strokeColor=#86EFAC;spacingTop=10;spacingLeft=10;arcSize=6;strokeWidth=1.2;" vertex="1" parent="1">
<mxGeometry x="40.0" y="317" width="241.5" height="66" as="geometry" />
</mxCell>
<mxCell id="role_5350e310" value="🗄" style="shape=ellipse;whiteSpace=wrap;html=1;align=center;verticalAlign=middle;fontSize=9;fontStyle=1;fontFamily=Inter;strokeWidth=0;fillColor=#7C3AED;strokeColor=#7C3AED;fontColor=#FFFFFF;" vertex="1" parent="1">
<mxGeometry x="255.5" y="322" width="18" height="18" as="geometry" />
</mxCell>
<mxCell id="vm_1df3d2f6" value="&lt;div style=&quot;line-height:1.3;&quot;&gt;&lt;span style=&quot;font-size:13px;&quot;&gt;&lt;b&gt;fileserver&lt;/b&gt;&lt;/span&gt;&lt;br&gt;🔴 &lt;b&gt;STOPPED&lt;/b&gt; | n/a&lt;/div&gt;" style="rounded=1;whiteSpace=wrap;html=1;align=left;verticalAlign=top;fontSize=11;fontFamily=Inter;fontColor=#3A1414;fillColor=#FEF2F2;strokeColor=#FCA5A5;spacingTop=10;spacingLeft=10;arcSize=6;strokeWidth=1.2;" vertex="1" parent="1">
<mxGeometry x="291.5" y="317" width="241.5" height="66" as="geometry" />
</mxCell>
<mxCell id="role_c8808ac1" value="🧰" style="shape=ellipse;whiteSpace=wrap;html=1;align=center;verticalAlign=middle;fontSize=9;fontStyle=1;fontFamily=Inter;strokeWidth=0;fillColor=#475569;strokeColor=#475569;fontColor=#FFFFFF;" vertex="1" parent="1">
<mxGeometry x="507.0" y="322" width="18" height="18" as="geometry" />
</mxCell>
<mxCell id="vm_5a665c77" value="&lt;div style=&quot;line-height:1.3;&quot;&gt;&lt;span style=&quot;font-size:13px;&quot;&gt;&lt;b&gt;harness&lt;/b&gt;&lt;/span&gt;&lt;br&gt;🔴 &lt;b&gt;STOPPED&lt;/b&gt; | n/a&lt;/div&gt;" style="rounded=1;whiteSpace=wrap;html=1;align=left;verticalAlign=top;fontSize=11;fontFamily=Inter;fontColor=#3A1414;fillColor=#FEF2F2;strokeColor=#FCA5A5;spacingTop=10;spacingLeft=10;arcSize=6;strokeWidth=1.2;" vertex="1" parent="1">
<mxGeometry x="40.0" y="391" width="241.5" height="66" as="geometry" />
</mxCell>
<mxCell id="role_638a62d1" value="⚙" style="shape=ellipse;whiteSpace=wrap;html=1;align=center;verticalAlign=middle;fontSize=9;fontStyle=1;fontFamily=Inter;strokeWidth=0;fillColor=#B45309;strokeColor=#B45309;fontColor=#FFFFFF;" vertex="1" parent="1">
<mxGeometry x="255.5" y="396" width="18" height="18" as="geometry" />
</mxCell>
<mxCell id="vm_289ebce7" value="&lt;div style=&quot;line-height:1.3;&quot;&gt;&lt;span style=&quot;font-size:13px;&quot;&gt;&lt;b&gt;lxconsole&lt;/b&gt;&lt;/span&gt;&lt;br&gt;🟢 &lt;b&gt;RUNNING&lt;/b&gt; | 10.0.50.173&lt;/div&gt;" style="rounded=1;whiteSpace=wrap;html=1;align=left;verticalAlign=top;fontSize=11;fontFamily=Inter;fontColor=#0B1F14;fillColor=#ECFDF3;strokeColor=#86EFAC;spacingTop=10;spacingLeft=10;arcSize=6;strokeWidth=1.2;" vertex="1" parent="1">
<mxGeometry x="291.5" y="391" width="241.5" height="66" as="geometry" />
</mxCell>
<mxCell id="role_defdb2b5" value="🧰" style="shape=ellipse;whiteSpace=wrap;html=1;align=center;verticalAlign=middle;fontSize=9;fontStyle=1;fontFamily=Inter;strokeWidth=0;fillColor=#475569;strokeColor=#475569;fontColor=#FFFFFF;" vertex="1" parent="1">
<mxGeometry x="507.0" y="396" width="18" height="18" as="geometry" />
</mxCell>
<mxCell id="vm_a95fc429" value="&lt;div style=&quot;line-height:1.3;&quot;&gt;&lt;span style=&quot;font-size:13px;&quot;&gt;&lt;b&gt;shellhub&lt;/b&gt;&lt;/span&gt;&lt;br&gt;🔴 &lt;b&gt;STOPPED&lt;/b&gt; | n/a&lt;/div&gt;" style="rounded=1;whiteSpace=wrap;html=1;align=left;verticalAlign=top;fontSize=11;fontFamily=Inter;fontColor=#3A1414;fillColor=#FEF2F2;strokeColor=#FCA5A5;spacingTop=10;spacingLeft=10;arcSize=6;strokeWidth=1.2;" vertex="1" parent="1">
<mxGeometry x="40.0" y="465" width="241.5" height="66" as="geometry" />
</mxCell>
<mxCell id="role_8a6caa1f" value="🧰" style="shape=ellipse;whiteSpace=wrap;html=1;align=center;verticalAlign=middle;fontSize=9;fontStyle=1;fontFamily=Inter;strokeWidth=0;fillColor=#475569;strokeColor=#475569;fontColor=#FFFFFF;" vertex="1" parent="1">
<mxGeometry x="255.5" y="470" width="18" height="18" as="geometry" />
</mxCell>
<mxCell id="vm_ad398d3b" value="&lt;div style=&quot;line-height:1.3;&quot;&gt;&lt;span style=&quot;font-size:13px;&quot;&gt;&lt;b&gt;testvm&lt;/b&gt;&lt;/span&gt;&lt;br&gt;🟢 &lt;b&gt;RUNNING&lt;/b&gt; | 10.0.50.45&lt;/div&gt;" style="rounded=1;whiteSpace=wrap;html=1;align=left;verticalAlign=top;fontSize=11;fontFamily=Inter;fontColor=#0B1F14;fillColor=#ECFDF3;strokeColor=#86EFAC;spacingTop=10;spacingLeft=10;arcSize=6;strokeWidth=1.2;" vertex="1" parent="1">
<mxGeometry x="291.5" y="465" width="241.5" height="66" as="geometry" />
</mxCell>
<mxCell id="role_fec1eca0" value="🧰" style="shape=ellipse;whiteSpace=wrap;html=1;align=center;verticalAlign=middle;fontSize=9;fontStyle=1;fontFamily=Inter;strokeWidth=0;fillColor=#475569;strokeColor=#475569;fontColor=#FFFFFF;" vertex="1" parent="1">
<mxGeometry x="507.0" y="470" width="18" height="18" as="geometry" />
</mxCell>
<mxCell id="vm_4e7be1a8" value="&lt;div style=&quot;line-height:1.3;&quot;&gt;&lt;span style=&quot;font-size:13px;&quot;&gt;&lt;b&gt;ubuntu-22-plcnext-conn&lt;/b&gt;&lt;/span&gt;&lt;br&gt;🟢 &lt;b&gt;RUNNING&lt;/b&gt; | 10.0.50.43&lt;/div&gt;" style="rounded=1;whiteSpace=wrap;html=1;align=left;verticalAlign=top;fontSize=11;fontFamily=Inter;fontColor=#0B1F14;fillColor=#ECFDF3;strokeColor=#86EFAC;spacingTop=10;spacingLeft=10;arcSize=6;strokeWidth=1.2;" vertex="1" parent="1">
<mxGeometry x="40.0" y="539" width="241.5" height="66" as="geometry" />
</mxCell>
<mxCell id="role_9f2596bb" value="🧰" style="shape=ellipse;whiteSpace=wrap;html=1;align=center;verticalAlign=middle;fontSize=9;fontStyle=1;fontFamily=Inter;strokeWidth=0;fillColor=#475569;strokeColor=#475569;fontColor=#FFFFFF;" vertex="1" parent="1">
<mxGeometry x="255.5" y="544" width="18" height="18" as="geometry" />
</mxCell>
<mxCell id="vm_813c86bb" value="&lt;div style=&quot;line-height:1.3;&quot;&gt;&lt;span style=&quot;font-size:13px;&quot;&gt;&lt;b&gt;ubuntu20&lt;/b&gt;&lt;/span&gt;&lt;br&gt;🟢 &lt;b&gt;RUNNING&lt;/b&gt; | 10.0.50.181&lt;/div&gt;" style="rounded=1;whiteSpace=wrap;html=1;align=left;verticalAlign=top;fontSize=11;fontFamily=Inter;fontColor=#0B1F14;fillColor=#ECFDF3;strokeColor=#86EFAC;spacingTop=10;spacingLeft=10;arcSize=6;strokeWidth=1.2;" vertex="1" parent="1">
<mxGeometry x="291.5" y="539" width="241.5" height="66" as="geometry" />
</mxCell>
<mxCell id="role_e45a2a04" value="🧰" style="shape=ellipse;whiteSpace=wrap;html=1;align=center;verticalAlign=middle;fontSize=9;fontStyle=1;fontFamily=Inter;strokeWidth=0;fillColor=#475569;strokeColor=#475569;fontColor=#FFFFFF;" vertex="1" parent="1">
<mxGeometry x="507.0" y="544" width="18" height="18" as="geometry" />
</mxCell>
<mxCell id="remote_d51f11c7" value="&lt;div style=&quot;line-height:1.35;&quot;&gt;&lt;span style=&quot;font-size:17px;&quot;&gt;&lt;b&gt;virgo-incus&lt;/b&gt;&lt;/span&gt;&lt;br&gt;🖥 &lt;b&gt;virgo&lt;/b&gt; | cluster: &lt;b&gt;false&lt;/b&gt;&lt;br&gt;🌍 10.0.50.9:8443&lt;br&gt;📦 nodes: &lt;b&gt;1&lt;/b&gt; | vms: &lt;b&gt;8&lt;/b&gt;&lt;/div&gt;" style="rounded=1;whiteSpace=wrap;html=1;align=left;verticalAlign=top;fontSize=13;fontStyle=1;fontFamily=Inter;spacingTop=12;spacingLeft=14;arcSize=10;fillColor=#ECF3FF;strokeColor=#3E7BDA;fontColor=#123765;" vertex="1" parent="1">
<mxGeometry x="565" y="165" width="525" height="488" as="geometry" />
</mxCell>
<mxCell id="node_1b8d7cdd" value="&lt;div style=&quot;line-height:1.35;&quot;&gt;&lt;b&gt;Node: virgo&lt;/b&gt;&lt;br&gt;VMs: &lt;b&gt;8&lt;/b&gt; | 🟢 7 | 🔴 1&lt;br&gt;roles: UTIL 3 | DB 2 | APP 1 | NET 1&lt;/div&gt;" style="rounded=1;whiteSpace=wrap;html=1;align=left;verticalAlign=top;fontSize=12;fontStyle=1;fontFamily=Inter;fillColor=#F8FAFC;strokeColor=#CBD5E1;fontColor=#0F172A;spacingTop=8;spacingLeft=10;arcSize=8;" vertex="1" parent="1">
<mxGeometry x="577" y="271" width="501" height="362" as="geometry" />
</mxCell>
<mxCell id="vm_51e9cf34" value="&lt;div style=&quot;line-height:1.3;&quot;&gt;&lt;span style=&quot;font-size:13px;&quot;&gt;&lt;b&gt;ai-stage&lt;/b&gt;&lt;/span&gt;&lt;br&gt;🟢 &lt;b&gt;RUNNING&lt;/b&gt; | 10.0.50.81&lt;/div&gt;" style="rounded=1;whiteSpace=wrap;html=1;align=left;verticalAlign=top;fontSize=11;fontFamily=Inter;fontColor=#0B1F14;fillColor=#ECFDF3;strokeColor=#86EFAC;spacingTop=10;spacingLeft=10;arcSize=6;strokeWidth=1.2;" vertex="1" parent="1">
<mxGeometry x="585.0" y="317" width="241.5" height="66" as="geometry" />
</mxCell>
<mxCell id="role_db4a2911" value="🧰" style="shape=ellipse;whiteSpace=wrap;html=1;align=center;verticalAlign=middle;fontSize=9;fontStyle=1;fontFamily=Inter;strokeWidth=0;fillColor=#475569;strokeColor=#475569;fontColor=#FFFFFF;" vertex="1" parent="1">
<mxGeometry x="800.5" y="322" width="18" height="18" as="geometry" />
</mxCell>
<mxCell id="vm_0653b7c2" value="&lt;div style=&quot;line-height:1.3;&quot;&gt;&lt;span style=&quot;font-size:13px;&quot;&gt;&lt;b&gt;airstrip&lt;/b&gt;&lt;/span&gt;&lt;br&gt;🟢 &lt;b&gt;RUNNING&lt;/b&gt; | 10.0.50.47&lt;/div&gt;" style="rounded=1;whiteSpace=wrap;html=1;align=left;verticalAlign=top;fontSize=11;fontFamily=Inter;fontColor=#0B1F14;fillColor=#ECFDF3;strokeColor=#86EFAC;spacingTop=10;spacingLeft=10;arcSize=6;strokeWidth=1.2;" vertex="1" parent="1">
<mxGeometry x="836.5" y="317" width="241.5" height="66" as="geometry" />
</mxCell>
<mxCell id="role_2bc5c5d9" value="🧰" style="shape=ellipse;whiteSpace=wrap;html=1;align=center;verticalAlign=middle;fontSize=9;fontStyle=1;fontFamily=Inter;strokeWidth=0;fillColor=#475569;strokeColor=#475569;fontColor=#FFFFFF;" vertex="1" parent="1">
<mxGeometry x="1052.0" y="322" width="18" height="18" as="geometry" />
</mxCell>
<mxCell id="vm_c74087e6" value="&lt;div style=&quot;line-height:1.3;&quot;&gt;&lt;span style=&quot;font-size:13px;&quot;&gt;&lt;b&gt;cassandra-int1&lt;/b&gt;&lt;/span&gt;&lt;br&gt;🟢 &lt;b&gt;RUNNING&lt;/b&gt; | 10.0.50.225&lt;/div&gt;" style="rounded=1;whiteSpace=wrap;html=1;align=left;verticalAlign=top;fontSize=11;fontFamily=Inter;fontColor=#0B1F14;fillColor=#ECFDF3;strokeColor=#86EFAC;spacingTop=10;spacingLeft=10;arcSize=6;strokeWidth=1.2;" vertex="1" parent="1">
<mxGeometry x="585.0" y="391" width="241.5" height="66" as="geometry" />
</mxCell>
<mxCell id="role_91237b5f" value="🗄" style="shape=ellipse;whiteSpace=wrap;html=1;align=center;verticalAlign=middle;fontSize=9;fontStyle=1;fontFamily=Inter;strokeWidth=0;fillColor=#7C3AED;strokeColor=#7C3AED;fontColor=#FFFFFF;" vertex="1" parent="1">
<mxGeometry x="800.5" y="396" width="18" height="18" as="geometry" />
</mxCell>
<mxCell id="vm_01514c80" value="&lt;div style=&quot;line-height:1.3;&quot;&gt;&lt;span style=&quot;font-size:13px;&quot;&gt;&lt;b&gt;homarr&lt;/b&gt;&lt;/span&gt;&lt;br&gt;🟢 &lt;b&gt;RUNNING&lt;/b&gt; | 10.0.50.41&lt;/div&gt;" style="rounded=1;whiteSpace=wrap;html=1;align=left;verticalAlign=top;fontSize=11;fontFamily=Inter;fontColor=#0B1F14;fillColor=#ECFDF3;strokeColor=#86EFAC;spacingTop=10;spacingLeft=10;arcSize=6;strokeWidth=1.2;" vertex="1" parent="1">
<mxGeometry x="836.5" y="391" width="241.5" height="66" as="geometry" />
</mxCell>
<mxCell id="role_220837a2" value="🧩" style="shape=ellipse;whiteSpace=wrap;html=1;align=center;verticalAlign=middle;fontSize=9;fontStyle=1;fontFamily=Inter;strokeWidth=0;fillColor=#166534;strokeColor=#166534;fontColor=#FFFFFF;" vertex="1" parent="1">
<mxGeometry x="1052.0" y="396" width="18" height="18" as="geometry" />
</mxCell>
<mxCell id="vm_1663767c" value="&lt;div style=&quot;line-height:1.3;&quot;&gt;&lt;span style=&quot;font-size:13px;&quot;&gt;&lt;b&gt;infisical&lt;/b&gt;&lt;/span&gt;&lt;br&gt;🟢 &lt;b&gt;RUNNING&lt;/b&gt; | 10.0.50.52&lt;/div&gt;" style="rounded=1;whiteSpace=wrap;html=1;align=left;verticalAlign=top;fontSize=11;fontFamily=Inter;fontColor=#0B1F14;fillColor=#ECFDF3;strokeColor=#86EFAC;spacingTop=10;spacingLeft=10;arcSize=6;strokeWidth=1.2;" vertex="1" parent="1">
<mxGeometry x="585.0" y="465" width="241.5" height="66" as="geometry" />
</mxCell>
<mxCell id="role_ef5330ae" value="🔐" style="shape=ellipse;whiteSpace=wrap;html=1;align=center;verticalAlign=middle;fontSize=9;fontStyle=1;fontFamily=Inter;strokeWidth=0;fillColor=#BE123C;strokeColor=#BE123C;fontColor=#FFFFFF;" vertex="1" parent="1">
<mxGeometry x="800.5" y="470" width="18" height="18" as="geometry" />
</mxCell>
<mxCell id="vm_63cf2e09" value="&lt;div style=&quot;line-height:1.3;&quot;&gt;&lt;span style=&quot;font-size:13px;&quot;&gt;&lt;b&gt;pg-backend&lt;/b&gt;&lt;/span&gt;&lt;br&gt;🟢 &lt;b&gt;RUNNING&lt;/b&gt; | 10.0.50.44&lt;/div&gt;" style="rounded=1;whiteSpace=wrap;html=1;align=left;verticalAlign=top;fontSize=11;fontFamily=Inter;fontColor=#0B1F14;fillColor=#ECFDF3;strokeColor=#86EFAC;spacingTop=10;spacingLeft=10;arcSize=6;strokeWidth=1.2;" vertex="1" parent="1">
<mxGeometry x="836.5" y="465" width="241.5" height="66" as="geometry" />
</mxCell>
<mxCell id="role_aa82f654" value="🗄" style="shape=ellipse;whiteSpace=wrap;html=1;align=center;verticalAlign=middle;fontSize=9;fontStyle=1;fontFamily=Inter;strokeWidth=0;fillColor=#7C3AED;strokeColor=#7C3AED;fontColor=#FFFFFF;" vertex="1" parent="1">
<mxGeometry x="1052.0" y="470" width="18" height="18" as="geometry" />
</mxCell>
<mxCell id="vm_2c43aca5" value="&lt;div style=&quot;line-height:1.3;&quot;&gt;&lt;span style=&quot;font-size:13px;&quot;&gt;&lt;b&gt;tester&lt;/b&gt;&lt;/span&gt;&lt;br&gt;🟢 &lt;b&gt;RUNNING&lt;/b&gt; | 10.0.50.46&lt;/div&gt;" style="rounded=1;whiteSpace=wrap;html=1;align=left;verticalAlign=top;fontSize=11;fontFamily=Inter;fontColor=#0B1F14;fillColor=#ECFDF3;strokeColor=#86EFAC;spacingTop=10;spacingLeft=10;arcSize=6;strokeWidth=1.2;" vertex="1" parent="1">
<mxGeometry x="585.0" y="539" width="241.5" height="66" as="geometry" />
</mxCell>
<mxCell id="role_297a4c04" value="🧰" style="shape=ellipse;whiteSpace=wrap;html=1;align=center;verticalAlign=middle;fontSize=9;fontStyle=1;fontFamily=Inter;strokeWidth=0;fillColor=#475569;strokeColor=#475569;fontColor=#FFFFFF;" vertex="1" parent="1">
<mxGeometry x="800.5" y="544" width="18" height="18" as="geometry" />
</mxCell>
<mxCell id="vm_80fb5b1c" value="&lt;div style=&quot;line-height:1.3;&quot;&gt;&lt;span style=&quot;font-size:13px;&quot;&gt;&lt;b&gt;wacli&lt;/b&gt;&lt;/span&gt;&lt;br&gt;🔴 &lt;b&gt;STOPPED&lt;/b&gt; | n/a&lt;/div&gt;" style="rounded=1;whiteSpace=wrap;html=1;align=left;verticalAlign=top;fontSize=11;fontFamily=Inter;fontColor=#3A1414;fillColor=#FEF2F2;strokeColor=#FCA5A5;spacingTop=10;spacingLeft=10;arcSize=6;strokeWidth=1.2;" vertex="1" parent="1">
<mxGeometry x="836.5" y="539" width="241.5" height="66" as="geometry" />
</mxCell>
<mxCell id="role_533064a1" value="🌐" style="shape=ellipse;whiteSpace=wrap;html=1;align=center;verticalAlign=middle;fontSize=9;fontStyle=1;fontFamily=Inter;strokeWidth=0;fillColor=#1D4ED8;strokeColor=#1D4ED8;fontColor=#FFFFFF;" vertex="1" parent="1">
<mxGeometry x="1052.0" y="544" width="18" height="18" as="geometry" />
</mxCell>
<mxCell id="remote_030ae67c" value="&lt;div style=&quot;line-height:1.35;&quot;&gt;&lt;span style=&quot;font-size:17px;&quot;&gt;&lt;b&gt;hetzner-incus2&lt;/b&gt;&lt;/span&gt;&lt;br&gt;🖥 &lt;b&gt;incus2&lt;/b&gt; | cluster: &lt;b&gt;false&lt;/b&gt;&lt;br&gt;🌍 138.201.135.107:8443&lt;br&gt;📦 nodes: &lt;b&gt;1&lt;/b&gt; | vms: &lt;b&gt;6&lt;/b&gt;&lt;/div&gt;" style="rounded=1;whiteSpace=wrap;html=1;align=left;verticalAlign=top;fontSize=13;fontStyle=1;fontFamily=Inter;spacingTop=12;spacingLeft=14;arcSize=10;fillColor=#FFF4E8;strokeColor=#E38B1A;fontColor=#7A3E00;" vertex="1" parent="1">
<mxGeometry x="1110" y="165" width="525" height="414" as="geometry" />
</mxCell>
<mxCell id="node_d2406a6c" value="&lt;div style=&quot;line-height:1.35;&quot;&gt;&lt;b&gt;Node: incus2&lt;/b&gt;&lt;br&gt;VMs: &lt;b&gt;6&lt;/b&gt; | 🟢 5 | 🔴 1&lt;br&gt;roles: APP 3 | CI 1 | DB 1 | UTIL 1&lt;/div&gt;" style="rounded=1;whiteSpace=wrap;html=1;align=left;verticalAlign=top;fontSize=12;fontStyle=1;fontFamily=Inter;fillColor=#F8FAFC;strokeColor=#CBD5E1;fontColor=#0F172A;spacingTop=8;spacingLeft=10;arcSize=8;" vertex="1" parent="1">
<mxGeometry x="1122" y="271" width="501" height="288" as="geometry" />
</mxCell>
<mxCell id="vm_f5560c22" value="&lt;div style=&quot;line-height:1.3;&quot;&gt;&lt;span style=&quot;font-size:13px;&quot;&gt;&lt;b&gt;arizephoenix&lt;/b&gt;&lt;/span&gt;&lt;br&gt;🟢 &lt;b&gt;RUNNING&lt;/b&gt; | 10.236.251.83&lt;/div&gt;" style="rounded=1;whiteSpace=wrap;html=1;align=left;verticalAlign=top;fontSize=11;fontFamily=Inter;fontColor=#0B1F14;fillColor=#ECFDF3;strokeColor=#86EFAC;spacingTop=10;spacingLeft=10;arcSize=6;strokeWidth=1.2;" vertex="1" parent="1">
<mxGeometry x="1130.0" y="317" width="241.5" height="66" as="geometry" />
</mxCell>
<mxCell id="role_2be5a68b" value="🧩" style="shape=ellipse;whiteSpace=wrap;html=1;align=center;verticalAlign=middle;fontSize=9;fontStyle=1;fontFamily=Inter;strokeWidth=0;fillColor=#166534;strokeColor=#166534;fontColor=#FFFFFF;" vertex="1" parent="1">
<mxGeometry x="1345.5" y="322" width="18" height="18" as="geometry" />
</mxCell>
<mxCell id="vm_a7cd2d61" value="&lt;div style=&quot;line-height:1.3;&quot;&gt;&lt;span style=&quot;font-size:13px;&quot;&gt;&lt;b&gt;customer-portal-new&lt;/b&gt;&lt;/span&gt;&lt;br&gt;🟢 &lt;b&gt;RUNNING&lt;/b&gt; | 10.236.251.84&lt;/div&gt;" style="rounded=1;whiteSpace=wrap;html=1;align=left;verticalAlign=top;fontSize=11;fontFamily=Inter;fontColor=#0B1F14;fillColor=#ECFDF3;strokeColor=#86EFAC;spacingTop=10;spacingLeft=10;arcSize=6;strokeWidth=1.2;" vertex="1" parent="1">
<mxGeometry x="1381.5" y="317" width="241.5" height="66" as="geometry" />
</mxCell>
<mxCell id="role_c8d1c9c0" value="🧩" style="shape=ellipse;whiteSpace=wrap;html=1;align=center;verticalAlign=middle;fontSize=9;fontStyle=1;fontFamily=Inter;strokeWidth=0;fillColor=#166534;strokeColor=#166534;fontColor=#FFFFFF;" vertex="1" parent="1">
<mxGeometry x="1597.0" y="322" width="18" height="18" as="geometry" />
</mxCell>
<mxCell id="vm_a25f64db" value="&lt;div style=&quot;line-height:1.3;&quot;&gt;&lt;span style=&quot;font-size:13px;&quot;&gt;&lt;b&gt;jenkins&lt;/b&gt;&lt;/span&gt;&lt;br&gt;🟢 &lt;b&gt;RUNNING&lt;/b&gt; | 10.236.251.80&lt;/div&gt;" style="rounded=1;whiteSpace=wrap;html=1;align=left;verticalAlign=top;fontSize=11;fontFamily=Inter;fontColor=#0B1F14;fillColor=#ECFDF3;strokeColor=#86EFAC;spacingTop=10;spacingLeft=10;arcSize=6;strokeWidth=1.2;" vertex="1" parent="1">
<mxGeometry x="1130.0" y="391" width="241.5" height="66" as="geometry" />
</mxCell>
<mxCell id="role_7ffa29fd" value="⚙" style="shape=ellipse;whiteSpace=wrap;html=1;align=center;verticalAlign=middle;fontSize=9;fontStyle=1;fontFamily=Inter;strokeWidth=0;fillColor=#B45309;strokeColor=#B45309;fontColor=#FFFFFF;" vertex="1" parent="1">
<mxGeometry x="1345.5" y="396" width="18" height="18" as="geometry" />
</mxCell>
<mxCell id="vm_65fa0b8d" value="&lt;div style=&quot;line-height:1.3;&quot;&gt;&lt;span style=&quot;font-size:13px;&quot;&gt;&lt;b&gt;showcase&lt;/b&gt;&lt;/span&gt;&lt;br&gt;🟢 &lt;b&gt;RUNNING&lt;/b&gt; | 10.236.251.81&lt;/div&gt;" style="rounded=1;whiteSpace=wrap;html=1;align=left;verticalAlign=top;fontSize=11;fontFamily=Inter;fontColor=#0B1F14;fillColor=#ECFDF3;strokeColor=#86EFAC;spacingTop=10;spacingLeft=10;arcSize=6;strokeWidth=1.2;" vertex="1" parent="1">
<mxGeometry x="1381.5" y="391" width="241.5" height="66" as="geometry" />
</mxCell>
<mxCell id="role_30116c77" value="🧩" style="shape=ellipse;whiteSpace=wrap;html=1;align=center;verticalAlign=middle;fontSize=9;fontStyle=1;fontFamily=Inter;strokeWidth=0;fillColor=#166534;strokeColor=#166534;fontColor=#FFFFFF;" vertex="1" parent="1">
<mxGeometry x="1597.0" y="396" width="18" height="18" as="geometry" />
</mxCell>
<mxCell id="vm_160b3a58" value="&lt;div style=&quot;line-height:1.3;&quot;&gt;&lt;span style=&quot;font-size:13px;&quot;&gt;&lt;b&gt;supabase&lt;/b&gt;&lt;/span&gt;&lt;br&gt;🟢 &lt;b&gt;RUNNING&lt;/b&gt; | 10.236.251.82&lt;/div&gt;" style="rounded=1;whiteSpace=wrap;html=1;align=left;verticalAlign=top;fontSize=11;fontFamily=Inter;fontColor=#0B1F14;fillColor=#ECFDF3;strokeColor=#86EFAC;spacingTop=10;spacingLeft=10;arcSize=6;strokeWidth=1.2;" vertex="1" parent="1">
<mxGeometry x="1130.0" y="465" width="241.5" height="66" as="geometry" />
</mxCell>
<mxCell id="role_e408ef95" value="🗄" style="shape=ellipse;whiteSpace=wrap;html=1;align=center;verticalAlign=middle;fontSize=9;fontStyle=1;fontFamily=Inter;strokeWidth=0;fillColor=#7C3AED;strokeColor=#7C3AED;fontColor=#FFFFFF;" vertex="1" parent="1">
<mxGeometry x="1345.5" y="470" width="18" height="18" as="geometry" />
</mxCell>
<mxCell id="vm_d95eb58d" value="&lt;div style=&quot;line-height:1.3;&quot;&gt;&lt;span style=&quot;font-size:13px;&quot;&gt;&lt;b&gt;testing-prtiv1&lt;/b&gt;&lt;/span&gt;&lt;br&gt;🔴 &lt;b&gt;STOPPED&lt;/b&gt; | n/a&lt;/div&gt;" style="rounded=1;whiteSpace=wrap;html=1;align=left;verticalAlign=top;fontSize=11;fontFamily=Inter;fontColor=#3A1414;fillColor=#FEF2F2;strokeColor=#FCA5A5;spacingTop=10;spacingLeft=10;arcSize=6;strokeWidth=1.2;" vertex="1" parent="1">
<mxGeometry x="1381.5" y="465" width="241.5" height="66" as="geometry" />
</mxCell>
<mxCell id="role_4f4cdbce" value="🧰" style="shape=ellipse;whiteSpace=wrap;html=1;align=center;verticalAlign=middle;fontSize=9;fontStyle=1;fontFamily=Inter;strokeWidth=0;fillColor=#475569;strokeColor=#475569;fontColor=#FFFFFF;" vertex="1" parent="1">
<mxGeometry x="1597.0" y="470" width="18" height="18" as="geometry" />
</mxCell>
<mxCell id="legend_title_183fc7ff" value="Legend" style="rounded=1;whiteSpace=wrap;html=1;align=left;verticalAlign=middle;fontSize=12;fontStyle=1;fontFamily=Inter;fillColor=#FFFFFF;strokeColor=#CBD5E1;fontColor=#0F172A;arcSize=8;" vertex="1" parent="1">
<mxGeometry x="20" y="673" width="140" height="36" as="geometry" />
</mxCell>
<mxCell id="lg1_8ae5f895" value="Running VM" style="rounded=1;whiteSpace=wrap;html=1;align=left;verticalAlign=top;fontSize=11;fontFamily=Inter;fontColor=#0B1F14;fillColor=#ECFDF3;strokeColor=#86EFAC;spacingTop=10;spacingLeft=10;arcSize=6;" vertex="1" parent="1">
<mxGeometry x="170" y="673" width="180" height="36" as="geometry" />
</mxCell>
<mxCell id="lg2_c3313f36" value="Stopped VM" style="rounded=1;whiteSpace=wrap;html=1;align=left;verticalAlign=top;fontSize=11;fontFamily=Inter;fontColor=#3A1414;fillColor=#FEF2F2;strokeColor=#FCA5A5;spacingTop=10;spacingLeft=10;arcSize=6;" vertex="1" parent="1">
<mxGeometry x="360" y="673" width="180" height="36" as="geometry" />
</mxCell>
<mxCell id="lg5_5bb5dd79" value="Role badges: 🌐 🗄 ⚙ 📈 🔐 🧰 🧩" style="rounded=1;whiteSpace=wrap;html=1;align=left;verticalAlign=middle;fontSize=12;fontStyle=1;fontFamily=Inter;fillColor=#FFFFFF;strokeColor=#CBD5E1;fontColor=#0F172A;arcSize=8;" vertex="1" parent="1">
<mxGeometry x="550" y="673" width="340" height="36" as="geometry" />
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

+105
View File
@@ -0,0 +1,105 @@
# Incus Topology Map
Generated: 2026-04-09 11:54:22 CEST
## Summary
- **hetzner-incus2** (https://138.201.135.107:8443) -> server: **incus2**, clustered: **false**, instances: **6**
- **kuber** (https://10.0.50.79:8443) -> server: **kuber**, clustered: **false**, instances: **8**
- **local** (unix://) -> server: **lenovo**, clustered: **false**, instances: **0**
- **virgo-incus** (https://10.0.50.9:8443) -> server: **virgo**, clustered: **false**, instances: **8**
## Visual Map
```mermaid
graph TD
ROOT[Incus Access]
ROOT --> R_hetznerincus2["remote: hetzner-incus2"]
R_hetznerincus2 --> N_hetznerincus2_incus2["node: incus2"]
N_hetznerincus2_incus2 --> V_hetznerincus2_incus2_defaultarizephoenix["default/arizephoenix (Running)\n10.236.251.83"]
N_hetznerincus2_incus2 --> V_hetznerincus2_incus2_defaultcustomerportalnew["default/customer-portal-new (Running)\n10.236.251.84"]
N_hetznerincus2_incus2 --> V_hetznerincus2_incus2_defaultjenkins["default/jenkins (Running)\n10.236.251.80"]
N_hetznerincus2_incus2 --> V_hetznerincus2_incus2_defaultshowcase["default/showcase (Running)\n10.236.251.81"]
N_hetznerincus2_incus2 --> V_hetznerincus2_incus2_defaultsupabase["default/supabase (Running)\n10.236.251.82"]
N_hetznerincus2_incus2 --> V_hetznerincus2_incus2_defaulttestingprtiv1["default/testing-prtiv1 (Stopped)\nn/a"]
ROOT --> R_kuber["remote: kuber"]
R_kuber --> N_kuber_kuber["node: kuber"]
N_kuber_kuber --> V_kuber_kuber_defaultcassandra["default/cassandra (Running)\n10.0.50.164"]
N_kuber_kuber --> V_kuber_kuber_defaultfileserver["default/fileserver (Stopped)\nn/a"]
N_kuber_kuber --> V_kuber_kuber_defaultharness["default/harness (Stopped)\nn/a"]
N_kuber_kuber --> V_kuber_kuber_defaultlxconsole["default/lxconsole (Running)\n10.0.50.173"]
N_kuber_kuber --> V_kuber_kuber_defaultshellhub["default/shellhub (Stopped)\nn/a"]
N_kuber_kuber --> V_kuber_kuber_defaulttestvm["default/testvm (Running)\n10.0.50.45"]
N_kuber_kuber --> V_kuber_kuber_defaultubuntu22plcnextconn["default/ubuntu-22-plcnext-conn (Running)\n10.0.50.43"]
N_kuber_kuber --> V_kuber_kuber_defaultubuntu20["default/ubuntu20 (Running)\n10.0.50.181"]
ROOT --> R_local["remote: local"]
R_local --> N_local_lenovo["node: lenovo"]
N_local_lenovo --> E_local_lenovo["(no VMs)"]
ROOT --> R_virgoincus["remote: virgo-incus"]
R_virgoincus --> N_virgoincus_virgo["node: virgo"]
N_virgoincus_virgo --> V_virgoincus_virgo_defaultaistage["default/ai-stage (Running)\n10.0.50.81"]
N_virgoincus_virgo --> V_virgoincus_virgo_defaultairstrip["default/airstrip (Running)\n10.0.50.47"]
N_virgoincus_virgo --> V_virgoincus_virgo_defaultcassandraint1["default/cassandra-int1 (Running)\n10.0.50.225"]
N_virgoincus_virgo --> V_virgoincus_virgo_defaulthomarr["default/homarr (Running)\n10.0.50.41"]
N_virgoincus_virgo --> V_virgoincus_virgo_defaultinfisical["default/infisical (Running)\n10.0.50.52"]
N_virgoincus_virgo --> V_virgoincus_virgo_defaultpgbackend["default/pg-backend (Running)\n10.0.50.44"]
N_virgoincus_virgo --> V_virgoincus_virgo_defaulttester["default/tester (Running)\n10.0.50.46"]
N_virgoincus_virgo --> V_virgoincus_virgo_defaultwacli["default/wacli (Stopped)\nn/a"]
```
## Tree View (Remote -> Node -> VM)
### Remote: hetzner-incus2
- Endpoint: https://138.201.135.107:8443
- Server name: incus2
- Clustered: false
- Projects: default
- Nodes:
- incus2
- default/arizephoenix [virtual-machine] status=Running primary_ip=10.236.251.83 all_ipv4=10.236.251.83, 172.17.0.1, 172.18.0.1
- default/customer-portal-new [virtual-machine] status=Running primary_ip=10.236.251.84 all_ipv4=10.236.251.84, 172.17.0.1, 172.18.0.1
- default/jenkins [virtual-machine] status=Running primary_ip=10.236.251.80 all_ipv4=10.236.251.80
- default/showcase [virtual-machine] status=Running primary_ip=10.236.251.81 all_ipv4=10.236.251.81, 172.17.0.1, 172.18.0.1, 172.19.0.1, 172.20.0.1, 172.21.0.1
- default/supabase [virtual-machine] status=Running primary_ip=10.236.251.82 all_ipv4=10.236.251.82, 172.17.0.1, 172.18.0.1
- default/testing-prtiv1 [virtual-machine] status=Stopped primary_ip=n/a all_ipv4=n/a
### Remote: kuber
- Endpoint: https://10.0.50.79:8443
- Server name: kuber
- Clustered: false
- Projects: default
- Nodes:
- kuber
- default/cassandra [virtual-machine] status=Running primary_ip=10.0.50.164 all_ipv4=10.0.50.164
- default/fileserver [virtual-machine] status=Stopped primary_ip=n/a all_ipv4=n/a
- default/harness [virtual-machine] status=Stopped primary_ip=n/a all_ipv4=n/a
- default/lxconsole [virtual-machine] status=Running primary_ip=10.0.50.173 all_ipv4=10.0.50.173, 172.17.0.1, 172.18.0.1
- default/shellhub [virtual-machine] status=Stopped primary_ip=n/a all_ipv4=n/a
- default/testvm [virtual-machine] status=Running primary_ip=10.0.50.45 all_ipv4=10.0.50.45, 172.16.0.1, 172.16.0.17, 172.17.0.1
- default/ubuntu-22-plcnext-conn [virtual-machine] status=Running primary_ip=10.0.50.43 all_ipv4=10.0.50.43, 172.17.0.1
- default/ubuntu20 [virtual-machine] status=Running primary_ip=10.0.50.181 all_ipv4=10.0.50.181, 172.17.0.1
### Remote: local
- Endpoint: unix://
- Server name: lenovo
- Clustered: false
- Projects: default
- Nodes:
- lenovo
- (no instances)
### Remote: virgo-incus
- Endpoint: https://10.0.50.9:8443
- Server name: virgo
- Clustered: false
- Projects: default
- Nodes:
- virgo
- default/ai-stage [virtual-machine] status=Running primary_ip=10.0.50.81 all_ipv4=10.0.50.81, 172.17.0.1, 172.18.0.1, 172.19.0.1, 172.20.0.1, 172.21.0.1
- default/airstrip [virtual-machine] status=Running primary_ip=10.0.50.47 all_ipv4=10.0.50.47, 172.17.0.1, 172.18.0.1
- default/cassandra-int1 [virtual-machine] status=Running primary_ip=10.0.50.225 all_ipv4=10.0.50.225, 172.17.0.1
- default/homarr [virtual-machine] status=Running primary_ip=10.0.50.41 all_ipv4=10.0.50.41, 172.17.0.1, 172.18.0.1
- default/infisical [virtual-machine] status=Running primary_ip=10.0.50.52 all_ipv4=10.0.50.52, 172.17.0.1, 172.18.0.1
- default/pg-backend [virtual-machine] status=Running primary_ip=10.0.50.44 all_ipv4=10.0.50.44, 172.17.0.1, 172.18.0.1
- default/tester [virtual-machine] status=Running primary_ip=10.0.50.46 all_ipv4=10.0.50.46
- default/wacli [virtual-machine] status=Stopped primary_ip=n/a all_ipv4=n/a