A VIP held exactly one address. It can now carry up to 19 additional IPv4
addresses (with prefix) in the same virtual_ipaddress block, on the same interface,
failing over together with the virtual IP.
- vip_instances.additional_ips (JSONB, default []); an empty list renders exactly
as before. Create/edit accept the list (omitted keeps, [] clears); it is part of
the applied snapshot and restored by Reject and undo.
- Every address write takes one advisory lock and checks the addresses against
node IPs and every other active VIP's primary and additional addresses.
- Adoption accepts an instance with several addresses: the ones after the first
become additional addresses when they render back exactly (IPv4, explicit
prefix, the instance's interface, no repeats); the peers must agree on them.
- UI: an "Additional addresses" list in the VIP form and a "+N" tag in the table.
v1.12.0: README, upgrade notes (delta migration, rolling-deploy and downgrade
rules, per-node live state), version files.
Fixes#73
A node could be a member of one VIP only: the form refused a second one, and the
delivery endpoint served each agent a single VIP's keepalived.conf.
- Delivery merges the node's applied VIPs into the one file it deploys: global_defs
once, then per VIP a vrrp_script chk_haproxy_<id> (each keeps its own failover
weight) and its vrrp_instance, ordered by VIP id. A node with one VIP gets the same
bytes and hash as before, so existing nodes do not rewrite or reload.
- One ack converges every VIP in the merged file; a deleted VIP's membership is
marked disabled once the node runs a file without it.
- A snapshot that cannot be merged leaves the node alone (status compose_error,
members show the reason). A database error during delivery answers "unavailable"
instead of "not_configured", which on a managed node meant teardown.
- Adoption refuses a keepalived.conf with several vrrp_instance blocks (taking it
over would remove the others), also for analyses stored by older versions and at
Apply for an adoption made before the upgrade. Discoveries record the parser
version; files analysed by an older one are re-reported once by v1.11.1+ agents.
- Schema 13 is a delta from 12 (two additive columns); built-in roles are not
re-seeded on a database already at 12.
No agent change.
The gate decides whether an operator's working keepalived.conf gets replaced,
so its rule is pinned directly: a supplied prefix resolves only the prefix
blocker, accepting data loss resolves only the loss blocker, and setting both
still cannot wave through an impossibility like an unknown VRID or an
unsupported auth_type.
The gate matches on substrings of the blocker prose the UI displays, which means
a reworded message would silently stop being waivable. One test therefore feeds
real parser output through it in both directions rather than hand-written
strings, so the message and the rule are checked together.
Also asserts that masking leaves no trace of a password containing spaces while
keeping the rest of the config readable, using the router's own regex so the
test breaks if it is ever loosened.
Groundwork for adopting a hand-maintained keepalived setup into HA/VIP
management. The page is empty today because the flow is one-way: VIPs are
declared in OpenManager and pushed to the node, and nothing reads what is
already there.
The heartbeat cannot drive adoption. It carries two keepalived facts -
keepalive_state (MASTER/BACKUP, best-effort from logs) and keepalive_ip (the
first address grepped out of virtual_ipaddress) - while render_keepalived_conf
needs eleven: virtual_router_id, auth_pass, interface, priority, prefix_length,
advert_int, unicast peers, track_haproxy, role, address and name. Guessing the
rest is not a cosmetic risk: a wrong VRID puts the nodes in separate VRRP
domains and a wrong auth_pass makes them reject each other, and either way both
nodes claim the VIP. So the config itself has to be read.
Extracting the fields is the easy half. Adoption REPLACES the operator's file
with our render, so anything their file contains that the renderer cannot
reproduce would be destroyed on takeover - a notify_master failover hook, an LVS
virtual_server section, a sync group, a second address in one instance, a custom
track_script. The parser therefore also returns everything it could not model,
and build_adoption_candidate turns each entry into a blocker with the file's own
line number. Values that are unknowable rather than unreproducible block too: a
missing virtual_router_id, and a missing prefix length, because our renderer
always writes an explicit prefix and picking one would silently change a live
VIP's netmask. keepalived's own documented defaults (state BACKUP, priority 100,
advert_int 1) are applied but reported in `defaulted`, so the UI can say which
values were assumed rather than read.
Handles the layout variation real files have: nested braces, `#` and `!`
comments, blocks opened and closed on one line, quoted script paths containing
spaces, and several vrrp_instance blocks in one file.
A parse result carries auth_pass in cleartext, since that is the only way to
re-render an identical config, so it must never be logged - noted on every
function that returns one.
Tests pin each blocker and the layout variants, and include the invariant that
keeps the parser honest: a config the renderer itself produced must parse back
with zero blockers, so adding a directive to render_keepalived_conf without
teaching the parser fails the suite instead of making OpenManager's own output
look unadoptable. Verified by mutation - eight deliberate weakenings of the
safety checks are each caught by at least one test.
No endpoint, no schema change and no agent change yet; nothing calls this.