Documentation
¶
Index ¶
- Constants
- Variables
- func ParseURI(uri string) (string, error)
- func Preflight(ctx context.Context, uri string) error
- func ShouldDefaultToLibvirt(engine string, forceLocal bool, p Probes) (bool, string)
- func SpecToDomainXML(spec qemu.Spec) ([]byte, error)
- func TranslateSpecPaths(spec qemu.Spec, m PathMap) (qemu.Spec, error)
- type Client
- func (c *Client) Close() error
- func (c *Client) DefineDomain(xml string) (string, error)
- func (c *Client) DestroyDomain(name string) error
- func (c *Client) DomainState(name string) (int32, error)
- func (c *Client) ListDomains() ([]string, error)
- func (c *Client) ShutdownDomain(name string) error
- func (c *Client) StartDomain(name string) error
- func (c *Client) UndefineDomain(name string) error
- type DomainClient
- type Engine
- type PathMap
- type PathMapping
- type Probes
Constants ¶
const ( DomainRunning int32 = 1 DomainShutoff int32 = 5 )
virDomainState values (subset).
Variables ¶
var ( // ErrUnreachable: TCP dial failed — libvirtd is not listening (or the // host is wrong). ErrUnreachable = errors.New("libvirtd unreachable") // ErrHandshake: TCP connected but the libvirt RPC handshake failed — // wrong service on the port, or auth rejected. ErrHandshake = errors.New("libvirt handshake failed") )
Typed connection errors, so callers (preflight, CELL-376) can map each failure to its own remediation instead of showing a raw dial error.
Functions ¶
func ParseURI ¶
ParseURI validates a libvirt connection URI and returns the TCP dial address. Only qemu+tcp:// is supported: the CLI runs inside a Linux cell and reaches the host's libvirtd over TCP (qemu+ssh:// is future work).
func Preflight ¶
Preflight verifies a libvirtd daemon answers at uri and completes the RPC handshake, mapping each failure mode to one actionable message (the CELL-44 pattern: read the error, know the next command).
func ShouldDefaultToLibvirt ¶
ShouldDefaultToLibvirt decides whether an --engine=qemu launch should upgrade to libvirt remote mode, and why.
Authority ordering follows accel.go: explicit intent always wins (--local pins the in-container path; any engine other than qemu is untouched), and the upgrade fires only when every probe agrees the environment is a Docker cell on a Mac where local qemu can only mean TCG.
func SpecToDomainXML ¶
SpecToDomainXML renders a qemu.Spec as a libvirt domain document.
The domain always targets the macOS host (type hvf, machine virt, cpu host-passthrough): in libvirt mode the CLI may run inside a Linux cell, but the VM boots on the darwin side of the connection — command.go's runtime.GOOS switches must not leak in here.
Anything libvirt can express natively is native (name, memory, vcpu, firmware, VNC, reboot policy). Everything else — guest NVMe controller (Windows ARM64 has no virtio storage driver inbox, CELL-359), ramfb, hostfwd user networking, xhci port sizing, serial chardevs — is taken VERBATIM from qemu.BuildRunCommand's argv and passed through <qemu:commandline>, so the two launch paths cannot drift: a new argv flag flows through automatically unless it is claimed by the native map.
func TranslateSpecPaths ¶
TranslateSpecPaths returns a copy of spec with every field QEMU opens on the host rewritten through the map. Empty fields stay empty; the input is not mutated. SSHKeyPath is deliberately absent: the ssh client runs on the CLI side, in the container namespace.
Types ¶
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client is a connection to a libvirtd daemon.
func Connect ¶
Connect dials the daemon named by uri and completes the libvirt handshake. The context bounds both the dial and the handshake.
func (*Client) Close ¶
Close disconnects from the daemon and closes the socket.
go-libvirt's Disconnect tears the socket down itself, so both it and our conn.Close can report "use of closed network connection" — benign teardown noise, not a failure (it broke the first field run, 2026-07-30).
func (*Client) DefineDomain ¶
DefineDomain registers (or replaces) a persistent domain from XML and returns its name.
func (*Client) DestroyDomain ¶
DestroyDomain force-stops a domain (hard power-off).
func (*Client) DomainState ¶
DomainState returns the libvirt run state for a domain (values from virDomainState: 1=running, 5=shutoff, ...).
func (*Client) ListDomains ¶
ListDomains returns the names of all domains, active and inactive.
func (*Client) ShutdownDomain ¶
ShutdownDomain requests a graceful guest shutdown (ACPI).
func (*Client) StartDomain ¶
StartDomain boots a defined domain by name.
func (*Client) UndefineDomain ¶
UndefineDomain removes a persistent domain definition.
type DomainClient ¶
type DomainClient interface {
DefineDomain(xml string) (string, error)
StartDomain(name string) error
ShutdownDomain(name string) error
DestroyDomain(name string) error
DomainState(name string) (int32, error)
Close() error
}
DomainClient is the slice of Client the engine needs; injectable in tests.
type Engine ¶
type Engine struct {
URI string
Spec qemu.Spec
Map PathMap
// SSHWaitTimeout bounds the post-boot SSH wait. The template is already
// installed and provisioned, so this is a boot, not a Windows install.
SSHWaitTimeout time.Duration
// ShutdownGraceTimeout bounds the graceful-shutdown wait before escalating
// to destroy.
ShutdownGraceTimeout time.Duration
// ShutdownPollInterval is how often Shutdown re-checks the domain state.
ShutdownPollInterval time.Duration
// ConnectFn is the transport factory; tests inject a fake.
ConnectFn func(ctx context.Context, uri string) (DomainClient, error)
// WaitSSHFn waits for the forwarded SSH port; tests inject a fake.
WaitSSHFn func(host string, port uint16, timeout time.Duration) error
// contains filtered or unexported fields
}
Engine boots and stops a prepped Windows template on the machine behind a libvirtd connection (CELL-377). It implements the vm.Engine lifecycle.
func (*Engine) Boot ¶
Boot defines and starts the domain, then waits for SSH. A domain that is already running is attached to instead of redefined.
func (*Engine) DomainXML ¶
DomainXML renders the domain document this engine would define: paths translated to the host namespace, hostfwd bound on all interfaces so the forward is reachable from the container (the mac's 127.0.0.1 is not).
type PathMap ¶
type PathMap []PathMapping
PathMap translates container paths to host paths for domain XML.
Empty means the CLI already runs on the host — every path passes through. Non-empty means strict translation: QEMU on the host cannot open a container-only path, so an unmapped path is an error, not a passthrough.
type PathMapping ¶
type PathMapping struct {
From string // container-side prefix (bind mount target)
To string // host-side prefix (bind mount source)
}
PathMapping rewrites one container path prefix to its host equivalent.
type Probes ¶
type Probes struct {
// InContainer reports whether the CLI runs inside a container.
InContainer func() bool
// HostResolves reports whether the Docker host gateway name resolves.
HostResolves func() bool
// KVMUsable reports whether /dev/kvm can actually be opened.
KVMUsable func() error
}
Probes are the environment signals behind the qemu→libvirt auto-default (CELL-378). Injectable so the decision matrix is unit-testable.
func DefaultProbes ¶
func DefaultProbes() Probes
DefaultProbes wires the production signals: /.dockerenv, a DNS lookup of host.docker.internal, and qemu.ProbeKVM. All three are one cheap syscall or lookup — this runs on every launch.