go-qemu

repository·master·Indexed 21 days ago

https://github.com/digitalocean/go-qemu

A collection of Go packages for interacting with running QEMU instances. It provides management facilities for individual virtual machines and groups of VMs via a hypervisor, including support for the QEMU Machine Protocol (QMP), QAPI schema parsing, and connectivity through Libvirt RPC, virsh, or UNIX sockets.

Tokens
2.5K
Snippets
10
Records
16
Agent score
74%

What's inside go-qemu

  1. Overview of go-qemu packages

    master

    The go-qemu project consists of several top-level packages designed for different levels of QEMU interaction:

    • hypervisor: Used for managing one or more QEMU virtual machines on a hypervisor. It is designed for managing groups of VMs and provides access to individual qemu.Domain types.
    • qemu: Used for interacting with a single running QEMU instance. It is suitable for quick experiments or managing individual VMs.
    • qmp: Enables interaction via the QEMU Machine Protocol (QMP). This package wraps code-generated types with friendlier APIs and is typically used internally by this repository.
  2. Interact with QEMU via the QMP package

    master
    The qmp package allows you to interact with QEMU instances using the QEMU Machine Protocol (QMP). It supports different connection methods depending on whether your environment is managed by Libvirt or uses direct UNIX sockets.
  3. Connect to QEMU via Libvirt drivers

    master

    If your QEMU instances are managed by Libvirt, you can use one of two drivers to proxy QMP requests through the Libvirt daemon:

    1. RPC Driver: A pure Go implementation of Libvirt's RPC protocol. You establish a connection using net.DialTimeout and initialize the monitor using libvirtrpc.New.
    2. virsh Driver: Proxies requests through the virsh executable. You can initialize this using qmp.NewLibvirtMonitor.

    Note: The RPC driver requires a connection object (e.g., via unix or tcp) and a domain name.

    // RPC Driver example
    //conn, err := net.DialTimeout("unix", "/var/run/libvirt/libvirt-sock", 2*time.Second)
    conn, err := net.DialTimeout("tcp", "192.168.1.1:16509", 2*time.Second)
    monitor := libvirtrpc.New("stage-lb-1", conn)
    
    // virsh Driver example
    monitor, err := qmp.NewLibvirtMonitor("qemu:///system", "stage-lb-1")
  4. Important usage notes for the qemu package

    master

    The qemu package is used in production at DigitalOcean alongside libvirt, but users should be aware of the following:

    • API Stability: The API is not considered stable and may change over time. It is highly recommended to vendor the package into your project to avoid breaking changes.
    • Reliability: While production-tested, subtle bugs may still exist.
    • Support: If you encounter issues, check the open issues on GitHub before filing a new one.
  5. Traverse a qapischema.Tree

    master

    To process a parsed QAPI schema, you must walk the *qapischema.Tree recursively. Because the Node field is an interface, you must use a type switch to identify and access specific QAPI data types.

    Supported types for type assertion include:

    • qapischema.Root: The root node (contains no data; traverse its .Children).
    • qapischema.Include
    • qapischema.Pragma
    • *qapischema.Struct
    • *qapischema.Union
    • *qapischema.Event
    • *qapischema.Command
    • *qapischema.Alternate
    func visit(tree *qapischema.Tree) {
    	switch data := tree.Node.(type) {
    	// Root node, no data, traverse the subtrees in the .Children field.
    	case qapischema.Root:
    	case qapischema.Include:
    	case qapischema.Pragma:
    	case *qapischema.Struct:
    	case *qapischema.Union:
    	case *qapischema.Event:
    	case *qapischema.Command:
    	case *qapischema.Alternate:
    	}
    
    	// Process the rest of the document
    	for _, t := range tree.Children {
    		visit(t)
    	}
    }
    
    func main() {
    	tree, _ := qapischema.Parse(input)
    
    	visit(tree)
    }
  6. Configure network access for go-qemu

    master

    When using go-qemu, the method of connection determines the required permissions and configuration:

    • Unix Sockets (-network=unix): If running locally on the hypervisor host, the user executing the program must belong to the libvirtd group to access /var/run/libvirt/libvirt-sock.
    • TCP Connections (-network=tcp): If connecting remotely, libvirtd on the hypervisor host must be configured to allow TCP connections. Note that this configuration is not inherently secure.
  7. Debug QAPI parsing with qapiparse

    master

    The parser does not emit diagnostic error messages; it simply stops parsing when it encounters an unrecognized element. To debug where a parser failure occurs:

    1. Use the qapiparse utility. It reads QAPI from stdin and prints a pretty string representation of the parse tree to stdout.
    2. Compare your input schema against the output tree to find the first element in the input that is missing from the tree.
    3. When reporting defects, provide a minimal viable reproducer of the QAPI input.
  8. List domains using the hypervisor package

    master

    The hypervisor_domain_list example demonstrates how to use the hypervisor package to retrieve a list of domains from a connected hypervisor. The returned domains are of type go-qemu/Domain.

    To run the example via TCP:

    $ go get github.com/digitalocean/go-qemu/...
    $ go run examples/hypervisor_domain_list/main.go -network=tcp -address="hypervisorhost:16509"
  9. Monitor QMP events

    master

    You can listen for real-time QMP events by calling monitor.Events(), which returns a channel of events. You can then range over this channel to process incoming events like POWERDOWN, SHUTDOWN, STOP, RESET, or RESUME.

    monitor.Connect()
    defer monitor.Disconnect()
    
    stream, _ := monitor.Events()
    for e := range stream {
    	log.Printf("EVENT: %s", e.Event)
    }
  10. Shut off a domain using the hypervisor package

    master

    The domain_system_powerdown example demonstrates how to use the hypervisor package to perform a system powerdown on a specified domain.

    To run the example locally:

    $ go run examples/domain_system_powerdown/main.go -domainName="ubuntu14.04"

    To run the example via TCP:

    $ go run examples/domain_system_powerdown/main.go -network=tcp -address="hypervisorhost:16509" -domainName="ubuntu14.04"