podman
769 строк · 22.2 Кб
1// Copyright 2018 The go-libvirt Authors.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package libvirt
16
17// We'll use c-for-go to extract the consts and typedefs from the libvirt
18// sources so we don't have to duplicate them here.
19//go:generate scripts/gen-consts.sh
20
21import (
22"bytes"
23"context"
24"encoding/json"
25"errors"
26"fmt"
27"net"
28"sync"
29"syscall"
30"time"
31
32"github.com/digitalocean/go-libvirt/internal/constants"
33"github.com/digitalocean/go-libvirt/internal/event"
34xdr "github.com/digitalocean/go-libvirt/internal/go-xdr/xdr2"
35"github.com/digitalocean/go-libvirt/socket"
36"github.com/digitalocean/go-libvirt/socket/dialers"
37)
38
39// ErrEventsNotSupported is returned by Events() if event streams
40// are unsupported by either QEMU or libvirt.
41var ErrEventsNotSupported = errors.New("event monitor is not supported")
42
43// ConnectURI defines a type for driver URIs for libvirt
44// the defined constants are *not* exhaustive as there are also options
45// e.g. to connect remote via SSH
46type ConnectURI string
47
48const (
49// QEMUSystem connects to a QEMU system mode daemon
50QEMUSystem ConnectURI = "qemu:///system"
51// QEMUSession connects to a QEMU session mode daemon (unprivileged)
52QEMUSession ConnectURI = "qemu:///session"
53// XenSystem connects to a Xen system mode daemon
54XenSystem ConnectURI = "xen:///system"
55//TestDefault connect to default mock driver
56TestDefault ConnectURI = "test:///default"
57
58// disconnectedTimeout is how long to wait for disconnect cleanup to
59// complete
60disconnectTimeout = 5 * time.Second
61)
62
63// Libvirt implements libvirt's remote procedure call protocol.
64type Libvirt struct {
65// socket connection
66socket *socket.Socket
67// closed after cleanup complete following the underlying connection to
68// libvirt being disconnected.
69disconnected chan struct{}
70
71// method callbacks
72cmux sync.RWMutex
73callbacks map[int32]chan response
74
75// event listeners
76emux sync.RWMutex
77events map[int32]*event.Stream
78
79// next request serial number
80s int32
81}
82
83// DomainEvent represents a libvirt domain event.
84type DomainEvent struct {
85CallbackID int32
86Domain Domain
87Event string
88Seconds uint64
89Microseconds uint32
90Padding uint8
91Details []byte
92}
93
94// GetCallbackID returns the callback ID of a QEMU domain event.
95func (de DomainEvent) GetCallbackID() int32 {
96return de.CallbackID
97}
98
99// GetCallbackID returns the callback ID of a libvirt lifecycle event.
100func (m DomainEventCallbackLifecycleMsg) GetCallbackID() int32 {
101return m.CallbackID
102}
103
104// qemuError represents a QEMU process error.
105type qemuError struct {
106Error struct {
107Class string `json:"class"`
108Description string `json:"desc"`
109} `json:"error"`
110}
111
112// Capabilities returns an XML document describing the host's capabilties.
113func (l *Libvirt) Capabilities() ([]byte, error) {
114caps, err := l.ConnectGetCapabilities()
115return []byte(caps), err
116}
117
118// called at connection time, authenticating with all supported auth types
119func (l *Libvirt) authenticate() error {
120// libvirt requires that we call auth-list prior to connecting,
121// even when no authentication is used.
122resp, err := l.AuthList()
123if err != nil {
124return err
125}
126
127for _, auth := range resp {
128switch auth {
129case constants.AuthNone:
130case constants.AuthPolkit:
131_, err := l.AuthPolkit()
132if err != nil {
133return err
134}
135default:
136continue
137}
138break
139}
140return nil
141}
142
143func (l *Libvirt) initLibvirtComms(uri ConnectURI) error {
144payload := struct {
145Padding [3]byte
146Name string
147Flags uint32
148}{
149Padding: [3]byte{0x1, 0x0, 0x0},
150Name: string(uri),
151Flags: 0,
152}
153
154buf, err := encode(&payload)
155if err != nil {
156return err
157}
158
159err = l.authenticate()
160if err != nil {
161return err
162}
163
164_, err = l.request(constants.ProcConnectOpen, constants.Program, buf)
165if err != nil {
166return err
167}
168
169return nil
170}
171
172// ConnectToURI establishes communication with the specified libvirt driver
173// The underlying libvirt socket connection will be created via the dialer.
174// Since the connection can be lost, the Disconnected function can be used
175// to monitor for a lost connection.
176func (l *Libvirt) ConnectToURI(uri ConnectURI) error {
177err := l.socket.Connect()
178if err != nil {
179return err
180}
181
182// Start watching the underlying socket connection immediately.
183// If we don't, and Libvirt goes away partway through initLibvirtComms,
184// then the callbacks that initLibvirtComms has registered will never
185// be closed, and therefore it will be stuck waiting for data from a
186// channel that will never arrive.
187go l.waitAndDisconnect()
188
189err = l.initLibvirtComms(uri)
190if err != nil {
191l.socket.Disconnect()
192return err
193}
194
195l.disconnected = make(chan struct{})
196
197return nil
198}
199
200// Connect establishes communication with the libvirt server.
201// The underlying libvirt socket connection will be created via the dialer.
202// Since the connection can be lost, the Disconnected function can be used
203// to monitor for a lost connection.
204func (l *Libvirt) Connect() error {
205return l.ConnectToURI(QEMUSystem)
206}
207
208// Disconnect shuts down communication with the libvirt server and closes the
209// underlying net.Conn.
210func (l *Libvirt) Disconnect() error {
211// Ordering is important here. We want to make sure the connection is closed
212// before unsubscribing and deregistering the events and requests, to
213// prevent new requests from racing.
214_, err := l.request(constants.ProcConnectClose, constants.Program, nil)
215
216// syscall.EINVAL is returned by the socket pkg when things have already
217// been disconnected.
218if err != nil && err != syscall.EINVAL {
219return err
220}
221err = l.socket.Disconnect()
222if err != nil {
223return err
224}
225
226// wait for the listen goroutine to detect the lost connection and clean up
227// to happen once it returns. Safeguard with a timeout.
228// Things not fully cleaned up is better than a deadlock.
229select {
230case <-l.disconnected:
231case <-time.After(disconnectTimeout):
232}
233
234return err
235}
236
237// Disconnected allows callers to detect if the underlying connection
238// to libvirt has been closed. If the returned channel is closed, then
239// the connection to libvirt has been lost (or disconnected intentionally).
240func (l *Libvirt) Disconnected() <-chan struct{} {
241return l.disconnected
242}
243
244// Domains returns a list of all domains managed by libvirt.
245//
246// Deprecated: use ConnectListAllDomains instead.
247func (l *Libvirt) Domains() ([]Domain, error) {
248// these are the flags as passed by `virsh list --all`
249flags := ConnectListDomainsActive | ConnectListDomainsInactive
250domains, _, err := l.ConnectListAllDomains(1, flags)
251return domains, err
252}
253
254// DomainState returns state of the domain managed by libvirt.
255//
256// Deprecated: use DomainGetState instead.
257func (l *Libvirt) DomainState(dom string) (DomainState, error) {
258d, err := l.lookup(dom)
259if err != nil {
260return DomainNostate, err
261}
262
263state, _, err := l.DomainGetState(d, 0)
264return DomainState(state), err
265}
266
267// SubscribeQEMUEvents streams domain events until the provided context is
268// cancelled. If a problem is encountered setting up the event monitor
269// connection an error will be returned. Errors encountered during streaming
270// will cause the returned event channel to be closed. QEMU domain events.
271func (l *Libvirt) SubscribeQEMUEvents(ctx context.Context, dom string) (<-chan DomainEvent, error) {
272d, err := l.lookup(dom)
273if err != nil {
274return nil, err
275}
276
277callbackID, err := l.QEMUConnectDomainMonitorEventRegister([]Domain{d}, nil, 0)
278if err != nil {
279return nil, err
280}
281
282stream := event.NewStream(constants.QEMUProgram, callbackID)
283l.addStream(stream)
284ch := make(chan DomainEvent)
285go func() {
286ctx, cancel := context.WithCancel(ctx)
287defer cancel()
288defer l.unsubscribeQEMUEvents(stream)
289defer stream.Shutdown()
290defer close(ch)
291
292for {
293select {
294case ev, ok := <-stream.Recv():
295if !ok {
296return
297}
298ch <- *ev.(*DomainEvent)
299case <-ctx.Done():
300return
301}
302}
303}()
304
305return ch, nil
306}
307
308// unsubscribeQEMUEvents stops the flow of events from QEMU through libvirt.
309func (l *Libvirt) unsubscribeQEMUEvents(stream *event.Stream) error {
310err := l.QEMUConnectDomainMonitorEventDeregister(stream.CallbackID)
311l.removeStream(stream.CallbackID)
312
313return err
314}
315
316// SubscribeEvents allows the caller to subscribe to any of the event types
317// supported by libvirt. The events will continue to be streamed until the
318// caller cancels the provided context. After canceling the context, callers
319// should wait until the channel is closed to be sure they're collected all the
320// events.
321func (l *Libvirt) SubscribeEvents(ctx context.Context, eventID DomainEventID,
322dom OptDomain) (<-chan interface{}, error) {
323
324callbackID, err := l.ConnectDomainEventCallbackRegisterAny(int32(eventID), nil)
325if err != nil {
326return nil, err
327}
328
329stream := event.NewStream(constants.QEMUProgram, callbackID)
330l.addStream(stream)
331
332ch := make(chan interface{})
333go func() {
334ctx, cancel := context.WithCancel(ctx)
335defer cancel()
336defer l.unsubscribeEvents(stream)
337defer stream.Shutdown()
338defer func() { close(ch) }()
339
340for {
341select {
342case ev, ok := <-stream.Recv():
343if !ok {
344return
345}
346ch <- ev
347case <-ctx.Done():
348return
349}
350}
351}()
352
353return ch, nil
354}
355
356// unsubscribeEvents stops the flow of the specified events from libvirt. There
357// are two steps to this process: a call to libvirt to deregister our callback,
358// and then removing the callback from the list used by the `Route` function. If
359// the deregister call fails, we'll return the error, but still remove the
360// callback from the list. That's ok; if any events arrive after this point, the
361// Route function will drop them when it finds no registered handler.
362func (l *Libvirt) unsubscribeEvents(stream *event.Stream) error {
363err := l.ConnectDomainEventCallbackDeregisterAny(stream.CallbackID)
364l.removeStream(stream.CallbackID)
365
366return err
367}
368
369// LifecycleEvents streams lifecycle events until the provided context is
370// cancelled. If a problem is encountered setting up the event monitor
371// connection, an error will be returned. Errors encountered during streaming
372// will cause the returned event channel to be closed.
373func (l *Libvirt) LifecycleEvents(ctx context.Context) (<-chan DomainEventLifecycleMsg, error) {
374callbackID, err := l.ConnectDomainEventCallbackRegisterAny(int32(DomainEventIDLifecycle), nil)
375if err != nil {
376return nil, err
377}
378
379stream := event.NewStream(constants.Program, callbackID)
380l.addStream(stream)
381
382ch := make(chan DomainEventLifecycleMsg)
383
384go func() {
385ctx, cancel := context.WithCancel(ctx)
386defer cancel()
387defer l.unsubscribeEvents(stream)
388defer stream.Shutdown()
389defer func() { close(ch) }()
390
391for {
392select {
393case ev, ok := <-stream.Recv():
394if !ok {
395return
396}
397ch <- ev.(*DomainEventCallbackLifecycleMsg).Msg
398case <-ctx.Done():
399return
400}
401}
402}()
403
404return ch, nil
405}
406
407// Run executes the given QAPI command against a domain's QEMU instance.
408// For a list of available QAPI commands, see:
409// http://git.qemu.org/?p=qemu.git;a=blob;f=qapi-schema.json;hb=HEAD
410func (l *Libvirt) Run(dom string, cmd []byte) ([]byte, error) {
411d, err := l.lookup(dom)
412if err != nil {
413return nil, err
414}
415
416payload := struct {
417Domain Domain
418Command []byte
419Flags uint32
420}{
421Domain: d,
422Command: cmd,
423Flags: 0,
424}
425
426buf, err := encode(&payload)
427if err != nil {
428return nil, err
429}
430
431res, err := l.request(constants.QEMUProcDomainMonitorCommand, constants.QEMUProgram, buf)
432if err != nil {
433return nil, err
434}
435
436// check for QEMU process errors
437if err = getQEMUError(res); err != nil {
438return nil, err
439}
440
441r := bytes.NewReader(res.Payload)
442dec := xdr.NewDecoder(r)
443data, _, err := dec.DecodeFixedOpaque(int32(r.Len()))
444if err != nil {
445return nil, err
446}
447
448// drop QMP control characters from start of line, and drop
449// any trailing NULL characters from the end
450return bytes.TrimRight(data[4:], "\x00"), nil
451}
452
453// Secrets returns all secrets managed by the libvirt daemon.
454//
455// Deprecated: use ConnectListAllSecrets instead.
456func (l *Libvirt) Secrets() ([]Secret, error) {
457secrets, _, err := l.ConnectListAllSecrets(1, 0)
458return secrets, err
459}
460
461// StoragePool returns the storage pool associated with the provided name.
462// An error is returned if the requested storage pool is not found.
463//
464// Deprecated: use StoragePoolLookupByName instead.
465func (l *Libvirt) StoragePool(name string) (StoragePool, error) {
466return l.StoragePoolLookupByName(name)
467}
468
469// StoragePools returns a list of defined storage pools. Pools are filtered by
470// the provided flags. See StoragePools*.
471//
472// Deprecated: use ConnectListAllStoragePools instead.
473func (l *Libvirt) StoragePools(flags ConnectListAllStoragePoolsFlags) ([]StoragePool, error) {
474pools, _, err := l.ConnectListAllStoragePools(1, flags)
475return pools, err
476}
477
478// Undefine undefines the domain specified by dom, e.g., 'prod-lb-01'.
479// The flags argument allows additional options to be specified such as
480// cleaning up snapshot metadata. For more information on available
481// flags, see DomainUndefine*.
482//
483// Deprecated: use DomainUndefineFlags instead.
484func (l *Libvirt) Undefine(dom string, flags DomainUndefineFlagsValues) error {
485d, err := l.lookup(dom)
486if err != nil {
487return err
488}
489
490return l.DomainUndefineFlags(d, flags)
491}
492
493// Destroy destroys the domain specified by dom, e.g., 'prod-lb-01'.
494// The flags argument allows additional options to be specified such as
495// allowing a graceful shutdown with SIGTERM than SIGKILL.
496// For more information on available flags, see DomainDestroy*.
497//
498// Deprecated: use DomainDestroyFlags instead.
499func (l *Libvirt) Destroy(dom string, flags DomainDestroyFlagsValues) error {
500d, err := l.lookup(dom)
501if err != nil {
502return err
503}
504
505return l.DomainDestroyFlags(d, flags)
506}
507
508// XML returns a domain's raw XML definition, akin to `virsh dumpxml <domain>`.
509// See DomainXMLFlag* for optional flags.
510//
511// Deprecated: use DomainGetXMLDesc instead.
512func (l *Libvirt) XML(dom string, flags DomainXMLFlags) ([]byte, error) {
513d, err := l.lookup(dom)
514if err != nil {
515return nil, err
516}
517
518xml, err := l.DomainGetXMLDesc(d, flags)
519return []byte(xml), err
520}
521
522// DefineXML defines a domain, but does not start it.
523//
524// Deprecated: use DomainDefineXMLFlags instead.
525func (l *Libvirt) DefineXML(x []byte, flags DomainDefineFlags) error {
526_, err := l.DomainDefineXMLFlags(string(x), flags)
527return err
528}
529
530// Version returns the version of the libvirt daemon.
531//
532// Deprecated: use ConnectGetLibVersion instead.
533func (l *Libvirt) Version() (string, error) {
534ver, err := l.ConnectGetLibVersion()
535if err != nil {
536return "", err
537}
538
539// The version is provided as an int following this formula:
540// version * 1,000,000 + minor * 1000 + micro
541// See src/libvirt-host.c # virConnectGetLibVersion
542major := ver / 1000000
543ver %= 1000000
544minor := ver / 1000
545ver %= 1000
546micro := ver
547
548versionString := fmt.Sprintf("%d.%d.%d", major, minor, micro)
549return versionString, nil
550}
551
552// Shutdown shuts down a domain. Note that the guest OS may ignore the request.
553// If flags is set to 0 then the hypervisor will choose the method of shutdown it considers best.
554//
555// Deprecated: use DomainShutdownFlags instead.
556func (l *Libvirt) Shutdown(dom string, flags DomainShutdownFlagValues) error {
557d, err := l.lookup(dom)
558if err != nil {
559return err
560}
561
562return l.DomainShutdownFlags(d, flags)
563}
564
565// Reboot reboots the domain. Note that the guest OS may ignore the request.
566// If flags is set to zero, then the hypervisor will choose the method of shutdown it considers best.
567//
568// Deprecated: use DomainReboot instead.
569func (l *Libvirt) Reboot(dom string, flags DomainRebootFlagValues) error {
570d, err := l.lookup(dom)
571if err != nil {
572return err
573}
574
575return l.DomainReboot(d, flags)
576}
577
578// Reset resets domain immediately without any guest OS shutdown
579//
580// Deprecated: use DomainReset instead.
581func (l *Libvirt) Reset(dom string) error {
582d, err := l.lookup(dom)
583if err != nil {
584return err
585}
586
587return l.DomainReset(d, 0)
588}
589
590// BlockLimit contains a name and value pair for a Get/SetBlockIOTune limit. The
591// Name field is the name of the limit (to see a list of the limits that can be
592// applied, execute the 'blkdeviotune' command on a VM in virsh). Callers can
593// use the QEMUBlockIO... constants below for the Name value. The Value field is
594// the limit to apply.
595type BlockLimit struct {
596Name string
597Value uint64
598}
599
600// SetBlockIOTune changes the per-device block I/O tunables within a guest.
601// Parameters are the name of the VM, the name of the disk device to which the
602// limits should be applied, and 1 or more BlockLimit structs containing the
603// actual limits.
604//
605// The limits which can be applied here are enumerated in the QEMUBlockIO...
606// constants above, and you can also see the full list by executing the
607// 'blkdeviotune' command on a VM in virsh.
608//
609// Example usage:
610// SetBlockIOTune("vm-name", "vda", BlockLimit{libvirt.QEMUBlockIOWriteBytesSec, 1000000})
611//
612// Deprecated: use DomainSetBlockIOTune instead.
613func (l *Libvirt) SetBlockIOTune(dom string, disk string, limits ...BlockLimit) error {
614d, err := l.lookup(dom)
615if err != nil {
616return err
617}
618
619params := make([]TypedParam, len(limits))
620for ix, limit := range limits {
621tpval := NewTypedParamValueUllong(limit.Value)
622params[ix] = TypedParam{Field: limit.Name, Value: *tpval}
623}
624
625return l.DomainSetBlockIOTune(d, disk, params, uint32(DomainAffectLive))
626}
627
628// GetBlockIOTune returns a slice containing the current block I/O tunables for
629// a disk.
630//
631// Deprecated: use DomainGetBlockIOTune instead.
632func (l *Libvirt) GetBlockIOTune(dom string, disk string) ([]BlockLimit, error) {
633d, err := l.lookup(dom)
634if err != nil {
635return nil, err
636}
637
638lims, _, err := l.DomainGetBlockIOTune(d, []string{disk}, 32, uint32(TypedParamStringOkay))
639if err != nil {
640return nil, err
641}
642
643var limits []BlockLimit
644
645// now decode each of the returned TypedParams. To do this we read the field
646// name and type, then use the type information to decode the value.
647for _, lim := range lims {
648var l BlockLimit
649name := lim.Field
650switch lim.Value.I.(type) {
651case uint64:
652l = BlockLimit{Name: name, Value: lim.Value.I.(uint64)}
653}
654limits = append(limits, l)
655}
656
657return limits, nil
658}
659
660// lookup returns a domain as seen by libvirt.
661func (l *Libvirt) lookup(name string) (Domain, error) {
662return l.DomainLookupByName(name)
663}
664
665// getQEMUError checks the provided response for QEMU process errors.
666// If an error is found, it is extracted an returned, otherwise nil.
667func getQEMUError(r response) error {
668pl := bytes.NewReader(r.Payload)
669dec := xdr.NewDecoder(pl)
670
671s, _, err := dec.DecodeString()
672if err != nil {
673return err
674}
675
676var e qemuError
677if err = json.Unmarshal([]byte(s), &e); err != nil {
678return err
679}
680
681if e.Error.Description != "" {
682return errors.New(e.Error.Description)
683}
684
685return nil
686}
687
688func (l *Libvirt) waitAndDisconnect() {
689// wait for the socket to indicate if/when it's been disconnected
690<-l.socket.Disconnected()
691
692// close event streams
693l.removeAllStreams()
694
695// Deregister all callbacks to prevent blocking on clients with
696// outstanding requests
697l.deregisterAll()
698
699select {
700case <-l.disconnected:
701// l.disconnected is already closed, i.e., Libvirt.ConnectToURI
702// was unable to complete all phases of its connection and
703// so this hadn't been assigned to an open channel yet (it
704// is set to a closed channel in Libvirt.New*)
705//
706// Just return to avoid closing an already-closed channel.
707return
708default:
709// if we make it here then reading from l.disconnected is blocking,
710// which suggests that it is open and must be closed.
711}
712
713close(l.disconnected)
714}
715
716// NewWithDialer configures a new Libvirt object that can be used to perform
717// RPCs via libvirt's socket. The actual connection will not be established
718// until Connect is called. The same Libvirt object may be used to re-connect
719// multiple times.
720func NewWithDialer(dialer socket.Dialer) *Libvirt {
721l := &Libvirt{
722s: 0,
723disconnected: make(chan struct{}),
724callbacks: make(map[int32]chan response),
725events: make(map[int32]*event.Stream),
726}
727
728l.socket = socket.New(dialer, l)
729
730// we start with a closed channel since that indicates no connection
731close(l.disconnected)
732
733return l
734}
735
736// New configures a new Libvirt RPC connection.
737// This function only remains to retain backwards compatability.
738// When Libvirt's Connect function is called, the Dial will simply return the
739// connection passed in here and start a goroutine listening/reading from it.
740// If at any point the Disconnect function is called, any subsequent Connect
741// call will simply return an already closed connection.
742//
743// Deprecated: Please use NewWithDialer.
744func New(conn net.Conn) *Libvirt {
745return NewWithDialer(dialers.NewAlreadyConnected(conn))
746}
747
748// NetworkUpdateCompat is a wrapper over NetworkUpdate which swaps `Command` and `Section` when needed.
749// This function must be used instead of NetworkUpdate to be sure that the
750// NetworkUpdate call works both with older and newer libvirtd connections.
751//
752// libvirt on-wire protocol had a bug for a long time where Command and Section
753// were reversed. It's been fixed in newer libvirt versions, and backported to
754// some older versions. This helper detects what argument order libvirtd expects
755// and makes the correct NetworkUpdate call.
756func (l *Libvirt) NetworkUpdateCompat(Net Network, Command NetworkUpdateCommand, Section NetworkUpdateSection, ParentIndex int32, XML string, Flags NetworkUpdateFlags) (err error) {
757// This is defined in libvirt/src/libvirt_internal.h and thus not available in go-libvirt autogenerated code
758const virDrvFeatureNetworkUpdateHasCorrectOrder = 16
759hasCorrectOrder, err := l.ConnectSupportsFeature(virDrvFeatureNetworkUpdateHasCorrectOrder)
760if err != nil {
761return fmt.Errorf("failed to confirm argument order for NetworkUpdate: %w", err)
762}
763
764// https://gitlab.com/libvirt/libvirt/-/commit/b0f78d626a18bcecae3a4d165540ab88bfbfc9ee
765if hasCorrectOrder == 0 {
766return l.NetworkUpdate(Net, uint32(Section), uint32(Command), ParentIndex, XML, Flags)
767}
768return l.NetworkUpdate(Net, uint32(Command), uint32(Section), ParentIndex, XML, Flags)
769}
770