// Package demo starts fake PVE clusters (pvetest: API and SSH) and fills a // database with them, to try the web UI without a Proxmox cluster: a primary, // two targets with differences (missing objects, different fields, accounts // and passwords, a missing mapping) and a PVE 8 cluster that is refused. package demo import ( "context" "crypto/sha256" "encoding/hex" "pve-confsync/internal/adapter" "pve-confsync/internal/model" "pve-confsync/internal/pve" "pve-confsync/internal/pve/pvetest" "pve-confsync/internal/store" ) // Password of the demo accounts admin, operateur and lecteur. const Password = "confsync-demo" // Clusters are the fake clusters; Close stops them. type Clusters struct{ servers []*pvetest.Server } // Close stops the fake clusters. func (c *Clusters) Close() { for _, s := range c.servers { s.Close() } } // Start starts the fake clusters and records them (and the demo accounts) in st. func Start(ctx context.Context, st *store.Store) (*Clusters, error) { ref := pvetest.New("9.1.4") ref.Options = map[string]string{ "registered-tags": "backup;dev;prod", "tag-style": "color-map=prod:c0392b:ffffff;dev:2980b9:ffffff,ordering=config,shape=full", "next-id": "lower=100,upper=999999", "user-tag-access": "user-allow=list,user-allow-list=team-a;team-b", "keyboard": "fr", } ref.Pending.Zones["vlanz"] = map[string]any{"type": "vlan", "bridge": "vmbr0", "mtu": 1500, "ipam": "pve"} ref.Pending.Zones["stor"] = map[string]any{"type": "vlan", "bridge": "vmbr2", "mtu": 9000, "ipam": "pve"} ref.Pending.Zones["vxz"] = map[string]any{"type": "vxlan", "peers": "10.0.0.1,10.0.0.2", "mtu": 1450, "ipam": "pve"} ref.Pending.Zones["evpnz"] = map[string]any{"type": "evpn", "controller": "evpnctl", "vrf-vxlan": 4000} ref.Pending.VNets["v100"] = map[string]any{"zone": "vlanz", "tag": 100, "alias": "prod"} ref.Pending.VNets["v200"] = map[string]any{"zone": "vlanz", "tag": 200, "alias": "dev"} ref.Pending.VNets["ceph"] = map[string]any{"zone": "stor", "tag": 50, "alias": "ceph public"} ref.Pending.VNets["vx1"] = map[string]any{"zone": "vxz", "tag": 10001, "alias": "overlay"} ref.Pending.Subnets["vlanz-10.1.0.0-24"] = map[string]any{"vnet": "v100", "gateway": "10.1.0.1", "dhcp-range": []any{"start-address=10.1.0.100,end-address=10.1.0.200"}} ref.Pending.Subnets["vlanz-10.2.0.0-24"] = map[string]any{"vnet": "v200", "gateway": "10.2.0.1"} ref.Commit() ref.Groups["admins"] = map[string]any{"comment": "Administrateurs"} ref.Groups["ops"] = map[string]any{"comment": "Exploitation"} ref.Roles["ConfSyncOps"] = "Sys.Audit,VM.Audit,VM.PowerMgmt" ref.Users["alice@pve"] = map[string]any{"enable": 1, "firstname": "Alice", "email": "alice@example.org", "groups": "admins"} ref.Users["bob@pve"] = map[string]any{"enable": 1, "firstname": "Bob", "email": "bob@example.org", "groups": "ops", "expire": 1893456000} ref.Users["carol@pam"] = map[string]any{"enable": 1, "comment": "compte Linux, non répliqué"} ref.Shadow["alice"] = "$5$Kd1Xa9$Q8yJdemoAliceHashOnlyForTheDemo0123456789AB" ref.Shadow["bob"] = "$5$Pq7Zr2$W3vLdemoBobHashOnlyForTheDemo0123456789ABCDE" // site-a: tags and an MTU behind, the VXLAN zone and the dev network missing a := pvetest.New("9.0.10") a.Options = map[string]string{ "registered-tags": "dev;prod", "tag-style": "color-map=prod:c0392b:ffffff;dev:2980b9:ffffff,ordering=config,shape=full", "next-id": "lower=2000,upper=2999", "keyboard": "en-us", } a.Pending.Zones["vlanz"] = map[string]any{"type": "vlan", "bridge": "vmbr1", "mtu": 9000, "ipam": "pve"} a.Pending.Zones["stor"] = map[string]any{"type": "vlan", "bridge": "vmbr2", "mtu": 9000, "ipam": "pve", "nodes": "pve-a1,pve-a2"} a.Pending.VNets["v100"] = map[string]any{"zone": "vlanz", "tag": 100, "alias": "production"} a.Pending.VNets["ceph"] = map[string]any{"zone": "stor", "tag": 50, "alias": "ceph public"} a.Pending.Subnets["vlanz-10.1.0.0-24"] = map[string]any{"vnet": "v100", "gateway": "10.1.0.254"} a.Commit() a.Groups["admins"] = map[string]any{"comment": "Admins"} a.Users["alice@pve"] = map[string]any{"enable": 1, "firstname": "Alice", "email": "alice@old.example.org", "groups": "admins"} a.Shadow["alice"] = "$5$OldOld$oldAliceHashOnSiteA0123456789ABCDEFGHIJKLMNOP" // site-b: nearly in line, but no mapping for the bridge of the "stor" zone // (blocking error) and a local zone absent from the reference (kept, no prune) b := pvetest.New("9.1.2") b.Options = map[string]string{ "registered-tags": "backup;dev;prod", "tag-style": "color-map=prod:c0392b:ffffff;dev:2980b9:ffffff,ordering=config,shape=full", "next-id": "lower=100,upper=999999", "user-tag-access": "user-allow=list,user-allow-list=team-a;team-b", } b.Pending.Zones["vlanz"] = map[string]any{"type": "vlan", "bridge": "vmbr0", "mtu": 1500, "ipam": "pve"} b.Pending.Zones["vxz"] = map[string]any{"type": "vxlan", "peers": "172.16.0.1,172.16.0.2", "mtu": 1450, "ipam": "pve"} b.Pending.Zones["labz"] = map[string]any{"type": "vlan", "bridge": "vmbr9", "ipam": "pve"} b.Pending.VNets["v100"] = map[string]any{"zone": "vlanz", "tag": 100, "alias": "prod"} b.Pending.VNets["v200"] = map[string]any{"zone": "vlanz", "tag": 200, "alias": "dev"} b.Pending.Subnets["vlanz-10.1.0.0-24"] = map[string]any{"vnet": "v100", "gateway": "10.1.0.1", "dhcp-range": []any{"start-address=10.1.0.100,end-address=10.1.0.200"}} b.Pending.Subnets["vlanz-10.2.0.0-24"] = map[string]any{"vnet": "v200", "gateway": "10.2.0.1"} b.Commit() b.Groups["admins"] = map[string]any{"comment": "Administrateurs"} b.Groups["ops"] = map[string]any{"comment": "Exploitation"} b.Roles["ConfSyncOps"] = "Sys.Audit,VM.Audit,VM.PowerMgmt" b.Users["alice@pve"] = map[string]any{"enable": 1, "firstname": "Alice", "email": "alice@example.org", "groups": "admins"} b.Users["bob@pve"] = map[string]any{"enable": 1, "firstname": "Bob", "email": "bob@example.org", "groups": "ops", "expire": 1893456000} b.Shadow["alice"] = ref.Shadow["alice"] b.Shadow["bob"] = ref.Shadow["bob"] // legacy: PVE 8, refused by the version check old := pvetest.New("8.4.1") c := &Clusters{servers: []*pvetest.Server{ref, a, b, old}} save := func(s *pvetest.Server, name, role string, pos int, mapping map[model.Kind]map[string]map[string]any, pol map[model.Kind]map[string]adapter.Policy) error { sum := sha256.Sum256(s.Certificate().Raw) host, port := s.SSHAddr() err := st.SaveCluster(ctx, "", store.Cluster{Name: name, Role: role, Position: pos, URL: s.URL, User: "root@pam", Password: pvetest.Password, TLS: store.TLSPin, Fingerprint: pve.FormatFingerprint(hex.EncodeToString(sum[:])), SSHHost: host, SSHPort: port, Mapping: mapping, Policies: pol}) if err != nil { return err } return st.Pin(ctx, name, "", s.SSHHostKey(), nil) } steps := []error{ save(ref, "pve-rennes", store.ClusterPrimary, 0, nil, nil), save(a, "site-a", store.ClusterTarget, 1, map[model.Kind]map[string]map[string]any{ model.KindDatacenter: {"datacenter": {"next-id": "lower=2000,upper=2999"}}, model.KindZone: { "vlanz": {"bridge": "vmbr1"}, "stor": {"bridge": "vmbr2", "nodes": "pve-a1,pve-a2"}, "vxz": {"peers": "192.168.10.1,192.168.10.2"}, }, }, nil), save(b, "site-b", store.ClusterTarget, 2, map[model.Kind]map[string]map[string]any{ model.KindZone: { "vlanz": {"bridge": "vmbr0"}, "vxz": {"peers": "172.16.0.1,172.16.0.2"}, }, }, map[model.Kind]map[string]adapter.Policy{model.KindZone: {"mtu": adapter.Ignore}}), save(old, "legacy-pve8", store.ClusterTarget, 3, nil, nil), st.SaveSettings(ctx, store.Settings{Kinds: model.Kinds, Policies: map[model.Kind]map[string]adapter.Policy{model.KindDatacenter: {"keyboard": adapter.Sync}}}), } for _, u := range []struct{ name, role string }{{"admin", store.RoleAdmin}, {"operateur", store.RoleOperator}, {"lecteur", store.RoleViewer}} { steps = append(steps, st.SaveUser(ctx, u.name, u.role, Password)) } for _, err := range steps { if err != nil { c.Close() return nil, err } } return c, nil }