Machine: lifecycle & state methods

MethodReturnsNotes
m.name()string
m.kind()string"vm" or "container" — reported so a script can *say* what it has, never so it has to branch to drive it
m.start() / m.stop() / m.stop_force() / m.restart()Result[unit, string]stop = graceful ladder (agent → ACPI → kill)
m.poweroff()Result[unit, string]Clean QMP quit — exits QEMU flushing block caches. The only safe seal for guests with no ACPI (DOS, Win 3.x), where a force-kill can drop qcow2 writes
m.state()stringone of "stopped" / "starting" / "running" / "stopping"
m.is_ready()boolThe sticky ready flag — once set it stays set while QEMU runs, so it does not drop across a guest reboot
m.agent_answering()boolThe live agent probe, ungated — goes false while the guest is down or mid-reboot. Use this to watch a reboot you requested from inside the guest
m.wait_ready(timeout_secs: int)Result[unit, string]Block until agent responds
m.wait_shutdown(timeout_secs: int)Result[unit, string]Block until powered off
m.ip()Result[string, string]Primary NIC IPv4 (DHCP lease / agent)
m.ip_nic(nic: int)Result[string, string]By NIC index (0-based)
m.is_healthy()boolLatest healthcheck verdict; a machine declaring none counts healthy once ready
m.logs(lines: int)Result[string, string]Tail of the console log — a container's captured stdout/stderr, a VM's serial log
m.logins()List[Login]The identities the machine declares with login {} (§19.2). Read from the lab file, so it answers on a stopped machine
m.as_login(selector: string)Result[Machine, string]A second handle onto the same machine, whose guest work runs as that login — a label, the account name, or the family floor (SYSTEM/root). Fails naming the machine on a selector nothing matches
m.as_account(user: string, password: string)Result[Machine, string]The same, for an account the lab file does not declare, or one whose declared secret has been rotated

Login carries label, user, password: Option[string], elevated and default. Reading it is what lets a provision script create exactly the account the lab file declares, instead of the password living in two places that drift. The secret crosses as written — none where the author declared none, never an empty string. elevated and default cross *resolved*, so a lone login reports default == true without the lab file saying so.

as_login / as_account are the wscript rung of §19.2's precedence ladder, resolved exactly as vmlab exec --user/--password resolves it. They return a handle rather than switching this one, so an identity is said once and every call on the result — exec, copy_to, terminal — carries it. That is what lets a provision {} step address a login's home directory **before that account has ever logged on** (§19.8), which is the only route for anything that must land as the developer rather than as the machine. A playbook {} has no rung here at all.

Inside a machine's own first-boot script the readiness pair is special-cased: is_ready() / wait_ready() mean "does the agent answer right now", because the sticky flag is deliberately withheld until that script returns. Everywhere else they report full readiness, and agent_answering() is how you get the live signal.