/** * How a scenario instance's resources are named. * * A declaration is a KIND; instances are many. Two instances of one declaration hold the * same addresses and must never meet, so every resource carries the instance id and * nothing is shared between them. */ const PREFIX = "mlab"; /** Instance ids are short and sortable — the last one left standing has to be findable. */ export function newInstanceId(scenario: string, now: Date): string { const stamp = now.toISOString().replace(/[-:T]/g, "").slice(2, 12); return `${scenario}-${stamp}`; } /** incus network names are limited to 15 characters, so this hashes rather than truncates. */ export function networkName(instanceId: string, segment: string): string { const digest = hash(`${instanceId}/${segment}`); return `${PREFIX}${digest}`; } export function machineName(instanceId: string, machine: string): string { return `${PREFIX}-${instanceId}-${machine}`; } export function instanceIdOf(machineName: string, machine: string): string | null { const suffix = `-${machine}`; if (!machineName.startsWith(`${PREFIX}-`) || !machineName.endsWith(suffix)) return null; return machineName.slice(PREFIX.length + 1, machineName.length - suffix.length); } export function machinePrefix(instanceId: string): string { return `${PREFIX}-${instanceId}-`; } /** FNV-1a, rendered base36. Short, stable, and collisions are a naming clash not a leak. */ function hash(text: string): string { let h = 0x811c9dc5; for (let i = 0; i < text.length; i++) { h ^= text.charCodeAt(i); h = Math.imul(h, 0x01000193) >>> 0; } return h.toString(36).padStart(7, "0").slice(0, 7); } /** * A deterministic MAC for a machine's Nth interface, in the locally-administered range. * * Set explicitly at creation rather than read back afterwards: incus assigns a MAC at * runtime and does not record it in the device config, so querying returns nothing. A * derived address is also stable across raises of the same instance, which makes an * in-guest match on it reproducible. */ export function macFor(instanceId: string, machine: string, index: number): string { const digest = hash(`${instanceId}/${machine}/${index}`); const octets: string[] = []; let value = 0; for (let i = 0; i < digest.length; i++) value = (value * 31 + digest.charCodeAt(i)) >>> 0; // 02 marks it locally administered, which is what a made-up address is supposed to say. octets.push("02"); for (let i = 0; i < 5; i++) { octets.push(((value >>> (i * 5)) & 0xff).toString(16).padStart(2, "0")); } return octets.join(":"); }