diff --git a/internal/constants/constants.go b/internal/constants/constants.go index 9499451..c1579ad 100644 --- a/internal/constants/constants.go +++ b/internal/constants/constants.go @@ -50,6 +50,7 @@ const ( ProcDomainGetState = 212 ProcDomainUndefineFlags = 231 ProcDomainDestroyFlags = 234 + ProcDomainShutdownFlags = 258 ProcConnectListAllDomains = 273 ProcConnectListAllStoragePools = 281 ProcConnectListAllSecrets = 287 diff --git a/libvirt.go b/libvirt.go index d2609fa..1c775e4 100644 --- a/libvirt.go +++ b/libvirt.go @@ -191,6 +191,25 @@ const ( DestroyFlagGraceful ) +// ShutdownFlags specifies options available when shutting down a domain. +type ShutdownFlags uint32 +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 +) + // DomainState specifies state of the domain type DomainState uint32 @@ -967,6 +986,40 @@ func (l *Libvirt) Version() (string, error) { 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. +func (l *Libvirt) Shutdown(dom string, flags ShutdownFlags) error { + d, err := l.lookup(dom) + if err != nil { + return err + } + + payload := struct { + Domain Domain + Flags ShutdownFlags + }{ + Domain: *d, + Flags: flags, + } + + buf, err := encode(&payload) + if err != nil { + return err + } + + resp, err := l.request(constants.ProcDomainShutdownFlags, constants.ProgramRemote, &buf) + if err != nil { + return err + } + + r := <-resp + if r.Status != StatusOK { + return decodeError(r.Payload) + } + + return nil +} + // lookup returns a domain as seen by libvirt. func (l *Libvirt) lookup(name string) (*Domain, error) { payload := struct { diff --git a/libvirt_test.go b/libvirt_test.go index c9a9e56..47d8f96 100644 --- a/libvirt_test.go +++ b/libvirt_test.go @@ -380,3 +380,13 @@ func TestDomainCreateWithFlags(t *testing.T) { t.Fatalf("unexpected create error: %v", err) } } + +func TestShutdown(t *testing.T) { + conn := libvirttest.New() + l := New(conn) + + var flags ShutdownFlags + if err := l.Shutdown("test", flags); err != nil { + t.Fatalf("unexpected shutdown error: %v", err) + } +} diff --git a/libvirttest/libvirt.go b/libvirttest/libvirt.go index a410949..f0bf087 100644 --- a/libvirttest/libvirt.go +++ b/libvirttest/libvirt.go @@ -359,6 +359,16 @@ var testCreateWithFlags = []byte{ 0xff, 0xff, 0xff, 0xff, // id } +var testShutdownReply = []byte{ + 0x00, 0x00, 0x00, 0x1c, // length + 0x20, 0x00, 0x80, 0x86, // program + 0x00, 0x00, 0x00, 0x01, // version + 0x00, 0x00, 0x00, 0xea, // procedure + 0x00, 0x00, 0x00, 0x01, // type + 0x00, 0x00, 0x00, 0x00, // serial + 0x00, 0x00, 0x00, 0x00, // status +} + // MockLibvirt provides a mock libvirt server for testing. type MockLibvirt struct { net.Conn @@ -438,6 +448,8 @@ func (m *MockLibvirt) handleRemote(procedure uint32, conn net.Conn) { conn.Write(m.reply(testDefineXML)) case constants.ProcDomainCreateWithFlags: conn.Write(m.reply(testCreateWithFlags)) + case constants.ProcDomainShutdownFlags: + conn.Write(m.reply(testShutdownReply)) default: fmt.Fprintln(os.Stderr, "unknown procedure", procedure) }