diff --git a/AUTHORS b/AUTHORS index 606522f..1abc5a7 100644 --- a/AUTHORS +++ b/AUTHORS @@ -15,3 +15,4 @@ Charlie Drage Michael Koppmann Simarpreet Singh Alexander Polyakov +Amanda Andrade diff --git a/internal/constants/constants.go b/internal/constants/constants.go index 48c2311..a7b7aba 100644 --- a/internal/constants/constants.go +++ b/internal/constants/constants.go @@ -41,17 +41,18 @@ const ( ProcConnectGetCapabilties = 7 ProcDomainGetXMLDesc = 14 ProcDomainLookupByName = 23 - ProcDomainReboot = 27 + ProcDomainReboot = 27 ProcAuthList = 66 ProcStoragePoolRefresh = 83 ProcStoragePoolLookupByName = 84 ProcConnectGetLibVersion = 157 + ProcDomainMemoryStats = 159 ProcDomainCreateWithFlags = 196 ProcDomainMigrateSetMaxSpeed = 207 ProcDomainGetState = 212 ProcDomainUndefineFlags = 231 ProcDomainDestroyFlags = 234 - ProcDomainReset = 245 + ProcDomainReset = 245 ProcDomainShutdownFlags = 258 ProcConnectListAllDomains = 273 ProcConnectListAllStoragePools = 281 diff --git a/libvirt.go b/libvirt.go index e58fb8d..cbe2d69 100644 --- a/libvirt.go +++ b/libvirt.go @@ -193,6 +193,7 @@ const ( // ShutdownFlags specifies options available when shutting down a domain. type ShutdownFlags uint32 + const ( // ShutdownAcpiPowerBtn - send ACPI event ShutdownAcpiPowerBtn ShutdownFlags = 1 << iota @@ -301,6 +302,7 @@ const ( // RebootFlags specifies domain reboot methods type RebootFlags uint32 + const ( // RebootAcpiPowerBtn - send ACPI event RebootAcpiPowerBtn RebootFlags = 1 << iota @@ -318,6 +320,54 @@ const ( RebootParavirt ) +// 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 +) + +// DomainMemoryStat specifies memory stats of the domain +type DomainMemoryStat struct { + Tag DomainMemoryStatTag + Val uint64 +} + // 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) @@ -409,10 +459,10 @@ func (l *Libvirt) DomainCreateWithFlags(dom string, flags DomainCreateFlags) err } req := struct { Domain Domain - Flags DomainCreateFlags - } { + Flags DomainCreateFlags + }{ Domain: *d, - Flags: flags, + Flags: flags, } buf, err := encode(&req) @@ -430,6 +480,49 @@ func (l *Libvirt) DomainCreateWithFlags(dom string, flags DomainCreateFlags) err return nil } +// DomainMemoryStats returns memory stats of the domain managed by libvirt. +func (l *Libvirt) DomainMemoryStats(dom string) ([]DomainMemoryStat, error) { + + d, err := l.lookup(dom) + if err != nil { + return nil, err + } + + req := struct { + Domain Domain + MaxStats uint32 + Flags uint32 + }{ + Domain: *d, + MaxStats: 8, + Flags: 0, + } + + buf, err := encode(&req) + if err != nil { + return nil, err + } + + resp, err := l.request(constants.ProcDomainMemoryStats, constants.ProgramRemote, &buf) + if err != nil { + return nil, err + } + + r := <-resp + + result := struct { + DomainMemoryStats []DomainMemoryStat + }{} + + dec := xdr.NewDecoder(bytes.NewReader(r.Payload)) + _, err = dec.Decode(&result) + if err != nil { + return nil, err + } + + return result.DomainMemoryStats, nil +} + // DomainState returns state of the domain managed by libvirt. func (l *Libvirt) DomainState(dom string) (DomainState, error) { d, err := l.lookup(dom) @@ -1082,7 +1175,7 @@ func (l *Libvirt) Reset(dom string) error { payload := struct { Domain Domain - Flags uint32 + Flags uint32 }{ Domain: *d, Flags: 0, diff --git a/libvirt_test.go b/libvirt_test.go index 8187f86..9d8c315 100644 --- a/libvirt_test.go +++ b/libvirt_test.go @@ -133,6 +133,34 @@ func TestDomainState(t *testing.T) { } } +func TestDomainMemoryStats(t *testing.T) { + conn := libvirttest.New() + l := New(conn) + + wantDomainMemoryStats := []DomainMemoryStat{ + DomainMemoryStat{ + Tag: 6, + Val: 1048576, + }, + DomainMemoryStat{ + Tag: 7, + Val: 91272, + }, + } + + gotDomainMemoryStats, err := l.DomainMemoryStats("test") + if err != nil { + t.Error(err) + } + + for i := range wantDomainMemoryStats { + if wantDomainMemoryStats[i] != gotDomainMemoryStats[i] { + t.Errorf("expected domain memory stat %v, got %v", wantDomainMemoryStats[i], gotDomainMemoryStats[i]) + } + } + +} + func TestEvents(t *testing.T) { conn := libvirttest.New() l := New(conn) diff --git a/libvirttest/libvirt.go b/libvirttest/libvirt.go index fc1fedf..5de6191 100644 --- a/libvirttest/libvirt.go +++ b/libvirttest/libvirt.go @@ -20,8 +20,8 @@ import ( "net" "sync/atomic" - "github.com/digitalocean/go-libvirt/internal/constants" "fmt" + "github.com/digitalocean/go-libvirt/internal/constants" "os" ) @@ -207,6 +207,26 @@ var testDomainsReply = []byte{ 0x00, 0x00, 0x02, } +var testDomainMemoryStatsReply = []byte{ + 0x00, 0x00, 0x00, 0x38, // length + 0x20, 0x00, 0x80, 0x86, // program + 0x00, 0x00, 0x00, 0x01, // version + 0x00, 0x00, 0x00, 0x9f, // procedure + 0x00, 0x00, 0x00, 0x01, // type + 0x00, 0x00, 0x00, 0x00, // serial + 0x00, 0x00, 0x00, 0x00, // status + + // tag 6 val 1048576 + // tag 7 val 91272 + 0x00, 0x00, 0x00, 0x02, + 0x00, 0x00, 0x00, 0x06, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x10, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x07, + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x64, 0x88, +} + var testDomainStateReply = []byte{ 0x00, 0x00, 0x00, 0x24, // length 0x20, 0x00, 0x80, 0x86, // program @@ -446,6 +466,8 @@ func (m *MockLibvirt) handleRemote(procedure uint32, conn net.Conn) { conn.Write(m.reply(testSecretsReply)) case constants.ProcDomainGetState: conn.Write(m.reply(testDomainStateReply)) + case constants.ProcDomainMemoryStats: + conn.Write(m.reply(testDomainMemoryStatsReply)) case constants.ProcDomainMigrateSetMaxSpeed: conn.Write(m.reply(testSetSpeedReply)) case constants.ProcMigratePerformParams: