// Package nodesync replicates the system configuration of one PVE node (the // master) to other nodes (the targets), of the same cluster or not. It is the // second replication mode of pve-confsync, next to the cluster to cluster one // (package engine). What the PVE API covers goes through the API: the network // interfaces (/nodes/{node}/network, written in interfaces.new then reloaded) // and the DNS servers (/nodes/{node}/dns). The other files go through SSH as // root on the node: multipath, chrony, sysctl, interfaces.d. Same pipeline as // the engine: read, normalize, diff, plan, apply with backup, reload, verify. package nodesync import "fmt" // ItemKind tells how an item is read and written. type ItemKind string const ( KindNetwork ItemKind = "network" // API /nodes/{node}/network KindDNS ItemKind = "dns" // API /nodes/{node}/dns KindFile ItemKind = "file" // files over SSH ) // Item is one replicable element. type Item struct { ID string `json:"id"` Kind ItemKind `json:"kind"` // Files: paths or glob patterns read on the nodes, and paths never touched. Paths []string `json:"paths,omitempty"` Exclude []string `json:"exclude,omitempty"` // Glob items hold several files: a file only on the target is kept, or // deleted with the prune option. Single file items are never deleted. Glob bool `json:"glob,omitempty"` // Additive files (multipath wwids) only get the lines of the master they // miss: nothing is removed from the target. Additive bool `json:"additive,omitempty"` // Reload is the command run on the node after a write (empty: none). Reload string `json:"reload,omitempty"` // Network items reload the network of the node: typed confirmation, // automatic restore when the node is unreachable afterwards. Network bool `json:"network,omitempty"` } // Items is the catalog, in apply order (reloads follow this order, the // network last). var Items = []Item{ {ID: "sysctl", Kind: KindFile, Paths: []string{"/etc/sysctl.d/*.conf"}, Glob: true, Reload: "sysctl --system"}, {ID: "chrony", Kind: KindFile, Paths: []string{"/etc/chrony/chrony.conf", "/etc/chrony/sources.d/*.sources"}, Glob: true, Reload: "systemctl restart chrony"}, {ID: "multipath", Kind: KindFile, Paths: []string{"/etc/multipath.conf"}, Reload: "multipathd reconfigure"}, {ID: "wwids", Kind: KindFile, Paths: []string{"/etc/multipath/wwids"}, Additive: true, Reload: "multipathd reconfigure"}, {ID: "dns", Kind: KindDNS}, // interfaces.d/sdn is generated by the SDN of PVE: never replicated {ID: "interfaces-d", Kind: KindFile, Paths: []string{"/etc/network/interfaces.d/*"}, Exclude: []string{"/etc/network/interfaces.d/sdn"}, Glob: true, Reload: "ifreload -a", Network: true}, {ID: "network", Kind: KindNetwork, Network: true}, } // ItemByID returns an item of the catalog. func ItemByID(id string) (Item, error) { for _, it := range Items { if it.ID == id { return it, nil } } return Item{}, fmt.Errorf("unknown item %q", id) } func itemRank(id string) int { for i, it := range Items { if it.ID == id { return i } } return len(Items) } // Policy of a field of an API item (network, dns). type Policy string const ( Sync Policy = "sync" // copied from the master Ignore Policy = "ignore" // never written Override Policy = "override" // only written from the target mapping ) // defaultPolicy: the addresses and gateways are those of each node; they are // only written from the mapping of a target. func defaultPolicy(item, field string) Policy { if item == "network" { switch field { case "cidr", "cidr6", "gateway", "gateway6": return Override } } return Sync } // DefaultPolicies lists the fields whose default is not "sync", for the UI. var DefaultPolicies = map[string]map[string]Policy{ "network": {"cidr": Override, "cidr6": Override, "gateway": Override, "gateway6": Override}, }