From c09ce7b21d146df7ce633ea03d0d0c480d40e803 Mon Sep 17 00:00:00 2001 From: Alexander Polakov Date: Thu, 4 May 2017 19:30:22 +0300 Subject: [PATCH] Add Reboot and Reset methods (#37) --- internal/constants/constants.go | 2 + libvirt.go | 86 +++++++++++++++++++++++++++++++++ libvirt_test.go | 19 ++++++++ libvirttest/libvirt.go | 14 ++++++ 4 files changed, 121 insertions(+) diff --git a/internal/constants/constants.go b/internal/constants/constants.go index c1579ad..48c2311 100644 --- a/internal/constants/constants.go +++ b/internal/constants/constants.go @@ -41,6 +41,7 @@ const ( ProcConnectGetCapabilties = 7 ProcDomainGetXMLDesc = 14 ProcDomainLookupByName = 23 + ProcDomainReboot = 27 ProcAuthList = 66 ProcStoragePoolRefresh = 83 ProcStoragePoolLookupByName = 84 @@ -50,6 +51,7 @@ const ( ProcDomainGetState = 212 ProcDomainUndefineFlags = 231 ProcDomainDestroyFlags = 234 + ProcDomainReset = 245 ProcDomainShutdownFlags = 258 ProcConnectListAllDomains = 273 ProcConnectListAllStoragePools = 281 diff --git a/libvirt.go b/libvirt.go index 1c775e4..e58fb8d 100644 --- a/libvirt.go +++ b/libvirt.go @@ -299,6 +299,25 @@ const ( DomainCreateFlagStartValidate ) +// RebootFlags specifies domain reboot methods +type RebootFlags uint32 +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 +) + // Capabilities returns an XML document describing the host's capabilties. func (l *Libvirt) Capabilities() ([]byte, error) { resp, err := l.request(constants.ProcConnectGetCapabilties, constants.ProgramRemote, nil) @@ -1020,6 +1039,73 @@ func (l *Libvirt) Shutdown(dom string, flags ShutdownFlags) error { return nil } +// 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 + } + + payload := struct { + Domain Domain + Flags RebootFlags + }{ + Domain: *d, + Flags: flags, + } + + buf, err := encode(&payload) + if err != nil { + return err + } + + resp, err := l.request(constants.ProcDomainReboot, constants.ProgramRemote, &buf) + if err != nil { + return err + } + + r := <-resp + if r.Status != StatusOK { + return decodeError(r.Payload) + } + + return nil +} + +// 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 + } + + payload := struct { + Domain Domain + Flags uint32 + }{ + Domain: *d, + Flags: 0, + } + + buf, err := encode(&payload) + if err != nil { + return err + } + + resp, err := l.request(constants.ProcDomainReset, 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 47d8f96..8187f86 100644 --- a/libvirt_test.go +++ b/libvirt_test.go @@ -390,3 +390,22 @@ func TestShutdown(t *testing.T) { t.Fatalf("unexpected shutdown error: %v", err) } } + +func TestReboot(t *testing.T) { + conn := libvirttest.New() + l := New(conn) + + var flags RebootFlags + if err := l.Reboot("test", flags); err != nil { + t.Fatalf("unexpected reboot error: %v", err) + } +} + +func TestReset(t *testing.T) { + conn := libvirttest.New() + l := New(conn) + + if err := l.Reset("test"); err != nil { + t.Fatalf("unexpected reset error: %v", err) + } +} diff --git a/libvirttest/libvirt.go b/libvirttest/libvirt.go index f0bf087..fc1fedf 100644 --- a/libvirttest/libvirt.go +++ b/libvirttest/libvirt.go @@ -369,6 +369,16 @@ var testShutdownReply = []byte{ 0x00, 0x00, 0x00, 0x00, // status } +var testRebootReply = []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 @@ -446,6 +456,10 @@ func (m *MockLibvirt) handleRemote(procedure uint32, conn net.Conn) { conn.Write(m.reply(testDestroyReply)) case constants.ProcDomainDefineXMLFlags: conn.Write(m.reply(testDefineXML)) + case constants.ProcDomainReboot: + conn.Write(m.reply(testRebootReply)) + case constants.ProcDomainReset: + conn.Write(m.reply(testRebootReply)) case constants.ProcDomainCreateWithFlags: conn.Write(m.reply(testCreateWithFlags)) case constants.ProcDomainShutdownFlags: