mirror of
https://dev.lirent.ru/Vatrog/vm-automation-signaling.git
synced 2026-06-25 20:36:36 +03:00
9bde398b6c
- core: runtime attach/detach of a per-endpoint adapter trio (runtime-safe add_adapter + vmsig_core_detach_endpoint, deferred reap) - roster: VMSIG_EV_ROSTER + CAP_ROSTER, retained per-endpoint and replayed to late subscribers - discovery: inotify trigger dir, vmid/endpoint slot allocator, host probe; vmsigd daemon with config + per-uid admission - input driver and vgpu perception built in-tree; vgpu perception as a separate library - memctx: own the supplied ro_fd (closed at detach) - deb packaging: install rules, systemd unit, tmpfiles, default config
199 lines
8.0 KiB
C
199 lines
8.0 KiB
C
/* test_discovery.c — discovery state machine (WS3), driven deterministically via the TEST
|
|
* hooks (no inotify/timer/threads). A fake host-probe controls config/live verdicts; a
|
|
* recording sink captures attach/detach; a CAP_ROSTER subscriber captures the published
|
|
* roster. Covers: appear->attach(slot+roster), duplicate, gone->detach(roster+free), bit
|
|
* reuse, config-fail drop, stale drop, and the retry-then-attach path. */
|
|
#define _GNU_SOURCE
|
|
#include "vmsig.h"
|
|
#include "vmsig_roster.h"
|
|
#include "discovery.h" /* pulls host_probe.h */
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <stdint.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <fcntl.h>
|
|
|
|
static int g_fail = 0;
|
|
#define CHECK(cond, msg) do { if (!(cond)) { printf(" FAIL: %s\n", (msg)); g_fail = 1; } } while (0)
|
|
|
|
/* ---- fake host-probe ---- */
|
|
typedef struct { int config_ok; int live_mode; int live_calls; } fakeprobe;
|
|
/* live_mode: 0=ok, 1=stale(dead, no retry), 2=retry-once-then-ok */
|
|
|
|
static int fp_config(const vmsig_host_probe* p, uint32_t vmid, vmsig_host_facts* out) {
|
|
fakeprobe* f = p->ud;
|
|
memset(out, 0, sizeof *out);
|
|
out->vmid = vmid;
|
|
snprintf(out->name, sizeof out->name, "win-%u", vmid);
|
|
snprintf(out->ram_path, sizeof out->ram_path, "/tmp/vm-%u-ram", vmid);
|
|
snprintf(out->qmp_path, sizeof out->qmp_path, "/tmp/%u.qmp", vmid);
|
|
out->cfg_ram_bytes = 4ull << 30;
|
|
out->share_on = f->config_ok;
|
|
out->ok = f->config_ok;
|
|
return 0;
|
|
}
|
|
static int fp_live(const vmsig_host_probe* p, vmsig_host_facts* io) {
|
|
fakeprobe* f = p->ud;
|
|
io->retry = 0;
|
|
f->live_calls++;
|
|
if (f->live_mode == 1) { io->ok = 0; io->vm_state = VMSIG_VM_SHUTDOWN; return 0; }
|
|
if (f->live_mode == 2 && f->live_calls == 1) { io->retry = 1; io->ok = 0; return 0; }
|
|
io->ok = 1; io->vm_state = VMSIG_VM_RUNNING; io->low = 0x80000000ull;
|
|
return 0;
|
|
}
|
|
|
|
/* ---- recording sink ---- */
|
|
typedef struct {
|
|
int n_attach, n_detach;
|
|
uint32_t la_vmid, la_ep, ld_vmid, ld_ep;
|
|
} recsink;
|
|
static int rs_attach(void* ud, vmsig_core* core, uint32_t vmid, uint32_t ep,
|
|
const vmsig_host_facts* f) {
|
|
(void)core; (void)f;
|
|
recsink* s = ud; s->n_attach++; s->la_vmid = vmid; s->la_ep = ep;
|
|
return 0;
|
|
}
|
|
static void rs_detach(void* ud, vmsig_core* core, uint32_t vmid, uint32_t ep) {
|
|
(void)core;
|
|
recsink* s = ud; s->n_detach++; s->ld_vmid = vmid; s->ld_ep = ep;
|
|
}
|
|
|
|
/* ---- roster subscriber ---- */
|
|
typedef struct { int attach, detach; uint32_t last_vmid, last_ep, last_action; char last_name[32]; } robs;
|
|
static int rob_on_ev(void* u, const vmsig_event* ev) {
|
|
robs* r = u;
|
|
if (ev->kind != VMSIG_EV_ROSTER) return 0;
|
|
const vmsig_roster* e = (const vmsig_roster*)ev->inln;
|
|
r->last_vmid = e->vmid; r->last_ep = ev->endpoint; r->last_action = e->action;
|
|
snprintf(r->last_name, sizeof r->last_name, "%s", e->name);
|
|
if (e->action == VMSIG_ROSTER_ATTACH) r->attach++;
|
|
else if (e->action == VMSIG_ROSTER_DETACH) r->detach++;
|
|
return 0;
|
|
}
|
|
|
|
static void test_discovery(void) {
|
|
printf("test_discovery\n");
|
|
vmsig_ctx* ctx = vmsig_ctx_new();
|
|
vmsig_core* core = vmsig_core_new(ctx);
|
|
|
|
robs ro; memset(&ro, 0, sizeof ro);
|
|
vmsig_inproc_cfg cfg; memset(&cfg, 0, sizeof cfg);
|
|
cfg.on_event = rob_on_ev; cfg.user = &ro;
|
|
void* ctl = vmsig_inproc_control_new(&cfg);
|
|
vmsig_grant g; memset(&g, 0, sizeof g);
|
|
g.principal = 1; g.endpoint_mask = ~0ull; g.source_mask = 0xFFFFFFFFu; g.cap_mask = VMSIG_CAP_ROSTER;
|
|
vmsig_core_add_control(core, vmsig_inproc_control_ops(), ctl, &g);
|
|
|
|
fakeprobe fp; memset(&fp, 0, sizeof fp); fp.config_ok = 1; fp.live_mode = 0;
|
|
vmsig_host_probe probe = { fp_config, fp_live, &fp };
|
|
recsink rs; memset(&rs, 0, sizeof rs);
|
|
vmsig_discovery_sink sink = { rs_attach, rs_detach, &rs };
|
|
|
|
char dir[] = "/tmp/vmsig_disc.XXXXXX";
|
|
CHECK(mkdtemp(dir) != NULL, "temp watch dir created");
|
|
vmsig_discovery* d = vmsig_discovery_new(core, dir, NULL, NULL, NULL, &probe, &sink);
|
|
CHECK(d != NULL, "discovery created");
|
|
|
|
/* 1) appear 101 -> attach ep0 + roster ATTACH */
|
|
vmsig_discovery_feed(d, 101, 1);
|
|
CHECK(rs.n_attach == 1 && rs.la_vmid == 101 && rs.la_ep == 0, "101 attached on ep0 (sink)");
|
|
CHECK(ro.attach == 1 && ro.last_vmid == 101 && ro.last_ep == 0 &&
|
|
ro.last_action == VMSIG_ROSTER_ATTACH, "roster ATTACH 101 ep0");
|
|
CHECK(strcmp(ro.last_name, "win-101") == 0, "roster carried the VM name");
|
|
CHECK(vmsig_discovery_slot_of_vmid(d, 101) == 0, "slot_of_vmid(101)==0");
|
|
|
|
/* 2) appear 102 -> ep1 */
|
|
vmsig_discovery_feed(d, 102, 1);
|
|
CHECK(rs.n_attach == 2 && rs.la_vmid == 102 && rs.la_ep == 1, "102 attached on ep1");
|
|
|
|
/* duplicate appear 101 -> ignored */
|
|
vmsig_discovery_feed(d, 101, 1);
|
|
CHECK(rs.n_attach == 2, "duplicate appear ignored");
|
|
|
|
/* 3) gone 101 -> detach + roster DETACH + slot freed */
|
|
vmsig_discovery_feed(d, 101, 0);
|
|
CHECK(rs.n_detach == 1 && rs.ld_vmid == 101 && rs.ld_ep == 0, "101 detached (sink)");
|
|
CHECK(ro.detach == 1 && ro.last_action == VMSIG_ROSTER_DETACH && ro.last_vmid == 101,
|
|
"roster DETACH 101");
|
|
CHECK(vmsig_discovery_slot_of_vmid(d, 101) == -1, "slot freed after detach");
|
|
|
|
/* 4) appear 103 -> reuse freed ep0 */
|
|
vmsig_discovery_feed(d, 103, 1);
|
|
CHECK(rs.la_ep == 0 && rs.la_vmid == 103, "103 reuses freed ep0 (lowest free)");
|
|
|
|
/* 5) config-fail -> drop */
|
|
fp.config_ok = 0;
|
|
int n = rs.n_attach;
|
|
vmsig_discovery_feed(d, 999, 1);
|
|
CHECK(rs.n_attach == n, "config-fail vmid dropped (no attach)");
|
|
fp.config_ok = 1;
|
|
|
|
/* 6) stale (file present, VM dead) -> drop */
|
|
fp.live_mode = 1;
|
|
n = rs.n_attach;
|
|
vmsig_discovery_feed(d, 105, 1);
|
|
CHECK(rs.n_attach == n, "stale VM dropped (no attach)");
|
|
fp.live_mode = 0;
|
|
|
|
/* 7) retry-then-ok: first probe retries, tick re-probes and attaches */
|
|
fp.live_mode = 2; fp.live_calls = 0;
|
|
n = rs.n_attach;
|
|
vmsig_discovery_feed(d, 104, 1);
|
|
CHECK(rs.n_attach == n, "retry: not attached on first probe");
|
|
CHECK(vmsig_discovery_slot_of_vmid(d, 104) == -1, "retry: no slot yet");
|
|
vmsig_discovery_tick(d);
|
|
CHECK(rs.n_attach == n + 1 && rs.la_vmid == 104, "retry: attached after re-probe");
|
|
|
|
vmsig_core_free(core);
|
|
vmsig_ctx_free(ctx);
|
|
rmdir(dir);
|
|
}
|
|
|
|
/* Bootstrap path: files already present when discovery starts are picked up by the REAL
|
|
* readdir + parse_vmid scan (not the test feed hook); junk names are ignored. */
|
|
static void touch(const char* dir, const char* name) {
|
|
char path[512];
|
|
snprintf(path, sizeof path, "%s/%s", dir, name);
|
|
int fd = open(path, O_CREAT | O_WRONLY | O_CLOEXEC, 0600);
|
|
if (fd >= 0) close(fd);
|
|
}
|
|
static void rm(const char* dir, const char* name) {
|
|
char path[512];
|
|
snprintf(path, sizeof path, "%s/%s", dir, name);
|
|
unlink(path);
|
|
}
|
|
|
|
static void test_bootstrap(void) {
|
|
printf("test_bootstrap\n");
|
|
vmsig_ctx* ctx = vmsig_ctx_new();
|
|
vmsig_core* core = vmsig_core_new(ctx);
|
|
|
|
fakeprobe fp; memset(&fp, 0, sizeof fp); fp.config_ok = 1; fp.live_mode = 0;
|
|
vmsig_host_probe probe = { fp_config, fp_live, &fp };
|
|
recsink rs; memset(&rs, 0, sizeof rs);
|
|
vmsig_discovery_sink sink = { rs_attach, rs_detach, &rs };
|
|
|
|
char dir[] = "/tmp/vmsig_boot.XXXXXX";
|
|
CHECK(mkdtemp(dir) != NULL, "temp dir");
|
|
touch(dir, "vm-200-ram"); /* valid trigger */
|
|
touch(dir, "notavm"); /* ignored */
|
|
touch(dir, "vm-bad-ram"); /* non-numeric => ignored */
|
|
|
|
vmsig_discovery* d = vmsig_discovery_new(core, dir, NULL, NULL, NULL, &probe, &sink);
|
|
CHECK(d != NULL, "discovery created");
|
|
CHECK(rs.n_attach == 1 && rs.la_vmid == 200, "bootstrap scan attached ONLY vm-200 (real parse)");
|
|
CHECK(vmsig_discovery_slot_of_vmid(d, 200) == 0, "200 pinned to ep0 via bootstrap");
|
|
|
|
vmsig_core_free(core);
|
|
vmsig_ctx_free(ctx);
|
|
rm(dir, "vm-200-ram"); rm(dir, "notavm"); rm(dir, "vm-bad-ram"); rmdir(dir);
|
|
}
|
|
|
|
int main(void) {
|
|
test_discovery();
|
|
test_bootstrap();
|
|
printf("discovery tests: %s\n", g_fail ? "FAIL" : "PASS");
|
|
return g_fail ? 1 : 0;
|
|
}
|