From 6293f7ab79fd95f8b474100f7f6a28447cd886fb Mon Sep 17 00:00:00 2001 From: Maarten Date: Tue, 24 Feb 2026 17:17:39 +0100 Subject: [PATCH] Redesign mermaid test with consistent style guidelines - Define 5-color semantic palette (Okabe-Ito derived, color-blind safe) - Use classDef for reusable styles instead of per-node statements - Limit node text to 2 lines max, move specs to tables - Consistent node shapes by role (rounded for network, stadium for external) - Dashed arrows for control plane, solid for data plane - Tinted subgraph fills matching dominant role color Co-Authored-By: Claude Opus 4.6 --- notes/mermaid-test.md | 285 ++++++++++++++++++++++++------------------ 1 file changed, 166 insertions(+), 119 deletions(-) diff --git a/notes/mermaid-test.md b/notes/mermaid-test.md index 8dbebb6..a9e1808 100644 --- a/notes/mermaid-test.md +++ b/notes/mermaid-test.md @@ -5,136 +5,160 @@ Delete this file after confirming. --- -## Test 1: HAProxy Architecture (flowchart, top-down) +## Style Guidelines -Original: `notes/haproxy-guide.md` lines 13-44 +All diagrams in this project follow a consistent style based on semantic +color-coding by element role, color-blind-safe colors (Okabe-Ito derived), +and minimal text per node. + +**Color palette** (5 semantic colors + 1 neutral): + +| Role | Color | Hex | Node shape | +|------|-------|-----|------------| +| Cluster node / VM | Teal | `#009E73` | Rectangle `["..."]` | +| Instance / container | Sky blue | `#56B4E9` | Rectangle `["..."]` | +| Network infrastructure | Blue | `#0072B2` | Rounded `("...")` | +| Load balancer / proxy | Amber | `#E69F00` | Rectangle `["..."]` | +| Management / control | Mauve | `#CC79A7` | Rectangle `["..."]` | +| External / entry point | Light gray | `#f5f5f5` | Stadium `(["..."])` | + +**Subgraph fills**: light tint of the dominant role color, darker stroke. + +**Rules**: +- Max 2 lines of text per node (name + one detail). Move specs to tables. +- Top-down (`TD`) for hierarchies; left-right (`LR`) for lateral/peer relationships. +- Solid arrows (`-->`) for data/traffic; dashed (`-.->`) for control/management. +- Use `classDef` for styling, never per-node `style` statements. +- No emojis or unicode symbols in labels (renderer compatibility). +- Edge labels: 1-3 words max, only when relationship isn't obvious. + +--- + +## Test 1: HAProxy Architecture + +Original: `notes/haproxy-guide.md` ```mermaid flowchart TD - client["Client / LAN Traffic"] - vip["UPLINK VIP Address\n192.168.103.200"] - ovnlb["OVN Load Balancer\n(distributes to HA pair\nfor failover)"] - ha1["HAProxy 01\n10.10.10.50\n(container)"] - ha2["HAProxy 02\n10.10.10.51\n(container)"] - ng1["nginx-01\n.60 :80"] - ng2["nginx-02\n.61 :80"] - ng3["nginx-03\n.62 :80"] + client(["Client / LAN"]) + vip["VIP 192.168.103.200"] + ovnlb("OVN Load Balancer") + ha1["HAProxy 01\n10.10.10.50"] + ha2["HAProxy 02\n10.10.10.51"] + ng1["nginx-01 · .60"] + ng2["nginx-02 · .61"] + ng3["nginx-03 · .62"] - client --> vip --> ovnlb - ovnlb --> ha1 - ovnlb --> ha2 - ha1 --> ng1 - ha1 --> ng2 - ha1 --> ng3 - ha2 --> ng1 - ha2 --> ng2 - ha2 --> ng3 + client --> vip + vip --> ovnlb + ovnlb --> ha1 & ha2 + ha1 & ha2 --> ng1 & ng2 & ng3 - style vip fill:#4a90d9,color:#fff - style ovnlb fill:#7b68ee,color:#fff - style ha1 fill:#2ecc71,color:#fff - style ha2 fill:#2ecc71,color:#fff - style ng1 fill:#e67e22,color:#fff - style ng2 fill:#e67e22,color:#fff - style ng3 fill:#e67e22,color:#fff + classDef external fill:#f5f5f5,color:#333,stroke:#999 + classDef network fill:#0072B2,color:#fff,stroke:#005a8e + classDef lb fill:#E69F00,color:#fff,stroke:#b87d00 + classDef instance fill:#56B4E9,color:#fff,stroke:#3a8fbf + + class client external + class vip,ovnlb network + class ha1,ha2 lb + class ng1,ng2,ng3 instance ``` --- -## Test 2: Bridge Topology (subgraphs for nodes) +## Test 2: Bridge Topology -Original: `notes/networking-guide.md` lines 81-100 +Original: `notes/networking-guide.md` ```mermaid flowchart TD - subgraph node1["Node 1 (net-node-01)"] - br1["incusbr0\n10.0.0.1/24"] - c1["c1\n.202"] - c2["c2\n.40"] - nat1["NAT → ens18\n192.168.1.209"] - c1 --- br1 - c2 --- br1 - br1 --> nat1 + subgraph node1["Node 1 · net-node-01"] + c1["c1 · .202"] & c2["c2 · .40"] --- br1("incusbr0\n10.0.0.1/24") + br1 --> nat1["NAT · 192.168.1.209"] end - subgraph node2["Node 2 (net-node-02)"] - br2["incusbr0\n10.0.0.1/24"] - c3["c3\n.52"] - nat2["NAT → ens18\n192.168.1.150"] - c3 --- br2 - br2 --> nat2 + subgraph node2["Node 2 · net-node-02"] + c3["c3 · .52"] --- br2("incusbr0\n10.0.0.1/24") + br2 --> nat2["NAT · 192.168.1.150"] end - nat1 --- lan - nat2 --- lan - lan(("LAN\n192.168.1.0/24")) + nat1 & nat2 --- lan(("LAN\n192.168.1.0/24")) - node1 ~~~ node2 + classDef instance fill:#56B4E9,color:#fff,stroke:#3a8fbf + classDef network fill:#0072B2,color:#fff,stroke:#005a8e + classDef node fill:#009E73,color:#fff,stroke:#007a5e - linkStyle 5 stroke:red,stroke-dasharray:5 - linkStyle 6 stroke:red,stroke-dasharray:5 + class c1,c2,c3 instance + class br1,br2,lan network + class nat1,nat2 node + + style node1 fill:#e6f5f0,stroke:#009E73 + style node2 fill:#e6f5f0,stroke:#009E73 ``` -> **Key point:** Each node has its own bridge with the same subnet. The -> bridges are NOT connected to each other. +Each node has its own bridge with the same subnet (10.0.0.1/24). +The bridges are **not** connected to each other — cross-node traffic fails. --- -## Test 3: OVN Topology (control plane + nodes + tunnels) +## Test 3: OVN Topology -Original: `notes/networking-guide.md` lines 196-227 +Original: `notes/networking-guide.md` ```mermaid flowchart TD subgraph cp["OVN Control Plane"] - ovnc["ovn-central\novn-northd + ovsdb-server\nNB: tcp:192.168.1.209:6641\nSB: tcp:192.168.1.209:6642"] + ovnc["ovn-central\nNB :6641 · SB :6642"] end - subgraph n1["Node 1 (net-node-01)"] - ctrl1["ovn-controller\novs-vswitchd"] - oc1["c1 (.2)"] - oc2["c2 (.3)"] - ls1["OVN logical switch\n10.10.10.0/24"] - oc1 --- ls1 - oc2 --- ls1 - ctrl1 ~~~ ls1 + subgraph n1["Node 1 · net-node-01"] + ctrl1["ovn-controller"] ~~~ ls1 + c1["c1 · .2"] & c2["c2 · .3"] --- ls1("logical switch\n10.10.10.0/24") end - subgraph n2["Node 2 (net-node-02)"] - ctrl2["ovn-controller\novs-vswitchd"] - oc3["c3 (.4)"] - ls2["OVN logical switch\n10.10.10.0/24"] - oc3 --- ls2 - ctrl2 ~~~ ls2 + subgraph n2["Node 2 · net-node-02"] + ctrl2["ovn-controller"] ~~~ ls2 + c3["c3 · .4"] --- ls2("logical switch\n10.10.10.0/24") end - subgraph n3["Node 3 (net-node-03)"] - ctrl3["ovn-controller\novs-vswitchd"] - oc4["c4 (.5)"] - ls3["OVN logical switch\n10.10.10.0/24"] - oc4 --- ls3 - ctrl3 ~~~ ls3 + subgraph n3["Node 3 · net-node-03"] + ctrl3["ovn-controller"] ~~~ ls3 + c4["c4 · .5"] --- ls3("logical switch\n10.10.10.0/24") end - ovnc -->|proxy devices| ctrl1 - ovnc -->|proxy devices| ctrl2 - ovnc -->|proxy devices| ctrl3 + ovnc -.-> ctrl1 & ctrl2 & ctrl3 - ls1 <-->|Geneve tunnel\n192.168.1.209| ls2 - ls2 <-->|Geneve tunnel\n192.168.1.150| ls3 - ls1 <-->|Geneve tunnel\n192.168.1.13| ls3 + ls1 <-->|Geneve| ls2 + ls2 <-->|Geneve| ls3 + ls1 <-->|Geneve| ls3 - lan(("LAN\n192.168.1.0/24")) - n1 --- lan - n2 --- lan - n3 --- lan + n1 & n2 & n3 --- lan(("LAN\n192.168.1.0/24")) + + classDef mgmt fill:#CC79A7,color:#fff,stroke:#a36088 + classDef instance fill:#56B4E9,color:#fff,stroke:#3a8fbf + classDef network fill:#0072B2,color:#fff,stroke:#005a8e + classDef node fill:#009E73,color:#fff,stroke:#007a5e + + class ovnc mgmt + class ctrl1,ctrl2,ctrl3 node + class c1,c2,c3,c4 instance + class ls1,ls2,ls3,lan network + + style cp fill:#f5e6f0,stroke:#CC79A7 + style n1 fill:#e6f5f0,stroke:#009E73 + style n2 fill:#e6f5f0,stroke:#009E73 + style n3 fill:#e6f5f0,stroke:#009E73 ``` +All instances share a single logical switch connected via Geneve tunnels. +The control plane (dashed lines) manages the data plane (solid lines). + --- ## Test 4: Geneve Tunnel Mesh -Original: `notes/ovn-deep-dive.md` lines 331-349 +Original: `notes/ovn-deep-dive.md` ```mermaid graph LR @@ -142,69 +166,92 @@ graph LR n2(("oc-node-02\n.141")) n3(("oc-node-03\n.142")) - n1 <-->|"Geneve\nUDP 6081\nBFD ✓"| n2 - n2 <-->|"Geneve\nUDP 6081\nBFD ✓"| n3 - n1 <-->|"Geneve\nUDP 6081\nBFD ✓"| n3 + n1 <-->|"Geneve 6081"| n2 + n2 <-->|"Geneve 6081"| n3 + n1 <-->|"Geneve 6081"| n3 - style n1 fill:#3498db,color:#fff - style n2 fill:#2ecc71,color:#fff - style n3 fill:#e74c3c,color:#fff + classDef chassis fill:#009E73,color:#fff,stroke:#007a5e + + class n1,n2,n3 chassis ``` +Full mesh — every pair has a Geneve tunnel (UDP 6081) with BFD health +monitoring. Tunnel keys are set per-packet from the OVN datapath. + --- -## Test 5: OVS Bridge Architecture (per-node) +## Test 5: OVS Bridge Architecture -Original: `notes/ovn-deep-dive.md` lines 305-313 +Original: `notes/ovn-deep-dive.md` ```mermaid flowchart LR - subgraph provider["incusovn7 (provider bridge)"] - nic["incusovn7 ← physical NIC"] - intport["incusovn7b ← internal port"] - patch1["patch-...-to-br-int"] + subgraph provider["incusovn7 · provider bridge"] + nic["physical NIC"] + intport["internal port"] + patch1["patch to br-int"] end - subgraph integration["br-int (integration bridge)"] - veth["veth* ← instance NICs"] - ovntun["ovn-* ← Geneve tunnels"] - patch2["patch-br-int-to-..."] - brint["br-int ← internal port"] + subgraph integration["br-int · integration bridge"] + veth["instance veth ports"] + ovntun["Geneve tunnels"] + patch2["patch to incusovn7"] end patch1 <-->|"patch port"| patch2 - style provider fill:#e8f4fd,stroke:#3498db - style integration fill:#eafaf1,stroke:#2ecc71 + classDef prov fill:#0072B2,color:#fff,stroke:#005a8e + classDef integ fill:#009E73,color:#fff,stroke:#007a5e + + class nic,intport,patch1 prov + class veth,ovntun,patch2 integ + + style provider fill:#e0eef8,stroke:#0072B2 + style integration fill:#e6f5f0,stroke:#009E73 ``` +Each IncusOS node runs two OVS bridges. The provider bridge is the +on-ramp to the physical network; the integration bridge handles all +OVN logical processing (ACLs, NAT, LB, routing). + --- ## Test 6: Operations Center Architecture -Original: `notes/operations-center-guide.md` lines 12-45 +Original: `notes/operations-center-guide.md` ```mermaid flowchart TD - subgraph proxmox["Proxmox VE Host (pve)\nIntel i9-13900HK · 64 GiB · 20 cores"] - subgraph cluster["Incus Cluster (OVN Geneve · net-prod 10.10.10.0/24)"] - n1["oc-node-01\nVMID 400\n4c/8G/64G\n.140\novn-central"] - n2["oc-node-02\nVMID 401\n4c/8G/50G\n.141"] - n3["oc-node-03\nVMID 402\n4c/8G/50G\n.142"] + subgraph proxmox["Proxmox VE Host · i9-13900HK · 64 GiB"] + subgraph cluster["Incus Cluster · net-prod 10.10.10.0/24"] + n1["oc-node-01\nVMID 400 · .140"] + n2["oc-node-02\nVMID 401 · .141"] + n3["oc-node-03\nVMID 402 · .142"] end - oc["oc-server\nVMID 920 · 2c/4G/50G\n.120\nhttps://192.168.102.120\nWeb UI + CLI + REST API"] + oc["Operations Center\nVMID 920 · .120"] end - vmbr0["vmbr0 (VLAN 69)\n192.168.100.0/22\nGateway: 192.168.100.1"] - ext["OVN External IPs\n192.168.103.200-210\nLB: .201 · Fwd: .202"] + vmbr0(("VLAN 69\n192.168.100.0/22")) + ext["OVN external IPs\n192.168.103.200-210"] proxmox --- vmbr0 - cluster -.->|"OVN external"| ext + cluster -.->|"external gateway"| ext - style proxmox fill:#f5f5f5,stroke:#333 - style cluster fill:#e8f4fd,stroke:#3498db - style oc fill:#fff3cd,stroke:#e67e22 - style n1 fill:#d4edda,stroke:#28a745 - style n2 fill:#d4edda,stroke:#28a745 - style n3 fill:#d4edda,stroke:#28a745 + classDef node fill:#009E73,color:#fff,stroke:#007a5e + classDef mgmt fill:#CC79A7,color:#fff,stroke:#a36088 + classDef network fill:#0072B2,color:#fff,stroke:#005a8e + + class n1,n2,n3 node + class oc mgmt + class vmbr0,ext network + + style proxmox fill:#f5f5f5,stroke:#999 + style cluster fill:#e6f5f0,stroke:#009E73 ``` + +| Component | VMID | IP | Specs | Role | +|-----------|------|----|-------|------| +| oc-node-01 | 400 | .140 | 4c/8G/64G | Cluster init + OVN central | +| oc-node-02 | 401 | .141 | 4c/8G/50G | Cluster member | +| oc-node-03 | 402 | .142 | 4c/8G/50G | Cluster member | +| oc-server | 920 | .120 | 2c/4G/50G | Operations Center |