Madagascar Local Authority folosește SQLite ca sursă de adevăr runtime pentru hosturi, aliasuri, vhosturi, Work Orders, workeri de date și certificate.
Locația implicită în checkout:
var/host-manager.sqlite
Locația runtime pe jumper:
/usr/local/xdev-host-manager/var/host-manager.sqlite
Path-ul poate fi schimbat cu:
HOST_MANAGER_DB=/path/to/host-manager.sqlite
Hosturile sunt identificate prin FQDN complet, nu prin short name. Exemplu: gw.local și gw.remote sunt identități diferite. Coloana compatibilă legacy_id păstrează ID-ul scurt folosit de UI-ul curent, dar cheia reală este hosts.fqdn.
Schema evită să transforme hosts într-un tabel cu prea multe coloane. Datele specializate sunt în tabele separate:
host_aliaseshost_roleshost_sourceshost_flagshost_sshvhostscertificates, certificate_dns_namesdata_workers, dhcp_leases, mdns_observationsdocuments rămâne doar tabel legacy pentru migrarea din modelul vechi document-store. Aplicația nu îl mai folosește ca sursă de adevăr.
Schema curentă este versiunea 2.
schema_meta('schema_version') = '2'
schema_meta păstrează și metadate runtime precum registry_updated_at.
| Tabel | Rol |
|---|---|
schema_meta |
metadate de schemă/runtime |
documents |
document-store legacy pentru migrare |
hosts |
hosturi canonice, identificate prin FQDN |
host_aliases |
aliasuri păstrate inclusiv după retragere |
host_roles |
roluri active/retrase per host |
host_sources |
surse active/retrase per host |
host_flags |
flaguri extensibile per host |
host_ssh |
profile SSH per host |
vhosts |
vhosturi mutabile între hosturi |
data_workers |
workeri/surse care colectează date |
dhcp_leases |
date observate din DHCP lease/reservation |
mdns_observations |
date observate din mDNS |
certificates |
certificate emise de CA locală |
certificate_dns_names |
SAN DNS names pentru certificate |
work_orders |
Work Orders |
work_order_checklist |
checklist items pentru Work Orders |
work_order_actions |
acțiuni confirmabile pentru Work Orders |
hostsCREATE TABLE hosts (
fqdn TEXT PRIMARY KEY,
legacy_id TEXT NOT NULL UNIQUE,
status TEXT NOT NULL DEFAULT 'active',
hosts_ip TEXT NOT NULL DEFAULT '',
dns_ip TEXT NOT NULL DEFAULT '',
monitoring TEXT NOT NULL DEFAULT 'pending',
notes TEXT NOT NULL DEFAULT '',
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL
);
Chei și indexuri:
hosts(fqdn)hosts(legacy_id)host_aliasesCREATE TABLE host_aliases (
alias_name TEXT NOT NULL,
host_fqdn TEXT NOT NULL REFERENCES hosts(fqdn)
ON UPDATE CASCADE ON DELETE RESTRICT,
alias_kind TEXT NOT NULL DEFAULT 'declared',
status TEXT NOT NULL DEFAULT 'active',
is_dns_published INTEGER NOT NULL DEFAULT 1,
created_at TEXT NOT NULL,
retired_at TEXT,
notes TEXT NOT NULL DEFAULT '',
PRIMARY KEY (alias_name, host_fqdn)
);
Indexuri:
CREATE UNIQUE INDEX idx_host_aliases_active_name
ON host_aliases(alias_name)
WHERE status = 'active';
CREATE INDEX idx_host_aliases_host_status
ON host_aliases(host_fqdn, status);
Reguli:
status = 'retired'baobab, sunt păstrate cu alias_kind = 'derived'pmx.baobab, sunt păstrate cu alias_kind = 'derived-vhost'vhostsCREATE TABLE vhosts (
vhost_fqdn TEXT PRIMARY KEY,
host_fqdn TEXT NOT NULL REFERENCES hosts(fqdn)
ON UPDATE CASCADE ON DELETE RESTRICT,
status TEXT NOT NULL DEFAULT 'active',
service_name TEXT NOT NULL DEFAULT '',
upstream_url TEXT NOT NULL DEFAULT '',
tls_mode TEXT NOT NULL DEFAULT 'local-ca',
certificate_id TEXT REFERENCES certificates(certificate_id)
ON UPDATE CASCADE ON DELETE SET NULL,
notes TEXT NOT NULL DEFAULT '',
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL
);
Indexuri:
CREATE INDEX idx_vhosts_host_status
ON vhosts(host_fqdn, status);
Un vhost se mută de pe un host pe altul prin update pe vhosts.host_fqdn. Vhosturile retrase rămân în tabel cu status = 'retired'.
host_roles, host_sources, host_flags, host_sshCREATE TABLE host_roles (
host_fqdn TEXT NOT NULL REFERENCES hosts(fqdn)
ON UPDATE CASCADE ON DELETE RESTRICT,
role TEXT NOT NULL,
status TEXT NOT NULL DEFAULT 'active',
created_at TEXT NOT NULL,
retired_at TEXT,
PRIMARY KEY (host_fqdn, role)
);
CREATE TABLE host_sources (
host_fqdn TEXT NOT NULL REFERENCES hosts(fqdn)
ON UPDATE CASCADE ON DELETE RESTRICT,
source TEXT NOT NULL,
status TEXT NOT NULL DEFAULT 'active',
created_at TEXT NOT NULL,
retired_at TEXT,
PRIMARY KEY (host_fqdn, source)
);
CREATE TABLE host_flags (
host_fqdn TEXT NOT NULL REFERENCES hosts(fqdn)
ON UPDATE CASCADE ON DELETE RESTRICT,
flag TEXT NOT NULL,
value TEXT NOT NULL DEFAULT '1',
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL,
PRIMARY KEY (host_fqdn, flag)
);
CREATE TABLE host_ssh (
host_fqdn TEXT NOT NULL REFERENCES hosts(fqdn)
ON UPDATE CASCADE ON DELETE RESTRICT,
profile_name TEXT NOT NULL DEFAULT 'default',
username TEXT NOT NULL DEFAULT '',
port INTEGER NOT NULL DEFAULT 22,
identity_file TEXT NOT NULL DEFAULT '',
address TEXT NOT NULL DEFAULT '',
local_forward_host TEXT NOT NULL DEFAULT '',
local_forward_port INTEGER,
remote_forward_host TEXT NOT NULL DEFAULT '',
remote_forward_port INTEGER,
notes TEXT NOT NULL DEFAULT '',
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL,
PRIMARY KEY (host_fqdn, profile_name)
);
data_workersCREATE TABLE data_workers (
worker_id TEXT PRIMARY KEY,
worker_type TEXT NOT NULL,
name TEXT NOT NULL DEFAULT '',
status TEXT NOT NULL DEFAULT 'active',
source TEXT NOT NULL DEFAULT '',
last_run_at TEXT,
notes TEXT NOT NULL DEFAULT '',
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL
);
Indexuri:
CREATE INDEX idx_data_workers_type_status
ON data_workers(worker_type, status);
Seed implicit:
dhcp-router, type dhcpmdns-listener, type mdnsdhcp_leasesCREATE TABLE dhcp_leases (
lease_key TEXT PRIMARY KEY,
worker_id TEXT NOT NULL REFERENCES data_workers(worker_id)
ON UPDATE CASCADE ON DELETE RESTRICT,
host_fqdn TEXT REFERENCES hosts(fqdn)
ON UPDATE CASCADE ON DELETE SET NULL,
observed_name TEXT NOT NULL DEFAULT '',
ip_address TEXT NOT NULL,
mac_address TEXT NOT NULL DEFAULT '',
lease_state TEXT NOT NULL DEFAULT '',
first_seen TEXT NOT NULL,
last_seen TEXT NOT NULL,
raw TEXT NOT NULL DEFAULT ''
);
Indexuri:
CREATE INDEX idx_dhcp_leases_ip ON dhcp_leases(ip_address);
CREATE INDEX idx_dhcp_leases_mac ON dhcp_leases(mac_address);
CREATE INDEX idx_dhcp_leases_worker_last_seen
ON dhcp_leases(worker_id, last_seen);
mdns_observationsCREATE TABLE mdns_observations (
observation_key TEXT PRIMARY KEY,
worker_id TEXT NOT NULL REFERENCES data_workers(worker_id)
ON UPDATE CASCADE ON DELETE RESTRICT,
host_fqdn TEXT REFERENCES hosts(fqdn)
ON UPDATE CASCADE ON DELETE SET NULL,
observed_name TEXT NOT NULL,
ip_address TEXT NOT NULL,
rr_type TEXT NOT NULL DEFAULT 'A',
ttl INTEGER NOT NULL DEFAULT 0,
first_seen TEXT NOT NULL,
last_seen TEXT NOT NULL,
seen_count INTEGER NOT NULL DEFAULT 1,
last_peer TEXT NOT NULL DEFAULT '',
raw TEXT NOT NULL DEFAULT ''
);
Indexuri:
CREATE INDEX idx_mdns_observations_name ON mdns_observations(observed_name);
CREATE INDEX idx_mdns_observations_ip ON mdns_observations(ip_address);
CREATE INDEX idx_mdns_observations_worker_last_seen
ON mdns_observations(worker_id, last_seen);
certificates și certificate_dns_namesCREATE TABLE certificates (
certificate_id TEXT PRIMARY KEY,
host_fqdn TEXT REFERENCES hosts(fqdn)
ON UPDATE CASCADE ON DELETE SET NULL,
common_name TEXT NOT NULL DEFAULT '',
subject TEXT NOT NULL DEFAULT '',
issuer TEXT NOT NULL DEFAULT '',
serial TEXT UNIQUE,
status TEXT NOT NULL DEFAULT 'issued',
not_before TEXT NOT NULL DEFAULT '',
not_after TEXT NOT NULL DEFAULT '',
fingerprint_sha256 TEXT UNIQUE,
cert_path TEXT NOT NULL DEFAULT '',
csr_path TEXT NOT NULL DEFAULT '',
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL,
notes TEXT NOT NULL DEFAULT ''
);
CREATE TABLE certificate_dns_names (
certificate_id TEXT NOT NULL REFERENCES certificates(certificate_id)
ON UPDATE CASCADE ON DELETE CASCADE,
dns_name TEXT NOT NULL,
PRIMARY KEY (certificate_id, dns_name)
);
Indexuri:
CREATE INDEX idx_certificate_dns_names_dns_name
ON certificate_dns_names(dns_name);
Certificatele emise sunt sincronizate când aplicația citește lista CA prin ca_manager.sh list-json.
CREATE TABLE work_orders (
id TEXT PRIMARY KEY,
status TEXT NOT NULL DEFAULT 'pending',
title TEXT NOT NULL DEFAULT '',
reason TEXT NOT NULL DEFAULT '',
created_at TEXT NOT NULL,
confirmed_at TEXT NOT NULL DEFAULT '',
result TEXT NOT NULL DEFAULT '',
updated_at TEXT NOT NULL
);
CREATE TABLE work_order_checklist (
work_order_id TEXT NOT NULL REFERENCES work_orders(id)
ON UPDATE CASCADE ON DELETE CASCADE,
item_id TEXT NOT NULL,
text TEXT NOT NULL DEFAULT '',
status TEXT NOT NULL DEFAULT 'pending',
owner TEXT NOT NULL DEFAULT '',
notes TEXT NOT NULL DEFAULT '',
updated_at TEXT NOT NULL DEFAULT '',
PRIMARY KEY (work_order_id, item_id)
);
CREATE TABLE work_order_actions (
work_order_id TEXT NOT NULL REFERENCES work_orders(id)
ON UPDATE CASCADE ON DELETE CASCADE,
position INTEGER NOT NULL,
type TEXT NOT NULL,
host_fqdn TEXT REFERENCES hosts(fqdn)
ON UPDATE CASCADE ON DELETE SET NULL,
host_legacy_id TEXT NOT NULL DEFAULT '',
name TEXT NOT NULL DEFAULT '',
payload TEXT NOT NULL DEFAULT '',
PRIMARY KEY (work_order_id, position)
);
La prima inițializare a unei baze fără rânduri în hosts, aplicația seed-uiește din:
documents.name = 'hosts_yaml', dacă există din vechiul modelconfig/hosts.yaml, dacă documentul legacy lipseșteWork Orders se seed-uiesc similar din documents.name = 'work_orders_yaml' sau config/work-orders.yaml.
Seed-ul curent produce:
hosts.*, pmx.* și pbs.*dhcp-router și mdns-listenerconfig/local-hosts.tsv rămâne manifest generat explicit din tabelele runtime.
Pe jumper:
cd /usr/local/xdev-host-manager
sqlite3 var/host-manager.sqlite '.tables'
sqlite3 var/host-manager.sqlite '.schema hosts'
sqlite3 var/host-manager.sqlite '.schema host_aliases'
sqlite3 var/host-manager.sqlite '.schema vhosts'
sqlite3 var/host-manager.sqlite 'pragma foreign_key_list(vhosts);'
sqlite3 var/host-manager.sqlite 'pragma index_list(host_aliases);'
sqlite3 var/host-manager.sqlite 'select fqdn, legacy_id, status, dns_ip from hosts order by legacy_id;'
sqlite3 var/host-manager.sqlite 'select alias_name, host_fqdn, alias_kind, status from host_aliases order by alias_name;'
sqlite3 var/host-manager.sqlite 'select vhost_fqdn, host_fqdn, status from vhosts order by vhost_fqdn;'
Backup recomandat:
cd /usr/local/xdev-host-manager
sqlite3 var/host-manager.sqlite ".backup 'backups/host-manager/host-manager.sqlite.$(date +%Y%m%d_%H%M%S).bak'"
Cu WAL activ, o copie brută trebuie să trateze fișierele ca set coerent:
var/host-manager.sqlite
var/host-manager.sqlite-wal
var/host-manager.sqlite-shm
Restore-ul înlocuiește sursa de adevăr runtime:
sudo systemctl stop host-manager
sudo cp backups/host-manager/host-manager.sqlite.YYYYMMDD_HHMMSS.bak var/host-manager.sqlite
sudo chown host-manager:host-manager var/host-manager.sqlite
sudo systemctl start host-manager
curl -fsS http://127.0.0.1:8088/healthz >/dev/null