2016-05-20 03:05:37 +03:00
|
|
|
// Copyright 2016 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
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bufio"
|
|
|
|
"bytes"
|
2016-07-19 19:27:31 +03:00
|
|
|
"encoding/json"
|
2016-05-20 03:05:37 +03:00
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"net"
|
2016-08-19 21:31:59 +03:00
|
|
|
"net/url"
|
2016-05-20 03:05:37 +03:00
|
|
|
"sync"
|
|
|
|
|
|
|
|
"github.com/davecgh/go-xdr/xdr2"
|
2016-05-20 05:56:27 +03:00
|
|
|
"github.com/digitalocean/go-libvirt/internal/constants"
|
2016-05-20 03:05:37 +03:00
|
|
|
)
|
|
|
|
|
|
|
|
// 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
|
2017-12-07 20:47:52 +03:00
|
|
|
mu *sync.Mutex
|
2016-05-20 03:05:37 +03:00
|
|
|
|
|
|
|
// method callbacks
|
|
|
|
cm sync.Mutex
|
|
|
|
callbacks map[uint32]chan response
|
|
|
|
|
|
|
|
// event listeners
|
|
|
|
em sync.Mutex
|
|
|
|
events map[uint32]chan *DomainEvent
|
|
|
|
|
|
|
|
// next request serial number
|
|
|
|
s uint32
|
|
|
|
}
|
|
|
|
|
|
|
|
// DomainEvent represents a libvirt domain event.
|
|
|
|
type DomainEvent struct {
|
|
|
|
CallbackID uint32
|
2017-11-20 21:50:32 +03:00
|
|
|
Domain Domain
|
2016-05-20 03:05:37 +03:00
|
|
|
Event string
|
|
|
|
Seconds uint64
|
|
|
|
Microseconds uint32
|
|
|
|
Padding uint8
|
|
|
|
Details []byte
|
|
|
|
}
|
|
|
|
|
2016-07-19 19:27:31 +03:00
|
|
|
// qemuError represents a QEMU process error.
|
|
|
|
type qemuError struct {
|
|
|
|
Error struct {
|
|
|
|
Class string `json:"class"`
|
|
|
|
Description string `json:"desc"`
|
|
|
|
} `json:"error"`
|
|
|
|
}
|
|
|
|
|
2016-12-01 04:37:30 +03:00
|
|
|
// DomainXMLFlags specifies options for dumping a domain's XML.
|
|
|
|
type DomainXMLFlags uint32
|
|
|
|
|
2017-08-31 18:21:31 +03:00
|
|
|
// DomainAffectFlags specifies options for whether an operation affects the
|
|
|
|
// running VM, or the persistent VM configuration on disk. See FlagDomain...
|
|
|
|
// consts for values.
|
|
|
|
type DomainAffectFlags uint32
|
|
|
|
|
|
|
|
// Consts used for flags
|
|
|
|
|
|
|
|
// virDomainModificationImpact and virTypedParameterFlags values. These are
|
|
|
|
// combined here because they are both used to set the same flags fields in the
|
|
|
|
// libvirt API.
|
|
|
|
const (
|
|
|
|
// FlagDomainAffectCurrent means affect the current domain state
|
|
|
|
FlagDomainAffectCurrent DomainAffectFlags = 0
|
|
|
|
// FlagDomainAffectLive means affect the running domain state
|
|
|
|
FlagDomainAffectLive = 1 << (iota - 1)
|
|
|
|
// FlagDomainAffectConfig means affect the persistent domain state.
|
|
|
|
FlagDomainAffectConfig
|
|
|
|
// FlagTypedParamStringOkay tells the server that this client understands
|
2017-09-15 17:48:46 +03:00
|
|
|
// TypedParamStrings.
|
2017-08-31 18:21:31 +03:00
|
|
|
FlagTypedParamStringOkay
|
|
|
|
)
|
|
|
|
|
2016-12-01 04:37:30 +03:00
|
|
|
const (
|
|
|
|
// DomainXMLFlagSecure dumps XML with sensitive information included.
|
|
|
|
DomainXMLFlagSecure DomainXMLFlags = 1 << iota
|
|
|
|
|
|
|
|
// DomainXMLFlagInactive dumps XML with inactive domain information.
|
|
|
|
DomainXMLFlagInactive
|
|
|
|
|
|
|
|
// DomainXMLFlagUpdateCPU dumps XML with guest CPU requirements according to the host CPU.
|
|
|
|
DomainXMLFlagUpdateCPU
|
|
|
|
|
|
|
|
// DomainXMLFlagMigratable dumps XML suitable for migration.
|
|
|
|
DomainXMLFlagMigratable
|
|
|
|
)
|
|
|
|
|
2017-12-15 16:55:41 +03:00
|
|
|
// scheduler parameters
|
|
|
|
const (
|
|
|
|
// DomainSchedulerCPUShares represents the propportional weight
|
|
|
|
// of the scheduler used on the host cpu, when using the posix
|
|
|
|
// scheduler. (ullong)
|
|
|
|
DomainSchedulerCPUShares = "cpu_shares"
|
|
|
|
|
|
|
|
// DomainSchedulerGlobalPeriod represents the enforcement period
|
|
|
|
// for a quota, in microseconds, for whole domain, when using the
|
|
|
|
// posix scheduler. (ullong)
|
|
|
|
DomainSchedulerGlobalPeriod = "global_period"
|
|
|
|
|
|
|
|
// DomainSchedulerGlobalQuota represents the maximum bandwidth to be
|
|
|
|
// used within a period for whole domain, when using the posix
|
|
|
|
// scheduler. (llong)
|
|
|
|
DomainSchedulerGlobalQuota = "global_quota"
|
|
|
|
|
|
|
|
// DomainSchedulerVCPUPeriod represents the enforcement period for a
|
|
|
|
// quota, in microseconds, for vcpus only, when using the posix
|
|
|
|
// scheduler. (ullong)
|
|
|
|
DomainSchedulerVCPUPeriod = "vcpu_period"
|
|
|
|
|
|
|
|
// DomainSchedulerVCPUQuota represents the maximum bandwidth to be
|
|
|
|
// used within a period for vcpus only, when using the posix
|
|
|
|
// scheduler. (llong)
|
|
|
|
DomainSchedulerVCPUQuota = "vcpu_quota"
|
|
|
|
|
|
|
|
// DomainSchedulerEmulatorPeriod represents the enforcement period
|
|
|
|
// for a quota in microseconds, when using the posix scheduler, for
|
|
|
|
// all emulator activity not tied to vcpus. (ullong)
|
|
|
|
DomainSchedulerEmulatorPeriod = "emulator_period"
|
|
|
|
|
|
|
|
// DomainSchedulerEmulatorQuota represents the maximum bandwidth to be
|
|
|
|
// used within a period for all emulator activity not tied to vcpus,
|
|
|
|
// when using the posix scheduler. (llong)
|
|
|
|
DomainSchedulerEmulatorQuota = "emulator_quota"
|
|
|
|
|
|
|
|
// DomainSchedulerIOThreadPeriod represents the enforcement period for
|
|
|
|
// a quota, in microseconds, for IOThreads only, when using the posix
|
|
|
|
// scheduler. (ullong)
|
|
|
|
DomainSchedulerIOThreadPeriod = "iothread_period"
|
|
|
|
|
|
|
|
// DomainSchedulerIOThreadQuota represents the maximum bandwidth to be
|
|
|
|
// used within a period for IOThreads only, when using the posix
|
|
|
|
// scheduler. (llong)
|
|
|
|
DomainSchedulerIOThreadQuota = "iothread_quota"
|
|
|
|
|
|
|
|
// DomainSchedulerWeight represents the relative weight, when using the
|
|
|
|
// credit scheduler. (uint)
|
|
|
|
DomainSchedulerWeight = "weight"
|
|
|
|
|
|
|
|
// DomainSchedulerCap represents the maximum scheduler cap, when using
|
|
|
|
// the credit scheduler. (uint)
|
|
|
|
DomainSchedulerCap = "cap"
|
|
|
|
|
|
|
|
// DomainSchedulerReservation represents the scheduler reservation
|
|
|
|
// value, when using the allocation scheduler. (llong)
|
|
|
|
DomainSchedulerReservation = "reservation"
|
|
|
|
|
|
|
|
// DomainSchedulerLimit represents the scheduler limit value, when using
|
|
|
|
// the allocation scheduler. (llong)
|
|
|
|
DomainSchedulerLimit = "limit"
|
|
|
|
|
|
|
|
// DomainSchedulerShares represents the scheduler shares value, when
|
|
|
|
// using the allocation scheduler. (int)
|
|
|
|
DomainSchedulerShares = "shares"
|
|
|
|
)
|
|
|
|
|
2016-08-19 21:31:59 +03:00
|
|
|
// MigrateFlags specifies options when performing a migration.
|
|
|
|
type MigrateFlags uint32
|
|
|
|
|
|
|
|
const (
|
|
|
|
// MigrateFlagLive performs a zero-downtime live migration.
|
|
|
|
MigrateFlagLive MigrateFlags = 1 << iota
|
|
|
|
|
|
|
|
// MigrateFlagPeerToPeer creates a direct source to destination control channel.
|
|
|
|
MigrateFlagPeerToPeer
|
|
|
|
|
|
|
|
// MigrateFlagTunneled tunnels migration data over the libvirtd connection.
|
|
|
|
MigrateFlagTunneled
|
|
|
|
|
|
|
|
// MigrateFlagPersistDestination will persist the VM on the destination host.
|
|
|
|
MigrateFlagPersistDestination
|
|
|
|
|
|
|
|
// MigrateFlagUndefineSource undefines the VM on the source host.
|
|
|
|
MigrateFlagUndefineSource
|
|
|
|
|
|
|
|
// MigrateFlagPaused will pause the remote side VM.
|
|
|
|
MigrateFlagPaused
|
|
|
|
|
|
|
|
// MigrateFlagNonSharedDisk migrate non-shared storage with full disk copy.
|
|
|
|
MigrateFlagNonSharedDisk
|
|
|
|
|
|
|
|
// MigrateFlagNonSharedIncremental migrate non-shared storage with incremental copy.
|
|
|
|
MigrateFlagNonSharedIncremental
|
|
|
|
|
|
|
|
// MigrateFlagChangeProtection prevents any changes to the domain configuration through the whole migration process.
|
|
|
|
MigrateFlagChangeProtection
|
|
|
|
|
|
|
|
// MigrateFlagUnsafe will force a migration even when it is considered unsafe.
|
|
|
|
MigrateFlagUnsafe
|
|
|
|
|
|
|
|
// MigrateFlagOffline is used to perform an offline migration.
|
|
|
|
MigrateFlagOffline
|
|
|
|
|
|
|
|
// MigrateFlagCompressed compresses data during migration.
|
|
|
|
MigrateFlagCompressed
|
|
|
|
|
|
|
|
// MigrateFlagAbortOnError will abort a migration on I/O errors encountered during migration.
|
|
|
|
MigrateFlagAbortOnError
|
|
|
|
|
|
|
|
// MigrateFlagAutoConverge forces convergence.
|
|
|
|
MigrateFlagAutoConverge
|
|
|
|
|
|
|
|
// MigrateFlagRDMAPinAll enables RDMA memory pinning.
|
|
|
|
MigrateFlagRDMAPinAll
|
|
|
|
)
|
|
|
|
|
2016-09-29 00:29:46 +03:00
|
|
|
// UndefineFlags specifies options available when undefining a domain.
|
|
|
|
type UndefineFlags uint32
|
|
|
|
|
|
|
|
const (
|
|
|
|
// UndefineFlagManagedSave removes all domain managed save data.
|
|
|
|
UndefineFlagManagedSave UndefineFlags = 1 << iota
|
|
|
|
|
|
|
|
// UndefineFlagSnapshotsMetadata removes all domain snapshot metadata.
|
|
|
|
UndefineFlagSnapshotsMetadata
|
|
|
|
|
|
|
|
// UndefineFlagNVRAM removes all domain NVRAM files.
|
|
|
|
UndefineFlagNVRAM
|
|
|
|
)
|
|
|
|
|
2016-12-09 19:49:14 +03:00
|
|
|
// DomainDefineXMLFlags specifies options available when defining a domain.
|
|
|
|
type DomainDefineXMLFlags uint32
|
|
|
|
|
|
|
|
const (
|
|
|
|
// DefineValidate validates the XML document against schema
|
|
|
|
DefineValidate DomainDefineXMLFlags = 1
|
|
|
|
)
|
|
|
|
|
2016-11-03 06:43:42 +03:00
|
|
|
// DestroyFlags specifies options available when destroying a domain.
|
|
|
|
type DestroyFlags uint32
|
|
|
|
|
|
|
|
const (
|
|
|
|
// DestroyFlagDefault default behavior, forcefully terminate the domain.
|
|
|
|
DestroyFlagDefault DestroyFlags = 1 << iota
|
|
|
|
|
|
|
|
// DestroyFlagGraceful only sends a SIGTERM no SIGKILL.
|
|
|
|
DestroyFlagGraceful
|
|
|
|
)
|
|
|
|
|
2017-05-04 18:39:45 +03:00
|
|
|
// ShutdownFlags specifies options available when shutting down a domain.
|
|
|
|
type ShutdownFlags uint32
|
2017-06-02 20:28:20 +03:00
|
|
|
|
2017-05-04 18:39:45 +03:00
|
|
|
const (
|
|
|
|
// ShutdownAcpiPowerBtn - send ACPI event
|
|
|
|
ShutdownAcpiPowerBtn ShutdownFlags = 1 << iota
|
|
|
|
|
|
|
|
// ShutdownGuestAgent - use guest agent
|
|
|
|
ShutdownGuestAgent
|
|
|
|
|
|
|
|
// ShutdownInitctl - use initctl
|
|
|
|
ShutdownInitctl
|
|
|
|
|
|
|
|
// ShutdownSignal - use signal
|
|
|
|
ShutdownSignal
|
|
|
|
|
|
|
|
// ShutdownParavirt - use paravirt guest control
|
|
|
|
ShutdownParavirt
|
|
|
|
)
|
|
|
|
|
2016-12-02 20:54:04 +03:00
|
|
|
// DomainState specifies state of the domain
|
|
|
|
type DomainState uint32
|
|
|
|
|
|
|
|
const (
|
|
|
|
// DomainStateNoState No state
|
2017-10-24 00:23:17 +03:00
|
|
|
DomainStateNoState DomainState = iota
|
2016-12-02 20:54:04 +03:00
|
|
|
// DomainStateRunning The domain is running
|
|
|
|
DomainStateRunning
|
|
|
|
// DomainStateBlocked The domain is blocked on resource
|
|
|
|
DomainStateBlocked
|
|
|
|
// DomainStatePaused The domain is paused by user
|
|
|
|
DomainStatePaused
|
|
|
|
// DomainStateShutdown The domain is being shut down
|
|
|
|
DomainStateShutdown
|
|
|
|
// DomainStateShutoff The domain is shut off
|
|
|
|
DomainStateShutoff
|
|
|
|
// DomainStateCrashed The domain is crashed
|
|
|
|
DomainStateCrashed
|
|
|
|
// DomainStatePMSuspended The domain is suspended by guest power management
|
|
|
|
DomainStatePMSuspended
|
|
|
|
// DomainStateLast This value will increase over time as new events are added to the libvirt
|
|
|
|
// API. It reflects the last state supported by this version of the libvirt API.
|
|
|
|
DomainStateLast
|
|
|
|
)
|
|
|
|
|
2017-01-20 05:40:31 +03:00
|
|
|
// SecretUsageType specifies the usage for a libvirt secret.
|
|
|
|
type SecretUsageType uint32
|
|
|
|
|
|
|
|
const (
|
|
|
|
// SecretUsageTypeNone specifies no usage.
|
|
|
|
SecretUsageTypeNone SecretUsageType = iota
|
|
|
|
// SecretUsageTypeVolume specifies a volume secret.
|
|
|
|
SecretUsageTypeVolume
|
|
|
|
// SecretUsageTypeCeph specifies secrets for ceph devices.
|
|
|
|
SecretUsageTypeCeph
|
|
|
|
// SecretUsageTypeISCSI specifies secrets for ISCSI devices.
|
|
|
|
SecretUsageTypeISCSI
|
|
|
|
)
|
|
|
|
|
2017-01-07 00:58:27 +03:00
|
|
|
// StoragePoolsFlags specifies storage pools to list.
|
|
|
|
type StoragePoolsFlags uint32
|
|
|
|
|
|
|
|
// These flags come in groups; if all bits from a group are 0,
|
|
|
|
// then that group is not used to filter results.
|
|
|
|
const (
|
|
|
|
StoragePoolsFlagInactive = 1 << iota
|
|
|
|
StoragePoolsFlagActive
|
|
|
|
|
|
|
|
StoragePoolsFlagPersistent
|
|
|
|
StoragePoolsFlagTransient
|
|
|
|
|
|
|
|
StoragePoolsFlagAutostart
|
|
|
|
StoragePoolsFlagNoAutostart
|
|
|
|
|
|
|
|
// pools by type
|
|
|
|
StoragePoolsFlagDir
|
|
|
|
StoragePoolsFlagFS
|
|
|
|
StoragePoolsFlagNETFS
|
|
|
|
StoragePoolsFlagLogical
|
|
|
|
StoragePoolsFlagDisk
|
|
|
|
StoragePoolsFlagISCSI
|
|
|
|
StoragePoolsFlagSCSI
|
|
|
|
StoragePoolsFlagMPATH
|
|
|
|
StoragePoolsFlagRBD
|
|
|
|
StoragePoolsFlagSheepdog
|
|
|
|
StoragePoolsFlagGluster
|
|
|
|
StoragePoolsFlagZFS
|
|
|
|
)
|
|
|
|
|
2017-05-04 18:17:25 +03:00
|
|
|
// DomainCreateFlags specify options for starting domains
|
|
|
|
type DomainCreateFlags uint32
|
|
|
|
|
|
|
|
const (
|
|
|
|
// DomainCreateFlagPaused creates paused domain.
|
|
|
|
DomainCreateFlagPaused = 1 << iota
|
|
|
|
|
|
|
|
// DomainCreateFlagAutoDestroy destoy domain after libvirt connection closed.
|
|
|
|
DomainCreateFlagAutoDestroy
|
|
|
|
|
|
|
|
// DomainCreateFlagBypassCache avoid file system cache pollution.
|
|
|
|
DomainCreateFlagBypassCache
|
|
|
|
|
|
|
|
// DomainCreateFlagStartForceBoot boot, discarding any managed save
|
|
|
|
DomainCreateFlagStartForceBoot
|
|
|
|
|
|
|
|
// DomainCreateFlagStartValidate validate the XML document against schema
|
|
|
|
DomainCreateFlagStartValidate
|
|
|
|
)
|
|
|
|
|
2017-05-04 19:30:22 +03:00
|
|
|
// RebootFlags specifies domain reboot methods
|
|
|
|
type RebootFlags uint32
|
2017-06-02 20:28:20 +03:00
|
|
|
|
2017-05-04 19:30:22 +03:00
|
|
|
const (
|
|
|
|
// RebootAcpiPowerBtn - send ACPI event
|
|
|
|
RebootAcpiPowerBtn RebootFlags = 1 << iota
|
|
|
|
|
|
|
|
// RebootGuestAgent - use guest agent
|
|
|
|
RebootGuestAgent
|
|
|
|
|
|
|
|
// RebootInitctl - use initctl
|
|
|
|
RebootInitctl
|
|
|
|
|
|
|
|
// RebootSignal - use signal
|
|
|
|
RebootSignal
|
|
|
|
|
|
|
|
// RebootParavirt - use paravirt guest control
|
|
|
|
RebootParavirt
|
|
|
|
)
|
|
|
|
|
2017-06-02 20:28:20 +03:00
|
|
|
// DomainMemoryStatTag specifies domain memory tags
|
|
|
|
type DomainMemoryStatTag uint32
|
|
|
|
|
|
|
|
const (
|
|
|
|
// DomainMemoryStatTagSwapIn - The total amount of data read from swap space (in kB).
|
|
|
|
DomainMemoryStatTagSwapIn DomainMemoryStatTag = iota
|
|
|
|
|
|
|
|
// DomainMemoryStatTagSwapOut - The total amount of memory written out to swap space (in kB).
|
|
|
|
DomainMemoryStatTagSwapOut
|
|
|
|
|
|
|
|
// DomainMemoryStatTagMajorFault - Page faults occur when a process makes a valid access to virtual memory
|
|
|
|
// that is not available. When servicing the page fault, if disk IO is
|
|
|
|
// required, it is considered a major fault.
|
|
|
|
// These are expressed as the number of faults that have occurred.
|
|
|
|
DomainMemoryStatTagMajorFault
|
|
|
|
|
|
|
|
// DomainMemoryStatTagMinorFault - If the page fault not require disk IO, it is a minor fault.
|
|
|
|
DomainMemoryStatTagMinorFault
|
|
|
|
|
|
|
|
// DomainMemoryStatTagUnused - The amount of memory left completely unused by the system (in kB).
|
|
|
|
DomainMemoryStatTagUnused
|
|
|
|
|
|
|
|
// DomainMemoryStatTagAvailable - The total amount of usable memory as seen by the domain (in kB).
|
|
|
|
DomainMemoryStatTagAvailable
|
|
|
|
|
|
|
|
// DomainMemoryStatTagActualBalloon - Current balloon value (in KB).
|
|
|
|
DomainMemoryStatTagActualBalloon
|
|
|
|
|
|
|
|
// DomainMemoryStatTagRss - Resident Set Size of the process running the domain (in KB).
|
|
|
|
DomainMemoryStatTagRss
|
|
|
|
|
|
|
|
// DomainMemoryStatTagUsable - How much the balloon can be inflated without pushing the guest system
|
|
|
|
// to swap, corresponds to 'Available' in /proc/meminfo
|
|
|
|
DomainMemoryStatTagUsable
|
|
|
|
|
|
|
|
// DomainMemoryStatTagLastUpdate - Timestamp of the last update of statistics, in seconds.
|
|
|
|
DomainMemoryStatTagLastUpdate
|
|
|
|
|
|
|
|
// DomainMemoryStatTagNr - The number of statistics supported by this version of the interface.
|
|
|
|
DomainMemoryStatTagNr
|
|
|
|
)
|
|
|
|
|
2016-09-30 18:54:47 +03:00
|
|
|
// Capabilities returns an XML document describing the host's capabilties.
|
|
|
|
func (l *Libvirt) Capabilities() ([]byte, error) {
|
2017-11-16 22:17:46 +03:00
|
|
|
caps, err := l.ConnectGetCapabilities()
|
2016-09-30 18:54:47 +03:00
|
|
|
return []byte(caps), err
|
|
|
|
}
|
|
|
|
|
2016-05-20 03:05:37 +03:00
|
|
|
// Connect establishes communication with the libvirt server.
|
|
|
|
// The underlying libvirt socket connection must be previously established.
|
|
|
|
func (l *Libvirt) Connect() error {
|
|
|
|
return l.connect()
|
|
|
|
}
|
|
|
|
|
|
|
|
// Disconnect shuts down communication with the libvirt server
|
|
|
|
// and closes the underlying net.Conn.
|
|
|
|
func (l *Libvirt) Disconnect() error {
|
|
|
|
// close event streams
|
|
|
|
for id := range l.events {
|
|
|
|
if err := l.removeStream(id); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// inform libvirt we're done
|
|
|
|
if err := l.disconnect(); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
return l.conn.Close()
|
|
|
|
}
|
|
|
|
|
|
|
|
// Domains returns a list of all domains managed by libvirt.
|
2017-11-20 21:50:32 +03:00
|
|
|
func (l *Libvirt) Domains() ([]Domain, error) {
|
2016-05-20 03:05:37 +03:00
|
|
|
// these are the flags as passed by `virsh`, defined in:
|
|
|
|
// src/remote/remote_protocol.x # remote_connect_list_all_domains_args
|
2017-11-16 22:17:46 +03:00
|
|
|
domains, _, err := l.ConnectListAllDomains(1, 3)
|
|
|
|
return domains, err
|
2016-05-20 03:05:37 +03:00
|
|
|
}
|
|
|
|
|
2016-12-02 20:54:04 +03:00
|
|
|
// DomainState returns state of the domain managed by libvirt.
|
|
|
|
func (l *Libvirt) DomainState(dom string) (DomainState, error) {
|
|
|
|
d, err := l.lookup(dom)
|
|
|
|
if err != nil {
|
|
|
|
return DomainStateNoState, err
|
|
|
|
}
|
|
|
|
|
2017-11-16 22:17:46 +03:00
|
|
|
state, _, err := l.DomainGetState(d, 0)
|
|
|
|
return DomainState(state), err
|
2016-12-02 20:54:04 +03:00
|
|
|
}
|
|
|
|
|
2016-05-20 03:05:37 +03:00
|
|
|
// Events streams domain events.
|
|
|
|
// 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) Events(dom string) (<-chan DomainEvent, error) {
|
|
|
|
d, err := l.lookup(dom)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
payload := struct {
|
|
|
|
Padding [4]byte
|
2017-11-20 21:50:32 +03:00
|
|
|
Domain Domain
|
2016-05-20 03:05:37 +03:00
|
|
|
Event [2]byte
|
|
|
|
Flags [2]byte
|
|
|
|
}{
|
|
|
|
Padding: [4]byte{0x0, 0x0, 0x1, 0x0},
|
2017-11-13 23:18:18 +03:00
|
|
|
Domain: d,
|
2016-05-20 03:05:37 +03:00
|
|
|
Event: [2]byte{0x0, 0x0},
|
|
|
|
Flags: [2]byte{0x0, 0x0},
|
|
|
|
}
|
|
|
|
|
|
|
|
buf, err := encode(&payload)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2016-05-20 05:56:27 +03:00
|
|
|
resp, err := l.request(constants.QEMUConnectDomainMonitorEventRegister, constants.ProgramQEMU, &buf)
|
2016-05-20 03:05:37 +03:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
res := <-resp
|
|
|
|
if res.Status != StatusOK {
|
2017-08-31 18:21:31 +03:00
|
|
|
err = decodeError(res.Payload)
|
2016-05-20 03:05:37 +03:00
|
|
|
if err == ErrUnsupported {
|
|
|
|
return nil, ErrEventsNotSupported
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil, decodeError(res.Payload)
|
|
|
|
}
|
|
|
|
|
|
|
|
dec := xdr.NewDecoder(bytes.NewReader(res.Payload))
|
|
|
|
|
|
|
|
cbID, _, err := dec.DecodeUint()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
stream := make(chan *DomainEvent)
|
|
|
|
l.addStream(cbID, stream)
|
|
|
|
c := make(chan DomainEvent)
|
|
|
|
go func() {
|
|
|
|
// process events
|
|
|
|
for e := range stream {
|
|
|
|
c <- *e
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
return c, nil
|
|
|
|
}
|
|
|
|
|
2016-08-19 21:31:59 +03:00
|
|
|
// Migrate synchronously migrates the domain specified by dom, e.g.,
|
|
|
|
// 'prod-lb-01', to the destination hypervisor specified by dest, e.g.,
|
|
|
|
// 'qemu+tcp://example.com/system'. The flags argument determines the
|
|
|
|
// type of migration and how it will be performed. For more information
|
|
|
|
// on available migration flags and their meaning, see MigrateFlag*.
|
|
|
|
func (l *Libvirt) Migrate(dom string, dest string, flags MigrateFlags) error {
|
|
|
|
_, err := url.Parse(dest)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
d, err := l.lookup(dom)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Two unknowns remain here , Libvirt specifies RemoteParameters
|
|
|
|
// and CookieIn. In testing both values are always set to 0 by virsh
|
|
|
|
// and the source does not provide clear definitions of their purpose.
|
|
|
|
// For now, using the same zero'd values as done by virsh will be Good Enough.
|
2017-11-16 22:17:46 +03:00
|
|
|
destURI := []string{dest}
|
|
|
|
remoteParams := []TypedParam{}
|
|
|
|
cookieIn := []byte{}
|
|
|
|
_, err = l.DomainMigratePerform3Params(d, destURI, remoteParams, cookieIn, uint32(flags))
|
|
|
|
return err
|
2016-08-19 21:31:59 +03:00
|
|
|
}
|
|
|
|
|
2016-08-02 01:43:50 +03:00
|
|
|
// MigrateSetMaxSpeed set the maximum migration bandwidth (in MiB/s) for a
|
|
|
|
// domain which is being migrated to another host. Specifying a negative value
|
|
|
|
// results in an essentially unlimited value being provided to the hypervisor.
|
|
|
|
func (l *Libvirt) MigrateSetMaxSpeed(dom string, speed int64) error {
|
|
|
|
d, err := l.lookup(dom)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2017-11-16 22:17:46 +03:00
|
|
|
return l.DomainMigrateSetMaxSpeed(d, uint64(speed), 0)
|
2016-08-02 01:43:50 +03:00
|
|
|
}
|
|
|
|
|
2016-05-20 03:05:37 +03:00
|
|
|
// 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 {
|
2017-11-20 21:50:32 +03:00
|
|
|
Domain Domain
|
2016-05-20 03:05:37 +03:00
|
|
|
Command []byte
|
|
|
|
Flags uint32
|
|
|
|
}{
|
2017-11-13 23:18:18 +03:00
|
|
|
Domain: d,
|
2016-05-20 03:05:37 +03:00
|
|
|
Command: cmd,
|
|
|
|
Flags: 0,
|
|
|
|
}
|
|
|
|
|
|
|
|
buf, err := encode(&payload)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2016-05-20 05:56:27 +03:00
|
|
|
resp, err := l.request(constants.QEMUDomainMonitor, constants.ProgramQEMU, &buf)
|
2016-05-20 03:05:37 +03:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
res := <-resp
|
2016-07-19 19:27:31 +03:00
|
|
|
// check for libvirt errors
|
2016-05-20 03:05:37 +03:00
|
|
|
if res.Status != StatusOK {
|
|
|
|
return nil, decodeError(res.Payload)
|
|
|
|
}
|
|
|
|
|
2016-07-19 19:27:31 +03:00
|
|
|
// check for QEMU process errors
|
|
|
|
if err = getQEMUError(res); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2016-05-20 03:05:37 +03:00
|
|
|
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
|
2016-07-19 19:27:31 +03:00
|
|
|
return bytes.TrimRight(data[4:], "\x00"), nil
|
2016-05-20 03:05:37 +03:00
|
|
|
}
|
|
|
|
|
2017-01-20 05:40:31 +03:00
|
|
|
// Secrets returns all secrets managed by the libvirt daemon.
|
2017-11-20 21:50:32 +03:00
|
|
|
func (l *Libvirt) Secrets() ([]Secret, error) {
|
2017-11-17 01:14:05 +03:00
|
|
|
secrets, _, err := l.ConnectListAllSecrets(1, 0)
|
|
|
|
return secrets, err
|
2017-01-20 05:40:31 +03:00
|
|
|
}
|
|
|
|
|
2017-01-10 00:54:30 +03:00
|
|
|
// StoragePool returns the storage pool associated with the provided name.
|
|
|
|
// An error is returned if the requested storage pool is not found.
|
2017-11-20 21:50:32 +03:00
|
|
|
func (l *Libvirt) StoragePool(name string) (StoragePool, error) {
|
2017-11-16 22:17:46 +03:00
|
|
|
return l.StoragePoolLookupByName(name)
|
2017-01-10 00:54:30 +03:00
|
|
|
}
|
|
|
|
|
2017-01-07 00:58:27 +03:00
|
|
|
// StoragePools returns a list of defined storage pools. Pools are filtered by
|
|
|
|
// the provided flags. See StoragePools*.
|
2017-11-20 21:50:32 +03:00
|
|
|
func (l *Libvirt) StoragePools(flags StoragePoolsFlags) ([]StoragePool, error) {
|
2017-11-16 22:17:46 +03:00
|
|
|
pools, _, err := l.ConnectListAllStoragePools(1, uint32(flags))
|
|
|
|
return pools, err
|
2017-01-07 00:58:27 +03:00
|
|
|
}
|
|
|
|
|
2016-09-29 00:29:46 +03:00
|
|
|
// 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 UndefineFlag*.
|
|
|
|
func (l *Libvirt) Undefine(dom string, flags UndefineFlags) error {
|
|
|
|
d, err := l.lookup(dom)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2017-11-16 22:17:46 +03:00
|
|
|
return l.DomainUndefineFlags(d, uint32(flags))
|
2016-09-29 00:29:46 +03:00
|
|
|
}
|
|
|
|
|
2016-11-03 06:43:42 +03:00
|
|
|
// 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 DestroyFlag*.
|
|
|
|
func (l *Libvirt) Destroy(dom string, flags DestroyFlags) error {
|
|
|
|
d, err := l.lookup(dom)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2017-11-16 22:17:46 +03:00
|
|
|
return l.DomainDestroyFlags(d, uint32(flags))
|
2016-11-03 06:43:42 +03:00
|
|
|
}
|
|
|
|
|
2016-12-01 04:37:30 +03:00
|
|
|
// XML returns a domain's raw XML definition, akin to `virsh dumpxml <domain>`.
|
|
|
|
// See DomainXMLFlag* for optional flags.
|
|
|
|
func (l *Libvirt) XML(dom string, flags DomainXMLFlags) ([]byte, error) {
|
|
|
|
d, err := l.lookup(dom)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2017-11-16 22:17:46 +03:00
|
|
|
xml, err := l.DomainGetXMLDesc(d, uint32(flags))
|
|
|
|
return []byte(xml), err
|
2016-12-01 04:37:30 +03:00
|
|
|
}
|
|
|
|
|
2016-12-09 19:49:14 +03:00
|
|
|
// DefineXML defines a domain, but does not start it.
|
|
|
|
func (l *Libvirt) DefineXML(x []byte, flags DomainDefineXMLFlags) error {
|
2017-11-16 22:17:46 +03:00
|
|
|
_, err := l.DomainDefineXMLFlags(string(x), uint32(flags))
|
|
|
|
return err
|
2016-12-09 19:49:14 +03:00
|
|
|
}
|
|
|
|
|
2016-05-20 03:05:37 +03:00
|
|
|
// Version returns the version of the libvirt daemon.
|
|
|
|
func (l *Libvirt) Version() (string, error) {
|
2017-11-16 22:17:46 +03:00
|
|
|
ver, err := l.ConnectGetLibVersion()
|
2016-05-20 03:05:37 +03:00
|
|
|
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
|
2017-11-16 22:17:46 +03:00
|
|
|
major := ver / 1000000
|
|
|
|
ver %= 1000000
|
|
|
|
minor := ver / 1000
|
|
|
|
ver %= 1000
|
|
|
|
micro := ver
|
2016-05-20 03:05:37 +03:00
|
|
|
|
|
|
|
versionString := fmt.Sprintf("%d.%d.%d", major, minor, micro)
|
|
|
|
return versionString, nil
|
|
|
|
}
|
|
|
|
|
2017-05-04 18:39:45 +03:00
|
|
|
// 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.
|
|
|
|
func (l *Libvirt) Shutdown(dom string, flags ShutdownFlags) error {
|
|
|
|
d, err := l.lookup(dom)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2017-11-16 22:17:46 +03:00
|
|
|
return l.DomainShutdownFlags(d, uint32(flags))
|
2017-05-04 18:39:45 +03:00
|
|
|
}
|
|
|
|
|
2017-05-04 19:30:22 +03:00
|
|
|
// 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.
|
|
|
|
func (l *Libvirt) Reboot(dom string, flags RebootFlags) error {
|
|
|
|
d, err := l.lookup(dom)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2017-11-16 22:17:46 +03:00
|
|
|
return l.DomainReboot(d, uint32(flags))
|
2017-05-04 19:30:22 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// Reset resets domain immediately without any guest OS shutdown
|
|
|
|
func (l *Libvirt) Reset(dom string) error {
|
|
|
|
d, err := l.lookup(dom)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2017-11-16 22:17:46 +03:00
|
|
|
return l.DomainReset(d, 0)
|
2017-05-04 19:30:22 +03:00
|
|
|
}
|
|
|
|
|
2017-08-31 18:21:31 +03:00
|
|
|
// 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
|
|
|
|
}
|
|
|
|
|
|
|
|
// BlockIOTune-able values. These tunables are different for different
|
|
|
|
// hypervisors; currently only the tunables for QEMU are defined here. These are
|
|
|
|
// not necessarily the only possible values; different libvirt versions may add
|
|
|
|
// or remove parameters from this list.
|
|
|
|
const (
|
2017-09-15 17:48:46 +03:00
|
|
|
QEMUBlockIOGroupName = "group_name"
|
2017-08-31 18:21:31 +03:00
|
|
|
QEMUBlockIOTotalBytesSec = "total_bytes_sec"
|
|
|
|
QEMUBlockIOReadBytesSec = "read_bytes_sec"
|
|
|
|
QEMUBlockIOWriteBytesSec = "write_bytes_sec"
|
|
|
|
QEMUBlockIOTotalIOPSSec = "total_iops_sec"
|
|
|
|
QEMUBlockIOReadIOPSSec = "read_iops_sec"
|
|
|
|
QEMUBlockIOWriteIOPSSec = "write_iops_sec"
|
|
|
|
QEMUBlockIOTotalBytesSecMax = "total_bytes_sec_max"
|
|
|
|
QEMUBlockIOReadBytesSecMax = "read_bytes_sec_max"
|
|
|
|
QEMUBlockIOWriteBytesSecMax = "write_bytes_sec_max"
|
|
|
|
QEMUBlockIOTotalIOPSSecMax = "total_iops_sec_max"
|
|
|
|
QEMUBlockIOReadIOPSSecMax = "read_iops_sec_max"
|
|
|
|
QEMUBlockIOWriteIOPSSecMax = "write_iops_sec_max"
|
|
|
|
QEMUBlockIOSizeIOPSSec = "size_iops_sec"
|
|
|
|
QEMUBlockIOTotalBytesSecMaxLength = "total_bytes_sec_max_length"
|
|
|
|
QEMUBlockIOReadBytesSecMaxLength = "read_bytes_sec_max_length"
|
|
|
|
QEMUBlockIOWriteBytesSecMaxLength = "write_bytes_sec_max_length"
|
|
|
|
QEMUBlockIOTotalIOPSSecMaxLength = "total_iops_sec_max_length"
|
|
|
|
QEMUBlockIOReadIOPSSecMaxLength = "read_iops_sec_max_length"
|
|
|
|
QEMUBlockIOWriteIOPSSecMaxLength = "write_iops_sec_max_length"
|
|
|
|
)
|
|
|
|
|
|
|
|
// 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})
|
|
|
|
func (l *Libvirt) SetBlockIOTune(dom string, disk string, limits ...BlockLimit) error {
|
|
|
|
d, err := l.lookup(dom)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2017-11-15 02:59:55 +03:00
|
|
|
params := make([]TypedParam, len(limits))
|
|
|
|
for ix, limit := range limits {
|
2017-11-13 23:18:18 +03:00
|
|
|
tpval := NewTypedParamValueUllong(limit.Value)
|
2017-11-15 02:59:55 +03:00
|
|
|
params[ix] = TypedParam{Field: limit.Name, Value: tpval}
|
2017-08-31 18:21:31 +03:00
|
|
|
}
|
|
|
|
|
2017-11-16 22:17:46 +03:00
|
|
|
return l.DomainSetBlockIOTune(d, disk, params, FlagDomainAffectLive)
|
2017-08-31 18:21:31 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// GetBlockIOTune returns a slice containing the current block I/O tunables for
|
|
|
|
// a disk.
|
|
|
|
func (l *Libvirt) GetBlockIOTune(dom string, disk string) ([]BlockLimit, error) {
|
|
|
|
d, err := l.lookup(dom)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2017-11-16 22:17:46 +03:00
|
|
|
lims, _, err := l.DomainGetBlockIOTune(d, []string{disk}, 32, FlagTypedParamStringOkay)
|
2017-08-31 18:21:31 +03:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2017-09-15 17:48:46 +03:00
|
|
|
var limits []BlockLimit
|
2017-08-31 18:21:31 +03:00
|
|
|
|
2017-09-15 17:48:46 +03:00
|
|
|
// 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.
|
2017-11-15 02:59:55 +03:00
|
|
|
for _, lim := range lims {
|
|
|
|
var l BlockLimit
|
|
|
|
name := lim.Field
|
|
|
|
switch lim.Value.Get().(type) {
|
|
|
|
case uint64:
|
|
|
|
l = BlockLimit{Name: name, Value: lim.Value.Get().(uint64)}
|
2017-09-15 17:48:46 +03:00
|
|
|
}
|
2017-11-15 02:59:55 +03:00
|
|
|
limits = append(limits, l)
|
|
|
|
}
|
|
|
|
|
2017-08-31 18:21:31 +03:00
|
|
|
return limits, nil
|
|
|
|
}
|
|
|
|
|
2016-05-20 03:05:37 +03:00
|
|
|
// lookup returns a domain as seen by libvirt.
|
2017-11-20 21:50:32 +03:00
|
|
|
func (l *Libvirt) lookup(name string) (Domain, error) {
|
2017-11-17 01:14:05 +03:00
|
|
|
return l.DomainLookupByName(name)
|
2016-05-20 03:05:37 +03:00
|
|
|
}
|
|
|
|
|
2016-07-19 19:27:31 +03:00
|
|
|
// 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
|
|
|
|
}
|
|
|
|
|
2016-05-20 03:05:37 +03:00
|
|
|
// 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),
|
2017-12-07 20:47:52 +03:00
|
|
|
mu: &sync.Mutex{},
|
2016-05-20 03:05:37 +03:00
|
|
|
callbacks: make(map[uint32]chan response),
|
|
|
|
events: make(map[uint32]chan *DomainEvent),
|
|
|
|
}
|
|
|
|
|
|
|
|
go l.listen()
|
|
|
|
|
|
|
|
return l
|
|
|
|
}
|