From 2dae4eb5a319931ee5e5f1333f56601577cd2d59 Mon Sep 17 00:00:00 2001 From: Mikhail Klementev Date: Thu, 12 Jul 2018 20:00:57 +0000 Subject: [PATCH] Implements automatic ballooning --- README.md | 11 +++++++++++ appvm.go | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+) diff --git a/README.md b/README.md index 820da1a..6d1314a 100644 --- a/README.md +++ b/README.md @@ -66,6 +66,17 @@ Default hotkey to release cursor: ctrl+alt. $ appvm stop chromium +## Automatic ballooning + +Add this command: + + $ appvm autoballoon + +to crontab like that: + + $ crontab -l + * * * * * /home/user/dev/go/bin/appvm autoballoon + # App description $ cat nix/chromium.nix diff --git a/appvm.go b/appvm.go index b9ed447..eb468c8 100644 --- a/appvm.go +++ b/appvm.go @@ -16,11 +16,13 @@ import ( "os" "os/exec" "path/filepath" + "strconv" "syscall" "time" "github.com/digitalocean/go-libvirt" "github.com/jollheef/go-system" + "github.com/olekukonko/tablewriter" kingpin "gopkg.in/alecthomas/kingpin.v2" ) @@ -239,6 +241,60 @@ func drop(name string) { os.RemoveAll(appDataPath) } +func autoBalloon(l *libvirt.Libvirt) { + domains, err := l.Domains() + if err != nil { + log.Fatal(err) + } + + const memoryMin = 512 * 1024 // 512 MiB + + table := tablewriter.NewWriter(os.Stdout) + table.SetHeader([]string{"Application VM", "Used memory", "Current memory", "Max memory", "New memory"}) + for _, d := range domains { + if d.Name[0:5] == "appvm" { + name := d.Name[6:] + + memoryUsedRaw, err := ioutil.ReadFile(os.Getenv("HOME") + "/appvm/" + name + "/.memory_used") + if err != nil { + log.Fatal(err) + } + memoryUsedMiB, err := strconv.Atoi(string(memoryUsedRaw[0 : len(memoryUsedRaw)-1])) + if err != nil { + log.Fatal(err) + } + memoryUsed := memoryUsedMiB * 1024 + + _, memoryMax, memoryCurrent, _, _, err := l.DomainGetInfo(d) + if err != nil { + log.Fatal(err) + } + + memoryNew := uint64(float64(memoryUsed) * 1.2) // +20% + + if memoryNew > memoryMax { + memoryNew = memoryMax - 1 + } + + if memoryNew < memoryMin { + memoryNew = memoryMin + } + + err = l.DomainSetMemory(d, memoryNew) + if err != nil { + log.Fatal(err) + } + + table.Append([]string{name, + fmt.Sprintf("%d", memoryUsed), + fmt.Sprintf("%d", memoryCurrent), + fmt.Sprintf("%d", memoryMax), + fmt.Sprintf("%d", memoryNew)}) + } + } + table.Render() +} + func main() { c, err := net.DialTimeout("unix", "/var/run/libvirt/libvirt-sock", time.Second) if err != nil { @@ -252,6 +308,7 @@ func main() { defer l.Disconnect() kingpin.Command("list", "List applications") + kingpin.Command("autoballoon", "Automatically adjust/reduce app vm memory") startName := kingpin.Command("start", "Start application").Arg("name", "Application name").Required().String() stopName := kingpin.Command("stop", "Stop application").Arg("name", "Application name").Required().String() dropName := kingpin.Command("drop", "Remove application data").Arg("name", "Application name").Required().String() @@ -265,5 +322,7 @@ func main() { stop(l, *stopName) case "drop": drop(*dropName) + case "autoballoon": + autoBalloon(l) } }