ネットワークトポロジ図の作成に特化したデスクトップアプリ。 Packet Tracer のように「機器を置いて、ポートを選んでつなぐ」GUI と、 人や AI が書ける YAML 定義を双方向に同期する。
- バージョン: 0.1(MVP 設計)
- 最終更新: 2026-10-30
- draw.io や Visio は汎用作図ツールなので、インターフェース名・ポートの使用状況・VLAN などを手で書き込む必要があり、間違いや更新漏れが起きやすい。
- 図がバイナリや独自形式だと Git で差分管理しづらく、AI に生成・編集させにくい。
- インターフェース中心: 機器は「機種」を持ち、機種に応じたポートが自動で用意される。リンクは必ず「機器:ポート」同士を結ぶ。
- GUI とテキストの双方向同期: GUI で編集すると YAML が更新され、YAML を編集すると図が更新される。
- AI フレンドリー: YAML は接続関係だけ書けば図になる(座標は省略可能、自動レイアウト)。JSON Schema を公開する。
- パケットの流れや ping などのシミュレーション(将来検討)
- 実機へのコンフィグ投入
- 複数人のリアルタイム共同編集
| 項目 | 採用技術 |
|---|---|
| 対象OS | macOS を最初に完成させる。Windows / Linux は後続で対応(同一コードベース) |
| アプリ基盤 | Tauri 2(Rust) |
| フロントエンド | TypeScript(strict)+ React + Vite |
| 図の描画 | React Flow(@xyflow/react) |
| 状態管理 | Zustand(+ Immer) |
| YAML 編集画面 | CodeMirror 6 |
| YAML パース/出力 | yaml(eemeli/yaml。コメント保持のため Document API を使う) |
| 自動レイアウト | elkjs |
| テスト | Vitest |
| 整形・静的解析 | ESLint + Prettier、Rust は rustfmt + clippy |
- 各ライブラリはプロジェクト作成時点の最新安定版を使う。
- Rust 側は最小限にする(ファイル入出力・ダイアログは Tauri 公式プラグイン
fs/dialogを利用)。ロジックは基本的に TypeScript 側に置く。 - OS 固有の処理を書く場合は必ず抽象化し、Windows / Linux 対応を妨げないこと。
- macOS 版は、Apple Silicon・Intel の両方で動くユニバーサル版を、DMG で GitHub Releases に配布する。対応は macOS 13.3 以降。署名は簡易署名(ad-hoc。Apple の Developer ID による署名・公証はしない)。
| 用語 | 意味 |
|---|---|
| トポロジ | 1つの図。1ファイル = 1トポロジ |
| デバイス | 図に置く機器(ルーター、スイッチ、サーバーなど) |
| モデル(機種) | デバイスの種類定義。ポート一覧・役割・アイコンを持つテンプレート |
| インターフェース / ポート | デバイスが持つ接続口。本仕様では同義 |
| エンドポイント | デバイスID:インターフェース名 の形式で表す接続端 |
| リンク | 2つのエンドポイントを結ぶ線 |
| ロール | デバイスの役割(router / l2switch / l3switch / firewall / server / hypervisor / pc / ap / cloud / generic) |
| ハイパーバイザ(ホスト) | VM やコンテナを動かす機器(Proxmox VE のノードなど)。ロールは hypervisor |
| ゲスト | ホストの上で動く VM(仮想マシン)またはコンテナ(Proxmox VE の LXC) |
| ブリッジ | ホストの中の仮想スイッチ(Proxmox VE の vmbr0 など)。ゲストはブリッジにつなぐ |
| bond | 複数の NIC を 1 本に束ねたもの(リンクアグリゲーション) |
| VLAN インターフェース | 1 つの VLAN だけを扱う論理的な IF(vmbr0.10 など) |
| クラスタ | 複数のホストをまとめて管理する単位(Proxmox VE のクラスタ) |
| SDN | クラスタ全体で仮想ネットワークを定義する仕組み(Proxmox VE の SDN。ゾーンと VNet) |
- 拡張子:
*.constella.yaml - 文字コード: UTF-8
- 先頭に形式バージョン
constella: 1を必須とする。
# yaml-language-server: $schema=https://raw.githubusercontent.com/stellarcielo/Constella/main/schema/constella.schema.json
constella: 1
meta:
title: 自宅ラボ DC 区画
description: 学習用ルーティング網
devices:
rt1:
model: generic-router-4
label: Edge Router
core-sw1:
model: cisco-c2960-24tt-l
interfaces:
Gi0/1:
description: to rt1
srv1:
model: generic-server-2
isp:
role: cloud # model 省略 = フレキシブル(任意のIF名を使える)
links:
- a: rt1:Gi0/0
b: isp:wan
- a: rt1:Gi0/1
b: core-sw1:Gi0/1
- a: core-sw1:Fa0/1
b: srv1:eth0
label: mgmt
layout:
rt1: { x: 300, y: 80 }
core-sw1: { x: 300, y: 240 }
# 座標が無いデバイスは自動レイアウトで配置する| フィールド | 必須 | 説明 |
|---|---|---|
model |
任意 | モデルID。省略時はフレキシブルデバイス |
role |
任意 | 省略時はモデルの role。モデルも無い場合は generic |
label |
任意 | 図に表示する名前。省略時はデバイスID |
host |
任意 | ゲストのとき、動いているホストのデバイスID(4.6) |
guest |
任意 | vm / container。省略時はモデルの guest。どちらも無く host があれば vm(4.6) |
vmid |
任意 | Proxmox VE の VMID(100〜999999999 の整数) |
interfaces |
任意 | インターフェースごとの追加情報(下記) |
デバイスID の規則: ^[A-Za-z0-9][A-Za-z0-9._-]*$(: は使用不可)
interfaces.<IF名> に書ける項目(MVP では保存・表示のみ。図への反映はフェーズ3以降):
description: 文字列ip: 文字列の配列(例:["192.0.2.1/30", "2001:db8::1/64"])mode:access/trunk/routedvlan: access の場合の VLAN ID。ゲストの IF では VLAN タグ(Proxmox VE のtag)、VLAN インターフェースではその VLAN IDvlans: trunk の場合の許可 VLAN(例:"10,20,100-110")。,・;・空白で区切り、10-20の範囲とall(すべて)・none(なし)も書ける。ゲストの IF では Proxmox VE のtrunks、VLAN aware なブリッジでは通す VLAN(Proxmox VE のbridge-vids。省略時は2-4094)transceiver: 挿しているトランシーバ(光モジュール)。例:10GBASE-SR。5.4 の一覧の名前(大文字小文字は区別しない)なら、ポートに合うかと、リンクの両端が合うかを確かめる。一覧に無い名前(製品の型番など)も書けるが、確かめない
論理的な IF(bond・ブリッジ・VLAN インターフェース。4.6)に書ける項目:
type:bond/bridge/vlan。省略時は物理的な IF(ポート)。typeを書いた IF は、モデルに無い名前でも使えるmembers: bond で束ねる IF、ブリッジにつなぐ IF(Proxmox VE のbond-slaves/bridge-ports)。同じデバイスの IF 名の配列bond-mode: bond のモード(balance-rr/active-backup/balance-xor/broadcast/802.3ad/balance-tlb/balance-alb)vlan-aware: ブリッジが VLAN aware(VLAN を区別して通す)か。true/false、省略時はfalsevlan-raw-device: VLAN インターフェースの親の IF- VLAN インターフェースの
vlanとvlan-raw-deviceは、名前が親.VLAN ID(vmbr0.10)なら省略できる。名前がvlan10の形ならvlanを省略できる
| フィールド | 必須 | 説明 |
|---|---|---|
a |
必須 | エンドポイント デバイスID:IF名 |
b |
必須 | エンドポイント |
media |
任意 | copper / fiber / dac / wireless / virtual。省略時は両端のトランシーバ(5.4)から、無ければポート種別から推定(ブリッジとのリンクは virtual) |
label |
任意 | リンク中央に表示する文字 |
- エンドポイントは 最初の
:でデバイスIDとIF名に分割する。 aとbの順序に意味はない。- ゲストの IF とホストのブリッジを結ぶリンクも書く(
a: web01:net0、b: pve1:vmbr0。4.6)。ブリッジには何本でもつなげる。 - 無線のポート(ポート種別
wireless)にも何本でもつなげる(AP に複数の端末をつなぐため)。
{ x: number, y: number }。GUI でデバイスを動かすと自動更新される。- ブリッジ(4.6)の位置は
pve1:vmbr0: { x: 300, y: 520 }のように書く。 - 任意で
_viewport: { x, y, zoom }を保存してよい。 - 手で動かした IF 名ラベル(7.3)の位置は
_labelsに書く。GUI でラベルを動かすと自動更新される。- キーはラベルの付く端(リンクの端と同じ
デバイスID:IF名。ブリッジとホストの間の線のホスト側のラベルはホスト:ブリッジ名)。 - 値は
{ along: number, side: number }。線の端から線に沿った距離と、線からの横のずれ(線に沿って外を向いたときの右がプラス)。
layout: _labels: pve2:eno3: { along: 120, side: -8 }
- キーはラベルの付く端(リンクの端と同じ
- 曲げた線(7.3)の点は
_bendsに書く。GUI で線を曲げると自動更新される。- キーは線の両端のノード(デバイスID。ブリッジなら
ホスト:ブリッジ名)を、名前の順に並べて空白でつないだもの。同じ 2 台の間の線(LAG など)は、まとめて同じ点を通る。 - 値は
{ x: number, y: number }の配列。キーの 1 つ目のノードの側から順に並べる。
layout: _bends: core1 core2: [ { x: 380, y: 300 }, { x: 380, y: 420 } ]
- キーは線の両端のノード(デバイスID。ブリッジなら
groups:
- id: rack1
kind: rack # site / rack / vlan / subnet / as / cluster / custom
label: Rack 1
members: [rt1, core-sw1]kind: clusterのグループは、クラスタのネットワークnetworks(マップ。キー = ネットワークの名前、値 = サブネット)を持てる(4.6)。
zones: SDN のゾーン(キー = ゾーンID)vnets: SDN の VNet(キー = VNet ID)
- Cisco 系の長い名前と短い名前を同一視する。
例:
GigabitEthernet0/1=Gi0/1、TenGigabitEthernet1/0/1=Te1/0/1 - 略称テーブル(初期):
Ethernet→Eth,FastEthernet→Fa,GigabitEthernet→Gi,TenGigabitEthernet→Te,TwentyFiveGigE→Twe,FortyGigabitEthernet→Fo,HundredGigE→Hu,Port-channel→Po,Vlan→Vl,Loopback→Lo,Serial→Se,Management→Mgmt - 大文字小文字は区別しない。
- YAML 読み込み時はどちらでも受け付け、保存時は短縮形に統一する。
- ただし
typeを書いた IF(bond・ブリッジ・VLAN インターフェース。4.6)の名前は Linux の名前なので、書いたとおりに保つ(Linux の VLAN インターフェースvlan10をVl10にしない)。比べるときは同じ規則で同一視する。
読み込み・編集のたびに検証し、結果を「問題パネル」に一覧表示する。
| レベル | 内容 | 扱い |
|---|---|---|
| エラー(パース) | YAML 構文エラー、constella バージョン不正 |
図は直前の正常状態のまま。エディタ上に赤線表示 |
| エラー(意味) | 存在しないデバイス/IF への参照、同一ポートの二重使用(ブリッジと無線のポートは除く)、デバイスID重複、未知のモデル | 図には反映するが、該当リンク/デバイスを赤で強調 |
| 警告 | ポート種別の不一致(例: RJ45 と SFP)、自分自身への接続、トランシーバがポートに合わない、両端のトランシーバが合わない(5.4)、layout._bends に対応する線が無い |
黄色で強調 |
- フレキシブルデバイス(model 無し、または
flexible: trueのモデル)は、未定義の IF 名を参照されたら自動でその IF を持っているものとして扱う。 - 銅線のトランシーバ(1000BASE-T・10GBASE-T)を挿した SFP のポートは、RJ45 のポートとみなしてポート種別を比べる。
- ハイパーバイザまわり(ゲスト・ブリッジ・bond・VLAN インターフェース・クラスタ・SDN)の検証は 4.6 を参照。
Proxmox VE のような仮想化基盤を描くための書き方。キーや IF の名前は Proxmox VE に合わせるが、ほかのハイパーバイザにも使える汎用の形にする。
devices:
core-sw1:
model: generic-l2switch-24
interfaces:
Gi0/1: { mode: trunk, vlans: "10,20,40" }
Gi0/2: { mode: trunk, vlans: "10,20,40" }
pve1:
model: generic-hypervisor-4 # ロールは hypervisor。物理的な NIC は eno1〜eno4
interfaces:
bond0: { type: bond, members: [eno1, eno2], bond-mode: 802.3ad }
vmbr0: { type: bridge, members: [bond0], vlan-aware: true }
vmbr0.10: { type: vlan, ip: [192.168.10.2/24] } # ホストの管理用の IP
web01:
model: generic-vm # guest: vm
host: pve1 # pve1 の上で動く
vmid: 100
interfaces:
net0: { vlan: 10, ip: [192.168.10.11/24] } # vlan = Proxmox VE の tag
fw01:
model: generic-vm
role: firewall # ロール(機能)は今までどおり。OPNsense の VM なら firewall
host: pve1
vmid: 101
interfaces:
net0: { mode: trunk, vlans: "10,20" } # Proxmox VE の trunks
db01:
model: generic-container # guest: container(Proxmox VE の LXC)
host: pve1
vmid: 200
interfaces:
net0: { vlan: 20 }
links:
- a: pve1:eno1 # 物理的な配線は今までどおり
b: core-sw1:Gi0/1
- a: pve1:eno2
b: core-sw1:Gi0/2
- a: web01:net0 # ゲストはブリッジにつなぐ
b: pve1:vmbr0
- a: fw01:net0
b: pve1:vmbr0
- a: db01:net0
b: pve1:vmbr0
layout:
pve1:vmbr0: { x: 300, y: 520 } # ブリッジの位置は「ホスト:ブリッジ」で覚える
groups:
- id: lab
kind: cluster
members: [pve1, pve2, pve3] # pve2・pve3 の定義は省略
networks: # クラスタのネットワーク(名前: サブネット)
corosync: 10.10.0.0/24
ceph-public: 10.20.0.0/24
migration: 10.30.0.0/24
sdn:
zones:
vlanzone: { type: vlan, bridge: vmbr0 } # nodes を省略 = ロールが hypervisor の機器すべて
vnets:
vnet40: { zone: vlanzone, tag: 40, subnets: [10.40.0.0/24] } # 各ホストに vnet40 ができる- ホストは、ふつうはロールが hypervisor の機器(組み込みモデル
generic-hypervisor-4など)。ほかのロールの機器(コンテナを動かすサーバーなど)もホストにできる。 - ゲストは、
hostに動いているホストのデバイスIDを書いた機器。- VM かコンテナかは
guest(vm/container)で表す。省略時はモデルのguest、それも無ければvm。 guestがあってhostが無い機器は、図に描かないホストの上のゲストとして扱う(クラウドの VM など)。- ロールは、ゲストでも機能を表す(OPNsense の VM なら firewall)。
- VM かコンテナかは
- ゲストの上でさらにゲストを動かす(入れ子の仮想化)こともできる。
- bond・ブリッジ・VLAN インターフェースは、ホストの
interfacesにtypeを付けて書く(Linux や Proxmox VE の/etc/network/interfacesと同じ考え方)。 - bond の
membersには物理的な IF を、ブリッジのmembersには物理的な IF・bond・VLAN インターフェースを書く。membersの無いブリッジは、ホストの中だけのネットワークになる。 - ホストの IP は、ブリッジや VLAN インターフェースの
ipに書く。 - リンクをつなぐのは、物理的な IF とブリッジだけ。bond・VLAN インターフェースにはつながない(束ねた線は、メンバーの物理的な IF ごとに書く)。
linksに、ゲストの IF とホストのブリッジを結ぶリンクを書く。ブリッジにつなげるのは、そのホストの上で動くゲストの IF だけ。- VLAN タグはゲストの IF の
vlanに、トランク(複数の VLAN を通す)はmode: trunkとvlansに書く。
groupsにkind: clusterのグループを書き、membersにホストを並べる。1 台のホストが入れるクラスタは 1 つだけ。networksに、クラスタのネットワークの名前とサブネット(10.10.0.0/24の形。IPv4・IPv6)を書く。名前は自由(corosync・ceph-public・ceph-cluster・migrationなど)。- クラスタのホストの IF のうち、IP がそのサブネットに入るものは、ラベルの IP の行にネットワークの名前を添える(
10.10.0.11/24 (corosync)。2 つ以上に入れば(ceph-public, ceph-cluster))。ゲストの IF には添えない。 - GUI では、ほかのグループと同じく作り、グループのインスペクタで種類をクラスタにして、ネットワークを編集する(7.5)。
- 最上位の
sdnに、ゾーン(zones)と VNet(vnets)を書く。 - ゾーンに書ける項目:
type(必須):simple/vlan/qinq/vxlan/evpnbridge: vlan・qinq のときに使う、各ホストのブリッジ(vlan・qinq では必須)tag: qinq の外側の VLAN ID(qinq では必須)nodes: ゾーンを使うホストのデバイスID。省略時は、ロールが hypervisor の機器すべて
- VNet に書ける項目:
zone(必須): ゾーンIDtag: VLAN ID(vlan・qinq では 1〜4094。qinq では内側の VLAN)。vxlan・evpn では VNI(1〜16777215)。simple 以外では必須subnets: サブネットの配列(10.40.0.0/24の形)
- VNet は、ゾーンの各ホストの中のブリッジとして扱う。ゲストは
pve1:vnet40につなぎ、位置もpve1:vnet40で覚える。 - VNet の箱は、ゲストがつながっているホストと、位置を覚えているホストにだけ描く(7.3)。
- ゾーンと VNet は YAML で書く(GUI で作る・編集する機能はまだ無い)。
| Proxmox VE | Constella |
|---|---|
| ノード | ロールが hypervisor の機器 |
| VM(qemu)/ CT(lxc) | guest: vm / guest: container の機器。host にノード、vmid に VMID |
ゲストの NIC(net0)の bridge=vmbr0 |
リンク web01:net0 ↔ pve1:vmbr0 |
ゲストの NIC の tag=10 |
IF net0 の vlan: 10 |
ゲストの NIC の trunks=20;30 |
IF net0 の mode: trunk、vlans: "20,30" |
bond-slaves / bond-mode |
type: bond の IF の members / bond-mode |
bridge-ports / bridge-vlan-aware / bridge-vids |
type: bridge の IF の members / vlan-aware / vlans |
vmbr0.10、vlan-raw-device |
type: vlan の IF、vlan-raw-device |
| クラスタ、corosync・Ceph・移行(migration)のネットワーク | kind: cluster のグループ、networks |
| SDN のゾーン・VNet | sdn の zones・vnets |
| レベル | 内容 |
|---|---|
| エラー | host のデバイスが無い。host をたどると輪になる(自分自身を含む) |
| エラー | VMID の重複(同じホストの中。ホストがクラスタに入っていれば、クラスタの中) |
| エラー | 1 台のホストが 2 つ以上のクラスタに入っている(2 つ目以降のクラスタでは数えない) |
| エラー | members の IF が無い。1 つの IF が 2 つの bond・ブリッジに入っている。bond の members に物理的でない IF がある。ブリッジの members にブリッジがある |
| エラー | VLAN インターフェースの親が無い・分からない。VLAN ID が分からない・1〜4094 の外 |
| エラー | ブリッジに、そのホストの上で動くゲスト以外の IF がつながっている。bond・VLAN インターフェースにリンクがつながっている |
| エラー | SDN: 参照先(ホスト・ゾーン)が無い。vlan・qinq のゾーンに bridge が無い、ゾーンのホストにその bridge が無い。qinq のゾーンに tag が無い。ゾーンに入っていないホストで VNet を使っている。VNet の tag が無い(simple 以外)・範囲の外(vlan・qinq では 1〜4094)。VNet ID がホストの IF 名と重なる |
| 警告 | VLAN の通り道(下記)で、通らない VLAN がある |
| 警告 | vlans の書き方が分からない(その場所は VLAN の通り道で確かめない) |
| 警告 | クラスタのホストに、networks のサブネットに入る IP が無い(ホストごとに 1 件にまとめ、足りないネットワークを並べる) |
| 警告 | networks のサブネットの書き方が分からない(そのネットワークは使わない)。kind: cluster 以外のグループに networks がある(使わない) |
| 警告 | VNet ID が 8 文字より長い(Proxmox VE の制限) |
| 警告 | VNet の subnets の書き方が分からない |
VLAN の通り道(最初のスイッチまでを確かめる):
- 対象は、ブリッジにつながったゲストの IF のタグ(
vlan)とトランクのvlans、ホストの VLAN インターフェースの VLAN、SDN の VLAN(vlan ゾーンは VNet のtag、qinq ゾーンはゾーンのtag(外側の VLAN))。トランクでvlansが無い(すべて通す)ゲストの IF と、VNet につながったゲストの IF は確かめない。 - SDN の VLAN は、ゾーンのすべてのホストで、ゾーンの
bridgeから確かめる(使っている IF はpve1:vnet40のように書く)。 - ブリッジが VLAN aware なら、ブリッジの
vlans(省略時は2-4094)に入っているか。VLAN aware でなければ、確かめずに先へ進む(タグを付けたまま物理的な IF から出るため)。 - ブリッジの
membersから bond をたどって物理的な IF へ進む。VLAN インターフェースは親から始める。membersの VLAN インターフェース(bond0.20など)の先と、親が VLAN インターフェースの VLAN インターフェースは、タグが内側に包まれるので確かめない(外側の VLAN は、その VLAN インターフェース自身で確かめる)。
- 物理的な IF にリンクでつながる相手のポートで通るか。機器のロールは問わない。リンクが無ければ確かめない。
- trunk(
mode: trunk、またはmodeが無くてvlansがある)なら、vlans(省略時はすべて通す)に入っているか。 - access なら、タグの付いた VLAN は通らない。
- routed のときと、
mode・vlansのどちらも無いときは確かめない。
- trunk(
- 警告は止まる場所(ブリッジ・相手のポート)ごとに 1 件出し、通らない VLAN と、それを使っている IF を書く。止まった VLAN は、その先では確かめない。
Proxmox VE のクラスタ(ノード 1 台だけでもよい)の構成を読み込み、新しい図を作る。
- どれか 1 台のノードで、root として書き出しスクリプト
scripts/proxmox-export.shを実行する。スクリプトはpvesh(Proxmox VE の API をコマンドで使う道具)で読むだけで、設定は変えない。パスワードやトークンも使わない。sh proxmox-export.sh > constella-proxmox.json - できた JSON ファイルを、ツールバーの「取り込む」(ファイルメニューの「Proxmox VE から取り込む…」)で選ぶ。未保存の変更があれば、開くときと同じく確かめる。
- 新しい図(未保存)として開く。座標は書かず、開くと自動で並べる。読めなかったものがあれば、取り込んだあとに一覧で知らせる。
書き出す JSON(format: constella-proxmox、version: 1):
exported(書き出した日時)、host(書き出したノード)nodes: ノードごとにnode(名前)、network(pvesh get /nodes/<node>/networkの結果)、qemu・lxc(ゲストごとにvmidと、pvesh get /nodes/<node>/qemu/<vmid>/configなどの設定)cluster:status(/cluster/status)、nodes(/cluster/config/nodes。corosync のアドレス)、options(/cluster/options。移行のネットワーク)、ceph(/etc/pve/ceph.confのpublic_network・cluster_network。Ceph を使っていなければnull)sdn:zones(/cluster/sdn/zones)、vnets(/cluster/sdn/vnets)、subnets(VNet ごとの/cluster/sdn/vnets/<vnet>/subnets)。適用済みの設定(running)を読み、読めなければ今の設定を読む。- 停止中のノードなど、読めなかったところは
nullにして、続きを書き出す。ノード 1 台だけでクラスタが無ければ、clusterの中は読めないところがnullになる。 cluster・sdnはフェーズ5-2 で足した(どちらも無い JSON も、同じversion: 1として読む。クラスタと SDN は無い扱い)。
取り込み方:
| Proxmox VE | Constella |
|---|---|
| ノード | モデル無し(どんな IF 名も使える)で role: hypervisor の機器。ID はノード名 |
| ネットワークの eth・OVSPort | 物理的な IF。使っていないものも書く(GUI でスイッチにつなぐとき、ポートの一覧に出るように) |
| bond・OVSBond | type: bond。slaves・ovs_bonds → members、bond_mode → bond-mode(Linux の bond のモードだけ) |
| bridge・OVSBridge | type: bridge。bridge_ports・ovs_ports → members、bridge_vlan_aware → vlan-aware(OVS のブリッジはいつも VLAN aware)、bridge_vids → vlans(既定の 2-4094 なら書かない) |
| vlan、タグのある OVSIntPort | type: vlan。vlan-raw-device・ovs_bridge と vlan-id・ovs_tag(名前が「親.VLAN ID」の形なら書かない) |
cidr・cidr6、comments |
ip、description |
| VM・CT | ゲスト(モデル generic-vm / generic-container)。host にノード、vmid に VMID |
ゲストの net0 など |
IF net0 など。tag → vlan、trunks → mode: trunk と vlans(; は , に)、CT の ip・ip6 と VM の cloud-init の ipconfig0 などの ip・ip6 → ip(dhcp などは書かない) |
ゲストの bridge= |
ゲストの IF とノードのブリッジ(SDN の VNet も)を結ぶリンク |
| クラスタ | kind: cluster のグループ。ID はクラスタ名、members はノード |
corosync のリンク 0(ring0_addr)・リンク 1 … |
networks の corosync・corosync-link1 …。サブネットは、そのアドレスを持つノードの IF の IP から求める |
移行のネットワーク(migration の network) |
networks の migration |
Ceph の public_network・cluster_network |
networks の ceph-public・ceph-cluster(2 つ以上書いてあれば ceph-public-2 …) |
| SDN のゾーン | sdn.zones。type・bridge・tag・nodes(, 区切りを配列に) |
| SDN の VNet | sdn.vnets。zone・tag、サブネットの cidr → subnets |
- ゲストのデバイスID は、VM の名前(CT はホスト名)。使えない文字は
-に置き換え、ほかの機器と重なれば-VMIDを付ける。名前が無ければvm100/ct200。名前と違う ID にしたときはlabelに名前を書く。 - 機器は、ノード(書き出した順)のあとに、ノードごとのゲスト(VMID の順)を並べる。ノードの IF は、物理的な IF・bond・ブリッジ・VLAN インターフェースの順に並べる。
- ネットワークを読めなかったノードには、ゲストの
bridge=からブリッジだけを作る。ネットワークを読めたノードのブリッジにも、SDN の VNet にも無いところへのリンクは作らない。 - クラスタのネットワークのサブネットが分からないとき(corosync のアドレスを持つ IF が無い、アドレスがホスト名など)は、そのネットワークを書かない。
- ゾーン・VNet のうち、取り込めない種類のゾーン(SDN の 5 種類にないもの)と、そこの VNet は取り込まない。
- 取り込めなかったもの(上の 3 つ、読めなかったノード・ゲスト)は、取り込んだあとに一覧で知らせる。
- テンプレート(
template: 1)の VM・CT は取り込まない。 - スイッチと物理的な配線は取り込めない(取り込んだあとに図に足してつなぐ)。
- 図のタイトルは「Proxmox VE」、説明は書き出した日時とノード。
id: cisco-c2960-24tt-l
vendor: Cisco
name: Catalyst 2960-24TT-L
role: l2switch
ports:
- name: "FastEthernet0/{1-24}"
type: rj45
speed: 100M
- name: "GigabitEthernet0/{1-2}"
type: rj45
speed: 1Gnameの{開始-終了}は連番展開する(1つの name に複数書くことも許可。例:Gi1/{0-1}/{1-4})。type:rj45/sfp/sfp+/sfp28/qsfp+/qsfp28/console/serial/wireless/virtualspeed: 表示用の文字列(100M/1G/10G/25G/40G/100Gなど)flexible: trueを指定すると、定義外の IF 名も使用可能になる。guest: vm/guest: containerを指定すると、そのモデルの機器はゲスト(4.6)になる。iconでアイコンを変えられる。組み込みのアイコンの名前(role と同じ名前。例:firewall)か、アイコンのフォルダ(5.2)の画像のファイル名(.svg・.png。例:fortigate.svg)を書く。省略すると role のアイコン。画像が見つからなければ role のアイコンで表示し、パレットで知らせる。
- 組み込みモデル: アプリ同梱(
src/library/models/*.yaml) - ユーザーモデル: OS ごとのアプリデータディレクトリ配下の
models/(macOS では~/Library/Application Support/Constella/models/)。Tauri のパス API で取得すること。 - 同じ id があればユーザーモデルを優先する。
- アイコンの画像(5.1 の
icon): ユーザーモデルのフォルダと並ぶicons/(macOS では~/Library/Application Support/Constella/icons/)。モデルの管理画面(5.5)で取り込むと、ここにコピーする(1 MB まで)。
汎用モデル(ポート名は Cisco 風に統一):
- 汎用モデル(
generic-*。仮想化のモデルも)はすべてflexible: trueにする(一覧に無い IF 名も使える)。実機モデルは定義したポートだけ。 generic-router-4(Gi0/0〜Gi0/3、rj45)generic-l2switch-24(Gi0/1〜Gi0/24 rj45 + Te0/25〜Te0/28 sfp+)generic-l3switch-48(Gi1/0/1〜Gi1/0/48 rj45 + Te1/1/1〜Te1/1/4 sfp+)generic-firewall-8(port1〜port8、rj45)generic-server-2(eth0〜eth1)generic-pc(eth0 rj45、wlan0 wireless)generic-ap(eth0, wlan0)generic-cloud(flexible)
仮想化のモデル(フェーズ4で追加。4.6):
generic-hypervisor-4(eno1〜eno4、rj45。role: hypervisor)generic-vm(net0〜net3、virtual、flexible。role: server、guest: vm)generic-container(net0〜net3、virtual、flexible。role: server、guest: container)
実機モデル(例として1つ):
cisco-c2960-24tt-l(上記 5.1 の定義)
実機モデルは後からユーザーが追加していく前提。実機モデルを追加する場合はポート構成を公式資料で確認すること。
IF の transceiver(4.3)で確かめる名前の一覧。アプリに組み込む。
| 速度 | 形 | 名前 |
|---|---|---|
| 1G | SFP | 1000BASE-SX・1000BASE-LX・1000BASE-EX・1000BASE-ZX・1000BASE-BX-U・1000BASE-BX-D・1000BASE-T |
| 10G | SFP+ | 10GBASE-SR・10GBASE-LRM・10GBASE-LR・10GBASE-ER・10GBASE-ZR・10GBASE-BX-U・10GBASE-BX-D・10GBASE-T・10G-DAC・10G-AOC |
| 25G | SFP28 | 25GBASE-SR・25GBASE-LR・25G-DAC・25G-AOC |
| 40G | QSFP+ | 40GBASE-SR4・40GBASE-LR4・40G-DAC・40G-AOC |
| 100G | QSFP28 | 100GBASE-SR4・100GBASE-LR4・100GBASE-CWDM4・100GBASE-DR・100G-DAC・100G-AOC |
- DAC は両端にコネクタの付いた銅のケーブル、AOC は両端にコネクタの付いた光のケーブル。BiDi(
-BX-U・-BX-D)は 1 本の光ファイバーで送受信するもの。 - 別名も受け付ける:
1000BASE-LX10・1000BASE-LH(=1000BASE-LX)、1000BASE-BX10-U・1000BASE-BX10-D・10GBASE-BX10-U・10GBASE-BX10-D(= それぞれの-BX-U・-BX-D)、10GBASE-CR・25GBASE-CR・40GBASE-CR4・100GBASE-CR4(= それぞれの DAC)。 - 挿せるポート:
- SFP のポート: SFP。SFP+ のポート: SFP・SFP+。SFP28 のポート: SFP・SFP+・SFP28
- QSFP+ のポート: QSFP+。QSFP28 のポート: QSFP+・QSFP28
- RJ45・コンソール・シリアル・無線・仮想のポートと、論理的な IF(4.6)には挿せない(一覧に無い名前でも警告する)
- リンクの両端が合うのは、同じ名前(別名は同じとみなす)のとき。BiDi は、同じ速度の U と D の組み合わせのとき。片方の端にしか書いていなければ、確かめない。
- media の推定(4.3): 光のもの(AOC を含む)は
fiber、1000BASE-T・10GBASE-Tはcopper、DAC はdac。両端で違うときは、端 a を優先する。
メニューの「ファイル > モデルの管理…」と、パレットの「管理」ボタンで開く、アプリの中の大きなダイアログ。
- 左にモデルの一覧(検索欄、role ごとのまとまり、組み込み・ユーザーの印)。読めないユーザーモデルのファイルも、一覧の下に出す。
- 右に、選んだモデルの中身: 名前・ID・メーカー・役割・ゲスト・フレキシブル、アイコン、ポート(種類ごとの数と名前の範囲)、開いている図で使っている台数。
- 操作:
- 「+ モデルを追加」、「複製」(組み込みもユーザーも)、「編集」(ユーザーモデル。追加・編集のダイアログを開く)、「削除」(ユーザーモデル。図で使っている台数を確認に出す)。
- 「アイコンを変える」(ユーザーモデル): 組み込みのアイコンから選ぶ、画像(SVG・PNG。1 MB まで)を取り込む、role のアイコンに戻す。取り込んだ画像は、どのモデルからも選べる。どのモデルも使っていない画像は消せる。
- 組み込みモデルは変えられない(複製してから変える)。
- 追加・編集のダイアログにも、同じ選び方のアイコンの欄を置く。
- まとめて書き出す・読み込む(別の Mac や人に渡すため):
- 「書き出す…」: 書き出すユーザーモデルをチェックで選び(最初はすべて)、1 つの YAML のファイル(名前の初期値は
constella-models.yaml)に書き出す。選んだモデルが使うアイコンの画像も入れる。 - 「読み込む…」: 書き出したファイルを選び、中身の一覧(新しいモデル・中身が違うモデル・今と同じモデル・組み込みのモデルを置き換えるモデル・読めないもの・名前を変えて取り込む画像)を確かめてから取り込む。
- 同じ ID のユーザーモデルがあって中身が違えば、「上書きして取り込む」か「重なるものは飛ばす」かを選ぶ。中身が同じなら何もしない。
- 画像は、同じ名前で中身も同じなら今の画像を使う。中身が違えば、同じ中身の画像が別の名前であればそれを使い(取り込み直しても増やさない)、無ければ
-2などを付けて取り込む。名前が変わったら、モデルのiconもその名前に書き換える(モデルの YAML のほかの書き方とコメントはそのまま)。取り込むモデルが使う画像だけを書き込む。 - 読めないモデルと、1 MB を超える画像は取り込まない。
- ファイルの形(先頭の
constella-modelsでトポロジの YAML と見分ける。モデルの YAML は書いたとおり、画像は base64):
- 「書き出す…」: 書き出すユーザーモデルをチェックで選び(最初はすべて)、1 つの YAML のファイル(名前の初期値は
constella-models: 1
models:
- file: my-switch.yaml
text: |
id: my-switch
role: l2switch
icon: my-switch.svg
...
icons:
- file: my-switch.svg
data: PHN2ZyB4bWxucz0i...┌──────────────────────────────────────────────────────────────┐
│ ツールバー: 新規 開く 取り込む 保存 | 選択 接続 | 自動整列 | YAML表示切替 │
├──────────┬───────────────────────────────┬──────────────────┤
│ パレット │ │ インスペクタ │
│ │ キャンバス │ (選択中の │
│ 検索欄 │ (React Flow) │ デバイス/リンク) │
│ ロール別 │ │ │
│ モデル一覧 │ ├──────────────────┤
│ │ │ YAMLエディタ │
│ │ │ (表示切替可) │
├──────────┴───────────────────────────────┴──────────────────┤
│ ステータスバー: ファイル名 / 未保存マーク / エラー n 件 警告 n 件 │
└──────────────────────────────────────────────────────────────┘
- YAML エディタは右側の下段に置き、ツールバーで表示/非表示を切り替える。将来的に「キャンバスとYAMLを左右半々」レイアウトにも切り替えられる構造にしておく。
- 問題パネルはステータスバーの件数をクリックすると開く。項目クリックで該当デバイス/リンク、および YAML の該当行へジャンプ。
- ツールバーの「グリッド」で、機器を背景の点(方眼)に合わせて置くかを切り替える(7.4)。アプリに覚えておき、最初は切ってある。
- macOS の標準メニューバー(ファイル/編集/表示/ウィンドウ/ヘルプ)と標準ショートカット(⌘N, ⌘O, ⌘S, ⇧⌘S, ⌘Z, ⇧⌘Z, ⌘C, ⌘V, ⌘A, Delete)に対応する。
- パレットからキャンバスへドラッグ&ドロップで配置。
- パレットのモデルは role ごとにまとめ、ゲストのモデル(
guestのあるモデル)は「ゲスト(VM・コンテナ)」にまとめる。インスペクタのモデル一覧も同じ。 - VM・コンテナのモデルを置くとき(4.6):
- ハイパーバイザ(またはゲストのいるホスト)の上に落とすと、そのホストのゲストになり、ホストの下に並ぶ(同じホストのゲストがいれば、その右)。
- ホストの枠の中に落とすと、その枠のホストのゲストになる(落とした位置に置く)。
- それ以外の所に落とすと、
hostなし。 - ほかのホストの枠や機器と重なるときは、同じ段の左、下の段の順に、空いている場所に置く。
- 置いたあとにドラッグしても、ホストは変わらない(インスペクタで変える)。
- bond・VLAN インターフェース(4.6)は、ホストのインスペクタの「bond・VLAN インターフェース」の欄で足して編集する(キャンバスには箱を置かない):
- bond は members(物理的な IF)とモード、VLAN インターフェースは親と VLAN ID、どちらも IP・説明を編集できる。
- VLAN インターフェースの名前が「親.VLAN ID」の形なら、親や VLAN ID を変えると名前も変える。それ以外の名前(
vlan10など)なら、vlan-raw-deviceとvlanに書く。 - bond・ブリッジの members の候補では、ほかの bond・ブリッジに入っている IF は選べない(外すことはできる)。
- ブリッジ(4.6)の作り方と編集:
- ホストのインスペクタの「ブリッジを追加」で足す。名前は
vmbr0・vmbr1… と自動で付け、ホストの下の段に置く(重なるときは、ゲストと同じ順に空いている場所を探す)。 - ブリッジの箱を選ぶと、ブリッジのインスペクタを出す(名前・members・VLAN aware・通す VLAN・IP・説明、つながっているゲストの一覧、削除)。名前を変えると、リンク・位置のキー・ほかの IF の members も書き換える。
- ブリッジの箱は、デバイスと同じく選ぶ・動かす(位置は
layoutのホスト:ブリッジ名)・Delete キーで消す(つながっていたリンクも消える)ことができる。
- ホストのインスペクタの「ブリッジを追加」で足す。名前は
- VNet(4.6)の箱を選ぶと、VNet のインスペクタを出す(ゾーン・tag・subnets・そのホストでつながっているゲストの表示と、「このホストから外す」)。「このホストから外す」と Delete キーは、そのホストの VNet の箱(つながっていたリンクと位置)を消す。VNet の定義は残る。
- デバイスIDは role ごとのプレフィックス + 連番で自動採番(
rt1,sw1,l3sw1,fw1,srv1,hv1,pc1,ap1,cloud1)。ゲストは role ではなく種類で採番する(vm1,ct1)。 - インスペクタでデバイスID・ラベル・モデルを変更できる。ゲストなら、種類・ホスト・VMID も変更できる。
- デバイスIDを変更したら、関連するリンク・layout・groups の参照もすべて書き換える(ゲストの
host、ブリッジの位置のキー、SDN のnodesも含む)。 - モデルを変更して既存リンクのポートが存在しなくなる場合は確認ダイアログを出す。
- ゲストのホストを変えると、今のホストのブリッジへのリンクは、新しいホストに同じ名前のブリッジがあれば付け替える。無ければ、確認ダイアログで「リンクを消す」か「そのまま残す(エラーになる)」かを選ぶ。
- デバイスIDを変更したら、関連するリンク・layout・groups の参照もすべて書き換える(ゲストの
- デバイスのインスペクタの IF 一覧では、IF ごとに接続先を出す(押すとそのリンクを選ぶ)。2 本以上つながっている IF(無線のポートなど)は本数を出し、押すとその線をすべて選ぶ。
- IF のトランシーバ(4.3)は、デバイスのインスペクタで IF の行を開いて入れる。そのポートに合うもの(5.4)を候補に出し、自由にも書ける。
- リンクのインスペクタでは、両端にまとめて入れる(挿せない端、つまり RJ45 などのポートとブリッジには入れない)。BiDi の U を入れると、反対の端に D を入れる。両端で違うものにするときは、IF の行で 1 つずつ入れる。
- ツールバーで「接続」モードにする(ショートカット:
C)。 - デバイスAをクリック → ポート選択ポップアップが開く。
- ポートを選ぶ → デバイスBをクリック → ポート選択ポップアップ → リンク作成。
- Esc でキャンセル。
- ブリッジ(4.6)の箱をクリックしたときは、ポート選択ポップアップを出さず、そのブリッジを端にする。
ポート選択ポップアップ:
- デバイスの右に開き、キャンバスに収まらなければ左に開く。下に収まらなければ、デバイスの下端から上へ開く(上下もキャンバスの中に収める)。ほかのパネルより手前に出す。
- ポート種別ごとにグループ表示(RJ45 / SFP+ …)。
- 使用中ポートはグレーアウトし、接続先(
core-sw1:Gi0/1など)を併記。選択不可。 - ただし無線のポート(4.3)は、使用中でも選べる(つながっている本数を添える)。
- 一覧上部に検索欄。
- 「次の空きポート」ボタン(先頭の未使用ポートを選択。無線のポートも、1 本もつながっていないときだけ選ぶ)。
- フレキシブルデバイスの場合は IF 名を自由入力できる。
- 相手側ポートと種別が合わない場合は警告アイコンを表示(接続自体は許可)。
- ブリッジは「ブリッジ(何本でもつなげる)」のグループに出し、使用中にしない(つながっている本数を添える)。ゾーンのホストでは、VNet(4.6)も同じグループに出す(
VNetと添える)。 - bond・VLAN インターフェースは出さない(線をつながないため)。
- デバイスの中心同士を直線で結ぶ「フローティングエッジ」方式で描画する(ポート数が多いため、デバイス上に接続点ハンドルは表示しない)。曲げる点(
layout._bends)があれば、その点を順に通る折れ線にする。 - リンク両端のデバイス寄りの位置にIF名(短縮形)を表示する。ラベルは線に重ならないよう小さな背景付きで描く。
- IF にトランシーバ・IP・VLAN が書かれていれば、IF 名ラベルの下に 1 行ずつ表示する(トランシーバは書いたとおり、IP は 1 つずつ、access は
VLAN 10、trunk はtrunk 10,20,30)。行の順は、IF 名・bond の名前(ハイパーバイザの表示)・トランシーバ・IP・VLAN。 - 同じデバイス間に複数リンクがある場合(LAG など)は、線を少しずつずらして重ならないようにする。
- 両端の IF 名ラベルが隣の線のラベルと重なるときは、線に沿って段違いにする。線が短くて段違いでは収まらないときは、線の両側(横向きの線なら上下)に分けて置く。そのときも、3 本以上で同じ側に並ぶラベルと、真ん中の線のラベルは段違いにする。
- 同じデバイスに付く別々の線の IF 名ラベルが重なるとき(線の向きが近いときなど)は、デバイスに近いラベルはそのままにし、遠いほうのラベルをその線に沿って外へずらす。ブリッジとホストの間の線のホスト側のラベルも同じに扱う。線が短くて重ならない所までずらせないときは、反対の端のラベルの手前までずらす。
- IF 名ラベル(ブリッジとホストの間の線のホスト側のラベルも)は、選択モードでドラッグして動かせる(ドロップしたときに
layout._labelsへ 1 回書く。Undo 可能)。- 動かした位置は、線の端からの「線に沿った距離」と「線からの横のずれ」で覚える。機器を動かしても、ラベルは線に付いていく。
- 手で動かしたラベルは自動ではずらさず、ほかのラベルがそれを避ける。
- ラベルをダブルクリックすると、自動の位置に戻す。ツールバーの「ラベルを戻す」で、すべてのラベルを戻す。
- ラベルをクリックすると、そのリンクを選ぶ。接続モードでは触れない。リンク中央のラベルと、自分自身へのリンクの輪のラベルは動かせない。
- リンクやデバイスを消すと、そのラベルの位置も消す。デバイスID を変えると、キーも書き換える。
- 線は、選択モードで曲げられる(ドロップしたときに
layout._bendsへ 1 回書く。Undo 可能)。- リンクを選ぶと、曲げる点(四角)と、区間の真ん中(小さな丸)につまみを出す。真ん中の丸をドラッグすると、そこに曲げる点を足す。曲げる点はドラッグで動かし、ダブルクリックで消す。
- ドラッグ中、隣の点(端なら機器の中心)と水平・垂直に近ければ(画面で 8px 以内)そろえ、ガイドの線(7.4)を出す。⌥(Option)を押している間はそろえない。
- 同じ 2 台の間の線(LAG など)は、まとめて同じ点を通り、平行にずらして描く。
- 両端の IF 名ラベルは、端の区間(機器から最初の点まで)に沿って置く。リンク中央のラベルは、折れ線の長さの真ん中に置く。
- リンクのインスペクタの「まっすぐに戻す」で、そのリンク(同じ 2 台の間の線すべて)の点を消す。
- 両端の機器をまとめて動かしたときは、点も一緒に動かす(ドラッグ中も)。片方だけ動かしたときは、点はそのまま。
- 同じ 2 台の間の線がすべて無くなったら、その点も消す。デバイスID やブリッジの名前を変えると、キーも書き換える。
- 自分自身へのリンクの輪と、ブリッジとホストの間の点線は曲げられない。
- media による線のスタイル: copper=実線(グレー系)、fiber=実線(オレンジ系)、dac=実線(青系)、wireless=破線、virtual=点線。
- 機器のアイコンは role ごとのアイコン。モデルに
icon(5.1)があれば、そのアイコンにする。組み込みのアイコンは role の色で描き、画像のアイコンは色を付けずにそのまま表示する。SVG・PNG の書き出しには、画像を埋め込む。 - ハイパーバイザ(4.6)の表示:
- ホストは枠で囲み、中にブリッジとゲストを置く。グループの枠は、メンバーのホストの枠を丸ごと囲む。
- ホストの枠のラベルは、直接のゲストの数(
VM 2・CT 1。ゲストがいなくてブリッジだけなら「ゲストなし」)。クリックすると、ホストとゲストをまとめて選ぶ。 - ホストの枠は「グループ」の表示とは別に、ゲストを表示しているときに出す。
- ゲストのアイコンには
VM/CTの印を付ける。 - ブリッジ(SDN の VNet を含む)は小さな箱で描き、VLAN aware なら
VLANの印を付け、IP を箱の下に出す。ブリッジの上の VLAN インターフェースの IP も、箱の下に「vmbr0.10 192.168.10.2/24」の形で出す。 - VNet の箱には
VNetの印を付け、箱の下に tag(vlan はVLAN 40、qinq はVLAN 40(外側 100)、vxlan はVXLAN 1000、evpn はEVPN 1000。「VLAN」の表示に従う)とsubnets(「IP」の表示に従う)を出す。点線は、vlan・qinq ならゾーンのブリッジの箱へ、vxlan・evpn ならホストへ引く(simple は引かない)。 - bond の IP と、bond・物理的な IF の上の VLAN インターフェースの IP は、ホストの名前の下に「bond1 10.20.0.11/24」の形で出す。
- bond に束ねた物理的な IF の線は、ホスト側のラベルの IF 名の下に、bond の名前を添える。
- ブリッジとホストの間には、点線(仮想のリンクと同じ)を引く。線のホスト側には、ブリッジのメンバー(bond0 など。bond なら、その下に「eno1 + eno2(802.3ad)」のように束ねた NIC とモードも)を出す。members の無いブリッジには線を引かない(ホストの中だけのネットワーク)。
- ゲストとブリッジのリンクは、ブリッジの箱につなぐ。ブリッジ側の端には IF 名を出さない(箱に名前があるため)。
- クラスタのネットワークに入る IF は、IP の行にネットワークの名前を添える(
10.10.0.11/24 (corosync)。4.6)。リンクの端・ホストの名前の下・ブリッジの箱の下の、どの IP の行も同じ。
- 表示オプション: IF名ラベル・トランシーバ・IP・VLAN・ゲスト(VM・コンテナ・ブリッジ)それぞれの表示/非表示、長い名前/短縮形の切り替え。
- 表示レイヤー(すべて / L1 / L2 / L3)で、ラベルの行とグループの枠をまとめて切り替える。
- L1: IF 名とトランシーバ、拠点・ラックの枠。ゲストとブリッジは隠す
- L2: IF 名と VLAN、VLAN の枠
- L3: IF 名と IP、サブネット・AS の枠
- 種類なし・custom・cluster の枠は、どのレイヤーでも表示する。
- レイヤーを選んだあとも、個々の表示オプションで調整できる。
- ショートカットは ⌘0(すべて)、⌘1〜⌘3(L1〜L3)。
- 選択・複数選択(ドラッグ範囲選択、⇧クリック)・移動・削除。
- ガイドとスナップ: 機器(ブリッジの箱も)をドラッグすると、アイコンの中心がほかの機器・ブリッジの箱の中心と縦・横でそろう位置に近づいたとき(画面で 8px 以内)吸い付き、そろった機器どうしの間にガイドの線(点線。両端を少しはみ出す)を出す。
- 複数の機器をまとめて動かすときは、つかんでいる機器(範囲選択の枠をつかんだときは、その中の最初の機器)を基準にそろえ、ほかの機器も同じだけ動かす。
- 等間隔: 同じ行(縦なら同じ列)に並ぶほかの機器と、アイコンの中心どうしの間隔が等しくなる位置に近づいたとき(画面で 8px 以内)も吸い付く。
- 同じ行とみなすのは、アイコンの中心の縦のずれがアイコンの高さより小さい機器・ブリッジの箱(横から見てアイコンが重なるもの)。
- 吸い付く位置は、両隣の 2 台の真ん中と、片側の隣の 2 台の間隔をそのまま延ばした位置。
- 中心をそろえる位置と等間隔の位置の両方が近いときは、近いほうに合わせる(同じ距離なら、そろえるほう)。
- 等しい間隔ごとに、アイコンのすき間に実線と両端の短い線のガイドを出す。同じ間隔で続いている機器があれば、その間隔にも出す。
- 機器のドラッグだけに効く(曲げる点のドラッグ、パレットから置くときには効かない)。
- ツールバーの「グリッド」を入れると、そろう機器が近くに無い向きは、アイコンの中心を背景の点(20px ごと)に合わせる。パレットから置くときも、アイコンの中心を背景の点に合わせる。
- ⌥(Option)を押している間は、吸い付かず、ガイドも出さない。矢印キーで動かすときは吸い付かない。
- デバイス削除時は接続していたリンクも削除する。SDN のゾーンの
nodesからも外す。- ホストを削除しても、ゲストは残して
hostを外す(ホストとゲストをまとめて消すときは、ホストの枠のラベルをクリックして選ぶ)。
- ホストを削除しても、ゲストは残して
- コピー&ペースト: 選択デバイスと、選択内で完結するリンクを複製。デバイスIDは自動で再採番。
- ゲストの
hostは、ホストも一緒に複製したなら新しいホストに、そうでなければ元のまま(図に無いホストなら外す)。 - 複製したリンクの曲げた点(貼り付けた位置に合わせてずらす)と、手で動かした IF 名ラベルの位置も複製する。
- グループ: メンバーの一部を複製したら、同じ ID のグループに入れる(貼り付け先に無ければ、そのグループを作る)。図にあるメンバーを全部複製したグループは、新しいグループとして複製する。
- 作るグループの ID は、空いていればそのまま、使われていれば末尾の数字を今ある最大の番号 + 1 にする(
rack1→rack2、数字が無ければdc→dc2)。種類・ラベル・ネットワークは引き継ぐ。 - クリップボードの YAML には、選んだデバイスの入っているグループを、図にあるメンバーをすべて書いて入れる(全部を複製したかどうかを、貼り付けるときに見分けるため)。
- 作るグループの ID は、空いていればそのまま、使われていれば末尾の数字を今ある最大の番号 + 1 にする(
- ゲストの
- Undo/Redo: すべての編集(GUI・YAML の両方)を対象にする。YAML エディタでの編集は、反映の単位(後述のデバウンス後)で1ステップとする。
- 作る: 機器を選んで「グループにする」(⌘G。編集メニュー、複数選択のインスペクタ、デバイスのインスペクタの「グループ」欄にもある)。
- 選んだデバイスをメンバーにした、種類なしのグループを
groupsの最後に足す。ブリッジの箱はメンバーに入れない。 - ID は
group1・group2…(デバイスID と同じく、今ある最大の番号 + 1)。 - 作ったグループを選び、インスペクタのラベル欄にカーソルを置く。「グループ」の表示が切ってあれば入れる。
- 選んだデバイスをメンバーにした、種類なしのグループを
- 選ぶ: 枠のラベルをクリックすると、メンバーを選び(そのままドラッグで一緒に動かせる)、インスペクタにグループを出す。トポロジのインスペクタ(何も選んでいないとき)のグループの一覧と、問題パネルのグループの問題からも選べる(枠を出していないグループや、メンバーが空のグループも)。選んだグループの枠は強調する。
- 選んでいる機器を変えると(クリック・範囲選択など)、グループの選択は外れる。
- グループのインスペクタ:
- ID(デバイスIDと同じ書き方。ほかのグループと重ならない)、種類、ラベル(空なら枠に ID を出す)。
- メンバーの一覧(クリックでそのデバイスを選ぶ、× で外す。図に無いデバイスの ID も外せる)と、デバイスを選んで足す欄。
- 種類がクラスタ(または
networksがある)なら、ネットワークの名前とサブネットの行を足す・変える・消す(4.6)。サブネットは書き方を確かめる。 - 「グループを削除」(メンバーの機器は残す)。
- 枠を出していなければ、その理由(「グループ」の表示、表示レイヤー、図にあるメンバーがいない)を添える。
- デバイスのインスペクタの「グループ」欄: 入っているグループ(クリックで選ぶ、× で外す)、ほかのグループに入れる欄、「新しいグループ」。
- ⇧⌘G(グループを解除。編集メニューにもある)で、選んでいるグループを消す(機器は残す)。グループを選んでいるときの Delete キーは、今までどおりメンバーの機器を消す(グループは空のまま残る)。
- YAML エディタにカーソルがあるときの ⌘G・⇧⌘G は、今までどおり「次を検索」「前を検索」。入力欄にカーソルがあるときは何もしない。
- グループを消しても、ほかのグループの書き方やコメントは変えない。すべて消したら
groupsごと消す。
キャンバスで右クリック(control + クリックも)すると、その物でできることをメニューに出す。
| 右クリックした所 | 項目 |
|---|---|
| 機器(ブリッジの箱と一緒に選んでいても) | 接続を始める(機器 1 台のとき)・コピー ⌘C・カット ⌘X・グループにする ⌘G・削除 ⌫ |
| ブリッジの箱(だけ) | 削除 ⌫ |
| 線(IF 名ラベルも) | まっすぐに戻す・ラベルを戻す・削除 ⌫ |
| グループの枠のラベル | メンバーをコピー ⌘C・グループを解除 ⇧⌘G |
| ホストの枠のラベル | 機器と同じ(ホストとゲストを選ぶ) |
| 何も無い所 | ここにペースト・すべてを選択 ⌘A・自動整列・ラベルを戻す(すべて) |
- 選んでいない物を右クリックしたら、それだけを選んでからメニューを出す。選んでいる物を右クリックしたら、選択はそのままで、選んでいる物にまとめて効く。何も無い所の右クリックでは、選択を変えない。
- 使えない項目は消さずに薄く出す(曲げていない線の「まっすぐに戻す」など)。
- 「接続を始める」は、接続モードにして、その機器のポート選択ポップアップ(7.2)を出す。
- 「ここにペースト」は、貼り付ける機器のまとまりの真ん中のアイコンの中心を、右クリックした位置に合わせる。続けて ⌘V で貼るときのずらす量には数えない。
- 右ボタンのドラッグは図を動かす操作なので、何も無い所では、動かさずに離したときだけメニューを出す。
- Esc・メニューの外のクリック・スクロールで閉じる。上下キーで項目を選び、Enter で実行する。
- 接続モードではメニューを出さず、作りかけの接続を取りやめる。
- キャンバスでは WebView の標準のメニューを出さない。キャンバスの外(入力欄など)では、標準のメニューのまま。
- 唯一の正(Single Source of Truth)はメモリ上のトポロジモデル(Zustand ストア)。
- GUI 操作 → モデル更新 → YAML 文字列を再生成 → エディタに反映。
- YAML 編集 → 300ms デバウンス → パース → 検証 → モデル更新 → キャンバス再描画。
- 更新の発生元(
gui/yaml/file)をタグ付けし、更新のループや、入力中のエディタ内容が上書きされることを防ぐ。
- ユーザーが書いたコメント・キー順は可能な限り保持する(
yamlの Document API で、既存ドキュメントに差分を当てる形で更新する)。 - 新規要素は末尾に追加する。
- IF 名は短縮形で出力する。
layoutは GUI でデバイスやラベルを動かしたとき・線を曲げたときに更新する(ドラッグ中は更新せず、ドロップ時に1回)。
- キャンバスは直前の正常なモデルを表示し続ける。
- エディタ上の該当行に赤線とエラーメッセージ。
- この状態で GUI 操作をした場合は、確認ダイアログ(「YAML にエラーがあります。GUI の内容で上書きしますか?」)を出す。
- elkjs の layered アルゴリズムを使用(上から下へ: cloud → router/firewall → l3switch → l2switch → server/hypervisor/pc/ap の順になるよう、role でレイヤーのヒントを与える)。
- ゲストは、ホストの枠の中にまとめて並べる(上からホスト・ブリッジ・ゲストの順)。ホストとゲストのかたまりは、ホストの段に置く。
- ファイル読み込み時、
layoutに座標が無いデバイスだけを自動配置する(既存の座標は動かさない)。- ホストに座標があるブリッジとゲストは、ホストの下に並べる(上からブリッジ・ゲストの段。同じ段にあれば、その右に続ける)。ほかの機器と重なったときは「自動整列」で直す。
- ツールバーの「自動整列」で全デバイスを再配置し、手で動かしたラベルも自動の位置に戻し、曲げた線もまっすぐに戻す(Undo 可能)。
- 新規 / 開く / 保存 / 名前を付けて保存。
- 未保存の変更がある場合はタイトルとステータスバーに表示し、閉じる・新規・開く操作の前に確認する。
- 最近使ったファイル(最大10件)をメニューに表示。
- 新規作成したファイルの先頭には、JSON Schema のコメント行(11章)を入れる。
- Proxmox VE から取り込む(4.7)。取り込んだ図は新規と同じく未保存の文書になる(最近使ったファイルには入れない)。
- ファイルを外部で変更された場合の検知はフェーズ2以降。
schema/constella.schema.json(JSON Schema)をリポジトリに置き、トポロジ YAML の仕様を機械可読にする。- 公開 URL:
https://raw.githubusercontent.com/stellarcielo/Constella/main/schema/constella.schema.json - ファイルの先頭に
# yaml-language-server: $schema=<公開 URL>を書くと、対応エディタ(VS Code の YAML 拡張機能など)で補完と検証が効く。
- 公開 URL:
examples/にサンプルトポロジを数個置く(小規模ラボ、2層スイッチ網、冗長構成+LAG、Proxmox VE)。- 将来: CLI(
constella validate <file>/constella render <file> -o out.svg)と MCP サーバーを提供し、AI から直接検証・描画できるようにする。そのため、モデル・パース・検証・レイアウトのロジックは React に依存しない純粋な TypeScript モジュールとして分離しておくこと。
- Tauri 2 + React + Vite のプロジェクト雛形、macOS でビルド・起動できる
- トポロジモデルの型定義、YAML パース/出力、IF 名正規化、バリデーション(Vitest でテスト)
- 組み込みモデルライブラリの読み込みとポート連番展開
- パレットからのデバイス配置、移動、削除
- ポート選択ポップアップによる接続
- フローティングエッジ + 両端 IF 名ラベル + 並列リンクのずらし表示
- インスペクタ(デバイスID/ラベル/モデル/IF一覧と使用状況、リンクの media/label)
- ファイルの新規/開く/保存
- 問題パネル
完了条件: 4.2 の例と同等のトポロジを GUI だけで作成して保存でき、保存した YAML を開き直すと同じ図が再現される。
- YAML エディタ(CodeMirror)とのリアルタイム双方向同期(コメント保持)
- Undo/Redo、コピー&ペースト
- elkjs による自動レイアウト
- SVG / PNG エクスポート
- JSON Schema と examples の整備
- 最近使ったファイル(最大10件)をメニューに表示
- 問題パネルの項目から YAML エディタの該当行へジャンプ
完了条件: YAML だけを手書き(座標なし)して開くと自動配置された図になり、GUI で動かすとコメントを残したまま YAML の layout が更新される。
- グループ(site / rack / vlan / subnet / as)の枠表示
- IF の IP / VLAN 情報をラベルに表示
- L1 / L2 / L3 表示切り替え
- ユーザーモデルの追加 UI
- macOS 版の配布準備(簡易署名・ユニバーサル版・アイコン・GitHub Releases)
- ハイパーバイザ(Proxmox VE など)の対応(4.6)
- 4-1 ゲストとホストの枠(role hypervisor、
host・guest・vmid、仮想化の組み込みモデル、ホストの枠、自動レイアウト、表示オプション「ゲスト」) - 4-2 ブリッジ(
type: bridge、ブリッジの箱、ゲストとのリンク、ブリッジの位置の保存) - 4-3 bond・VLAN インターフェース(
type: bond/vlan、ラベル) - 4-4 VLAN の通り道の検証
- 4-5 クラスタ(
kind: cluster、networks) - 4-6 SDN(ゾーン・VNet)
- 4-1 ゲストとホストの枠(role hypervisor、
- Proxmox VE からの取り込み(4.7)
- 5-1 書き出しスクリプトと、ノード・ネットワーク・ゲスト・リンクの取り込み
- 5-2 クラスタ(
kind: cluster、corosync・migration のネットワーク)と SDN の取り込み - API から直接取り込む(候補。URL と API トークンを入力する)
- IF 名ラベルを手で動かす・元に戻す(7.3)
- トランシーバ(光モジュール)を IF に書く・確かめる(5.4)
- 線を曲げる(7.3)
- ガイドとスナップ(7.4)
- モデルの管理画面(5.5)
- モデルのアイコンを置き換える(5.1)
- グループを GUI で作る・編集する(7.5)
- 無線のポートに何本でもつなぐ(4.3)・汎用モデルをフレキシブルにする(5.3)
- コピー&ペーストで、曲げた点・ラベルの位置・グループも引き継ぐ(7.4)
- 右クリックのメニュー(7.6)
- 等間隔のガイド(7.4)
- モデルとアイコンをまとめて書き出す・読み込む(5.5)
- Windows / Linux ビルドとインストーラ
- CLI / MCP サーバー
- LLDP / SNMP からの取り込み、containerlab / NetBox との連携
- シミュレーション機能
constella/
├── CLAUDE.md
├── docs/
│ └── SPEC.md
├── schema/
│ └── constella.schema.json
├── examples/
├── src-tauri/ # Rust(最小限)
└── src/
├── core/ # React 非依存の純粋ロジック
│ ├── types.ts
│ ├── parse.ts # YAML → モデル
│ ├── serialize.ts # モデル → YAML(Document 差分更新)
│ ├── validate.ts
│ ├── ifname.ts # IF 名の正規化・略称
│ ├── portExpand.ts # {1-24} の連番展開
│ └── layout.ts # elkjs ラッパ
├── library/
│ └── models/ # 組み込みモデル YAML
├── store/ # Zustand ストア、Undo 履歴
├── components/
│ ├── Canvas/ # React Flow、カスタムノード/エッジ
│ ├── Palette/
│ ├── Inspector/
│ ├── PortPicker/
│ ├── YamlEditor/
│ ├── ProblemsPanel/
│ └── Toolbar/
├── icons/ # ロール別アイコン(オリジナル SVG)
└── platform/ # Tauri API の薄いラッパ(OS 差分をここに閉じ込める)
- アイコンはオリジナルのシンプルな SVG を用意する。他社のアイコンセットはライセンス確認が必要なため同梱しない。
src/core/は Vitest で単体テストを書く(パース、出力、IF 名正規化、連番展開、バリデーション、コメント保持)。- 往復テスト:
examples/の各ファイルを「読み込み → 出力」して意味的に同一であることを確認する。 - UI は各フェーズの完了条件を手動確認チェックリストで確認する。