summaryrefslogtreecommitdiff
path: root/vendor/github.com/digitalocean/go-libvirt/libvirt.go
diff options
context:
space:
mode:
authorbaude <bbaude@redhat.com>2021-03-15 14:52:43 -0500
committerbaude <bbaude@redhat.com>2021-03-25 08:43:51 -0500
commitb5f54a9b23e8d9418700494da9aa78d8db354c43 (patch)
tree59dfb9edf3faf6d184f6af40522f71968948133a /vendor/github.com/digitalocean/go-libvirt/libvirt.go
parenta861f6fd3ebe4fe0b63a1b550e6b99d7525228c0 (diff)
downloadpodman-b5f54a9b23e8d9418700494da9aa78d8db354c43.tar.gz
podman-b5f54a9b23e8d9418700494da9aa78d8db354c43.tar.bz2
podman-b5f54a9b23e8d9418700494da9aa78d8db354c43.zip
introduce podman machine
podman machine allows podman to create, manage, and interact with a vm running some form of linux (default is fcos). podman is then configured to be able to interact with the vm automatically. while this is usable on linux, the real push is to get this working on both current apple architectures in macos. Ashley Cui contributed to this PR and was a great help. [NO TESTS NEEDED] Signed-off-by: baude <bbaude@redhat.com>
Diffstat (limited to 'vendor/github.com/digitalocean/go-libvirt/libvirt.go')
-rw-r--r--vendor/github.com/digitalocean/go-libvirt/libvirt.go608
1 files changed, 608 insertions, 0 deletions
diff --git a/vendor/github.com/digitalocean/go-libvirt/libvirt.go b/vendor/github.com/digitalocean/go-libvirt/libvirt.go
new file mode 100644
index 000000000..a76eafdd2
--- /dev/null
+++ b/vendor/github.com/digitalocean/go-libvirt/libvirt.go
@@ -0,0 +1,608 @@
+// Copyright 2018 The go-libvirt Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Package libvirt is a pure Go implementation of the libvirt RPC protocol.
+// For more information on the protocol, see https://libvirt.org/internals/l.html
+package libvirt
+
+// We'll use c-for-go to extract the consts and typedefs from the libvirt
+// sources so we don't have to duplicate them here.
+//go:generate scripts/gen-consts.sh
+
+import (
+ "bufio"
+ "bytes"
+ "context"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "net"
+ "sync"
+
+ "github.com/digitalocean/go-libvirt/internal/constants"
+ "github.com/digitalocean/go-libvirt/internal/event"
+ xdr "github.com/digitalocean/go-libvirt/internal/go-xdr/xdr2"
+)
+
+// ErrEventsNotSupported is returned by Events() if event streams
+// are unsupported by either QEMU or libvirt.
+var ErrEventsNotSupported = errors.New("event monitor is not supported")
+
+// Libvirt implements libvirt's remote procedure call protocol.
+type Libvirt struct {
+ conn net.Conn
+ r *bufio.Reader
+ w *bufio.Writer
+ mu *sync.Mutex
+
+ // method callbacks
+ cmux sync.RWMutex
+ callbacks map[int32]chan response
+
+ // event listeners
+ emux sync.RWMutex
+ events map[int32]*event.Stream
+
+ // next request serial number
+ s int32
+}
+
+// DomainEvent represents a libvirt domain event.
+type DomainEvent struct {
+ CallbackID int32
+ Domain Domain
+ Event string
+ Seconds uint64
+ Microseconds uint32
+ Padding uint8
+ Details []byte
+}
+
+// GetCallbackID returns the callback ID of a QEMU domain event.
+func (de DomainEvent) GetCallbackID() int32 {
+ return de.CallbackID
+}
+
+// GetCallbackID returns the callback ID of a libvirt lifecycle event.
+func (m DomainEventCallbackLifecycleMsg) GetCallbackID() int32 {
+ return m.CallbackID
+}
+
+// qemuError represents a QEMU process error.
+type qemuError struct {
+ Error struct {
+ Class string `json:"class"`
+ Description string `json:"desc"`
+ } `json:"error"`
+}
+
+// Capabilities returns an XML document describing the host's capabilties.
+func (l *Libvirt) Capabilities() ([]byte, error) {
+ caps, err := l.ConnectGetCapabilities()
+ return []byte(caps), err
+}
+
+// Connect establishes communication with the libvirt server.
+// The underlying libvirt socket connection must be previously established.
+func (l *Libvirt) Connect() error {
+ payload := struct {
+ Padding [3]byte
+ Name string
+ Flags uint32
+ }{
+ Padding: [3]byte{0x1, 0x0, 0x0},
+ Name: "qemu:///system",
+ Flags: 0,
+ }
+
+ buf, err := encode(&payload)
+ if err != nil {
+ return err
+ }
+
+ // libvirt requires that we call auth-list prior to connecting,
+ // event when no authentication is used.
+ _, err = l.request(constants.ProcAuthList, constants.Program, buf)
+ if err != nil {
+ return err
+ }
+
+ _, err = l.request(constants.ProcConnectOpen, constants.Program, buf)
+ if err != nil {
+ return err
+ }
+
+ return nil
+}
+
+// Disconnect shuts down communication with the libvirt server and closes the
+// underlying net.Conn.
+func (l *Libvirt) Disconnect() error {
+ // close event streams
+ for _, ev := range l.events {
+ l.unsubscribeEvents(ev)
+ }
+
+ // Deregister all callbacks to prevent blocking on clients with
+ // outstanding requests
+ l.deregisterAll()
+
+ _, err := l.request(constants.ProcConnectClose, constants.Program, nil)
+ if err != nil {
+ return err
+ }
+
+ return l.conn.Close()
+}
+
+// Domains returns a list of all domains managed by libvirt.
+//
+// Deprecated: use ConnectListAllDomains instead.
+func (l *Libvirt) Domains() ([]Domain, error) {
+ // these are the flags as passed by `virsh list --all`
+ flags := ConnectListDomainsActive | ConnectListDomainsInactive
+ domains, _, err := l.ConnectListAllDomains(1, flags)
+ return domains, err
+}
+
+// DomainState returns state of the domain managed by libvirt.
+//
+// Deprecated: use DomainGetState instead.
+func (l *Libvirt) DomainState(dom string) (DomainState, error) {
+ d, err := l.lookup(dom)
+ if err != nil {
+ return DomainNostate, err
+ }
+
+ state, _, err := l.DomainGetState(d, 0)
+ return DomainState(state), err
+}
+
+// SubscribeQEMUEvents streams domain events until the provided context is
+// cancelled. If a problem is encountered setting up the event monitor
+// connection an error will be returned. Errors encountered during streaming
+// will cause the returned event channel to be closed. QEMU domain events.
+func (l *Libvirt) SubscribeQEMUEvents(ctx context.Context, dom string) (<-chan DomainEvent, error) {
+ d, err := l.lookup(dom)
+ if err != nil {
+ return nil, err
+ }
+
+ callbackID, err := l.QEMUConnectDomainMonitorEventRegister([]Domain{d}, nil, 0)
+ if err != nil {
+ return nil, err
+ }
+
+ stream := event.NewStream(constants.QEMUProgram, callbackID)
+ l.addStream(stream)
+ ch := make(chan DomainEvent)
+ go func() {
+ ctx, cancel := context.WithCancel(ctx)
+ defer cancel()
+ defer l.unsubscribeQEMUEvents(stream)
+ defer stream.Shutdown()
+ defer func() { close(ch) }()
+
+ for {
+ select {
+ case ev, ok := <-stream.Recv():
+ if !ok {
+ return
+ }
+ ch <- *ev.(*DomainEvent)
+ case <-ctx.Done():
+ return
+ }
+ }
+ }()
+
+ return ch, nil
+}
+
+// unsubscribeQEMUEvents stops the flow of events from QEMU through libvirt.
+func (l *Libvirt) unsubscribeQEMUEvents(stream *event.Stream) error {
+ err := l.QEMUConnectDomainMonitorEventDeregister(stream.CallbackID)
+ l.removeStream(stream.CallbackID)
+
+ return err
+}
+
+// SubscribeEvents allows the caller to subscribe to any of the event types
+// supported by libvirt. The events will continue to be streamed until the
+// caller cancels the provided context. After canceling the context, callers
+// should wait until the channel is closed to be sure they're collected all the
+// events.
+func (l *Libvirt) SubscribeEvents(ctx context.Context, eventID DomainEventID,
+ dom OptDomain) (<-chan interface{}, error) {
+
+ callbackID, err := l.ConnectDomainEventCallbackRegisterAny(int32(eventID), nil)
+ if err != nil {
+ return nil, err
+ }
+
+ stream := event.NewStream(constants.QEMUProgram, callbackID)
+ l.addStream(stream)
+
+ ch := make(chan interface{})
+ go func() {
+ ctx, cancel := context.WithCancel(ctx)
+ defer cancel()
+ defer l.unsubscribeEvents(stream)
+ defer stream.Shutdown()
+ defer func() { close(ch) }()
+
+ for {
+ select {
+ case ev, ok := <-stream.Recv():
+ if !ok {
+ return
+ }
+ ch <- ev
+ case <-ctx.Done():
+ return
+ }
+ }
+ }()
+
+ return ch, nil
+}
+
+// unsubscribeEvents stops the flow of the specified events from libvirt. There
+// are two steps to this process: a call to libvirt to deregister our callback,
+// and then removing the callback from the list used by the `route` fucntion. If
+// the deregister call fails, we'll return the error, but still remove the
+// callback from the list. That's ok; if any events arrive after this point, the
+// route function will drop them when it finds no registered handler.
+func (l *Libvirt) unsubscribeEvents(stream *event.Stream) error {
+ err := l.ConnectDomainEventCallbackDeregisterAny(stream.CallbackID)
+ l.removeStream(stream.CallbackID)
+
+ return err
+}
+
+// LifecycleEvents streams lifecycle events until the provided context is
+// cancelled. If a problem is encountered setting up the event monitor
+// connection, an error will be returned. Errors encountered during streaming
+// will cause the returned event channel to be closed.
+func (l *Libvirt) LifecycleEvents(ctx context.Context) (<-chan DomainEventLifecycleMsg, error) {
+ callbackID, err := l.ConnectDomainEventCallbackRegisterAny(int32(DomainEventIDLifecycle), nil)
+ if err != nil {
+ return nil, err
+ }
+
+ stream := event.NewStream(constants.Program, callbackID)
+ l.addStream(stream)
+
+ ch := make(chan DomainEventLifecycleMsg)
+
+ go func() {
+ ctx, cancel := context.WithCancel(ctx)
+ defer cancel()
+ defer l.unsubscribeEvents(stream)
+ defer stream.Shutdown()
+ defer func() { close(ch) }()
+
+ for {
+ select {
+ case ev, ok := <-stream.Recv():
+ if !ok {
+ return
+ }
+ ch <- ev.(*DomainEventCallbackLifecycleMsg).Msg
+ case <-ctx.Done():
+ return
+ }
+ }
+ }()
+
+ return ch, nil
+}
+
+// Run executes the given QAPI command against a domain's QEMU instance.
+// For a list of available QAPI commands, see:
+// http://git.qemu.org/?p=qemu.git;a=blob;f=qapi-schema.json;hb=HEAD
+func (l *Libvirt) Run(dom string, cmd []byte) ([]byte, error) {
+ d, err := l.lookup(dom)
+ if err != nil {
+ return nil, err
+ }
+
+ payload := struct {
+ Domain Domain
+ Command []byte
+ Flags uint32
+ }{
+ Domain: d,
+ Command: cmd,
+ Flags: 0,
+ }
+
+ buf, err := encode(&payload)
+ if err != nil {
+ return nil, err
+ }
+
+ res, err := l.request(constants.QEMUProcDomainMonitorCommand, constants.QEMUProgram, buf)
+ if err != nil {
+ return nil, err
+ }
+
+ // check for QEMU process errors
+ if err = getQEMUError(res); err != nil {
+ return nil, err
+ }
+
+ r := bytes.NewReader(res.Payload)
+ dec := xdr.NewDecoder(r)
+ data, _, err := dec.DecodeFixedOpaque(int32(r.Len()))
+ if err != nil {
+ return nil, err
+ }
+
+ // drop QMP control characters from start of line, and drop
+ // any trailing NULL characters from the end
+ return bytes.TrimRight(data[4:], "\x00"), nil
+}
+
+// Secrets returns all secrets managed by the libvirt daemon.
+//
+// Deprecated: use ConnectListAllSecrets instead.
+func (l *Libvirt) Secrets() ([]Secret, error) {
+ secrets, _, err := l.ConnectListAllSecrets(1, 0)
+ return secrets, err
+}
+
+// StoragePool returns the storage pool associated with the provided name.
+// An error is returned if the requested storage pool is not found.
+//
+// Deprecated: use StoragePoolLookupByName instead.
+func (l *Libvirt) StoragePool(name string) (StoragePool, error) {
+ return l.StoragePoolLookupByName(name)
+}
+
+// StoragePools returns a list of defined storage pools. Pools are filtered by
+// the provided flags. See StoragePools*.
+//
+// Deprecated: use ConnectListAllStoragePools instead.
+func (l *Libvirt) StoragePools(flags ConnectListAllStoragePoolsFlags) ([]StoragePool, error) {
+ pools, _, err := l.ConnectListAllStoragePools(1, flags)
+ return pools, err
+}
+
+// Undefine undefines the domain specified by dom, e.g., 'prod-lb-01'.
+// The flags argument allows additional options to be specified such as
+// cleaning up snapshot metadata. For more information on available
+// flags, see DomainUndefine*.
+//
+// Deprecated: use DomainUndefineFlags instead.
+func (l *Libvirt) Undefine(dom string, flags DomainUndefineFlagsValues) error {
+ d, err := l.lookup(dom)
+ if err != nil {
+ return err
+ }
+
+ return l.DomainUndefineFlags(d, flags)
+}
+
+// Destroy destroys the domain specified by dom, e.g., 'prod-lb-01'.
+// The flags argument allows additional options to be specified such as
+// allowing a graceful shutdown with SIGTERM than SIGKILL.
+// For more information on available flags, see DomainDestroy*.
+//
+// Deprecated: use DomainDestroyFlags instead.
+func (l *Libvirt) Destroy(dom string, flags DomainDestroyFlagsValues) error {
+ d, err := l.lookup(dom)
+ if err != nil {
+ return err
+ }
+
+ return l.DomainDestroyFlags(d, flags)
+}
+
+// XML returns a domain's raw XML definition, akin to `virsh dumpxml <domain>`.
+// See DomainXMLFlag* for optional flags.
+//
+// Deprecated: use DomainGetXMLDesc instead.
+func (l *Libvirt) XML(dom string, flags DomainXMLFlags) ([]byte, error) {
+ d, err := l.lookup(dom)
+ if err != nil {
+ return nil, err
+ }
+
+ xml, err := l.DomainGetXMLDesc(d, flags)
+ return []byte(xml), err
+}
+
+// DefineXML defines a domain, but does not start it.
+//
+// Deprecated: use DomainDefineXMLFlags instead.
+func (l *Libvirt) DefineXML(x []byte, flags DomainDefineFlags) error {
+ _, err := l.DomainDefineXMLFlags(string(x), flags)
+ return err
+}
+
+// Version returns the version of the libvirt daemon.
+//
+// Deprecated: use ConnectGetLibVersion instead.
+func (l *Libvirt) Version() (string, error) {
+ ver, err := l.ConnectGetLibVersion()
+ if err != nil {
+ return "", err
+ }
+
+ // The version is provided as an int following this formula:
+ // version * 1,000,000 + minor * 1000 + micro
+ // See src/libvirt-host.c # virConnectGetLibVersion
+ major := ver / 1000000
+ ver %= 1000000
+ minor := ver / 1000
+ ver %= 1000
+ micro := ver
+
+ versionString := fmt.Sprintf("%d.%d.%d", major, minor, micro)
+ return versionString, nil
+}
+
+// Shutdown shuts down a domain. Note that the guest OS may ignore the request.
+// If flags is set to 0 then the hypervisor will choose the method of shutdown it considers best.
+//
+// Deprecated: use DomainShutdownFlags instead.
+func (l *Libvirt) Shutdown(dom string, flags DomainShutdownFlagValues) error {
+ d, err := l.lookup(dom)
+ if err != nil {
+ return err
+ }
+
+ return l.DomainShutdownFlags(d, flags)
+}
+
+// Reboot reboots the domain. Note that the guest OS may ignore the request.
+// If flags is set to zero, then the hypervisor will choose the method of shutdown it considers best.
+//
+// Deprecated: use DomainReboot instead.
+func (l *Libvirt) Reboot(dom string, flags DomainRebootFlagValues) error {
+ d, err := l.lookup(dom)
+ if err != nil {
+ return err
+ }
+
+ return l.DomainReboot(d, flags)
+}
+
+// Reset resets domain immediately without any guest OS shutdown
+//
+// Deprecated: use DomainReset instead.
+func (l *Libvirt) Reset(dom string) error {
+ d, err := l.lookup(dom)
+ if err != nil {
+ return err
+ }
+
+ return l.DomainReset(d, 0)
+}
+
+// BlockLimit contains a name and value pair for a Get/SetBlockIOTune limit. The
+// Name field is the name of the limit (to see a list of the limits that can be
+// applied, execute the 'blkdeviotune' command on a VM in virsh). Callers can
+// use the QEMUBlockIO... constants below for the Name value. The Value field is
+// the limit to apply.
+type BlockLimit struct {
+ Name string
+ Value uint64
+}
+
+// SetBlockIOTune changes the per-device block I/O tunables within a guest.
+// Parameters are the name of the VM, the name of the disk device to which the
+// limits should be applied, and 1 or more BlockLimit structs containing the
+// actual limits.
+//
+// The limits which can be applied here are enumerated in the QEMUBlockIO...
+// constants above, and you can also see the full list by executing the
+// 'blkdeviotune' command on a VM in virsh.
+//
+// Example usage:
+// SetBlockIOTune("vm-name", "vda", BlockLimit{libvirt.QEMUBlockIOWriteBytesSec, 1000000})
+//
+// Deprecated: use DomainSetBlockIOTune instead.
+func (l *Libvirt) SetBlockIOTune(dom string, disk string, limits ...BlockLimit) error {
+ d, err := l.lookup(dom)
+ if err != nil {
+ return err
+ }
+
+ params := make([]TypedParam, len(limits))
+ for ix, limit := range limits {
+ tpval := NewTypedParamValueUllong(limit.Value)
+ params[ix] = TypedParam{Field: limit.Name, Value: *tpval}
+ }
+
+ return l.DomainSetBlockIOTune(d, disk, params, uint32(DomainAffectLive))
+}
+
+// GetBlockIOTune returns a slice containing the current block I/O tunables for
+// a disk.
+//
+// Deprecated: use DomainGetBlockIOTune instead.
+func (l *Libvirt) GetBlockIOTune(dom string, disk string) ([]BlockLimit, error) {
+ d, err := l.lookup(dom)
+ if err != nil {
+ return nil, err
+ }
+
+ lims, _, err := l.DomainGetBlockIOTune(d, []string{disk}, 32, uint32(TypedParamStringOkay))
+ if err != nil {
+ return nil, err
+ }
+
+ var limits []BlockLimit
+
+ // now decode each of the returned TypedParams. To do this we read the field
+ // name and type, then use the type information to decode the value.
+ for _, lim := range lims {
+ var l BlockLimit
+ name := lim.Field
+ switch lim.Value.I.(type) {
+ case uint64:
+ l = BlockLimit{Name: name, Value: lim.Value.I.(uint64)}
+ }
+ limits = append(limits, l)
+ }
+
+ return limits, nil
+}
+
+// lookup returns a domain as seen by libvirt.
+func (l *Libvirt) lookup(name string) (Domain, error) {
+ return l.DomainLookupByName(name)
+}
+
+// getQEMUError checks the provided response for QEMU process errors.
+// If an error is found, it is extracted an returned, otherwise nil.
+func getQEMUError(r response) error {
+ pl := bytes.NewReader(r.Payload)
+ dec := xdr.NewDecoder(pl)
+
+ s, _, err := dec.DecodeString()
+ if err != nil {
+ return err
+ }
+
+ var e qemuError
+ if err = json.Unmarshal([]byte(s), &e); err != nil {
+ return err
+ }
+
+ if e.Error.Description != "" {
+ return errors.New(e.Error.Description)
+ }
+
+ return nil
+}
+
+// New configures a new Libvirt RPC connection.
+func New(conn net.Conn) *Libvirt {
+ l := &Libvirt{
+ conn: conn,
+ s: 0,
+ r: bufio.NewReader(conn),
+ w: bufio.NewWriter(conn),
+ mu: &sync.Mutex{},
+ callbacks: make(map[int32]chan response),
+ events: make(map[int32]*event.Stream),
+ }
+
+ go l.listen()
+
+ return l
+}