diff --git a/src/consts/consts.go b/src/consts/consts.go index 2df65e72497070646722d95a0dd1f8b3a3b98188..5d8b3714f7eaf4a6fcd142b0f2292564683d575a 100644 --- a/src/consts/consts.go +++ b/src/consts/consts.go @@ -11,4 +11,12 @@ const ( VMSpiceMaxPort = 65000 MaxUploadSize = "30G" + + // We estimate that KVM allows for this amount of RAM saving in % + // (due to page sharing across VMs). + KsmRamSaving = 0.3 + + // To prevent RAM saturation, we refuse running new VMs if more than + // this amount of memory is being used (in %). + RamUsageLimit = .85 ) diff --git a/src/qga/qga.go b/src/qga/qga.go index 8c90baa157f79024f3f9e00c4454fd6adf02b624..6bfb80c43e4764776fbb3dfb093b64302fc7324a 100644 --- a/src/qga/qga.go +++ b/src/qga/qga.go @@ -38,6 +38,7 @@ func (q *QGACon)SendShutdown() error { return err } +// TODO: work in progress... func (q *QGACon)WriteFile(filePath string) error { filePath = "/home/nexus/pipo.txt" _, err := q.sock.Write([]byte(`{"execute":"guest-file-open", "arguments":{"path":"`+filePath+`","mode":"w+"}}`)) diff --git a/src/utils/memory.go b/src/utils/memory.go index a75f0b2e80671ff5b9079c041a1e703c55c37fca..70b8c0e4d255f51e29dba54ba7013fad8d5dec46 100644 --- a/src/utils/memory.go +++ b/src/utils/memory.go @@ -21,9 +21,8 @@ func GetRAM() (int, int, error) { scan := bufio.NewScanner(f) scan.Split(bufio.ScanLines) - var memTotal int = -1 - var memAvail int = -1 - + var memTotal int = -1 + var memAvail int = -1 const units = 1024 for scan.Scan() { diff --git a/src/vms/vms.go b/src/vms/vms.go index 662f583535b2e012b295dc8a751b06486ab3ac46..49ff2201dc2efd35c6ddfa0bbd88107f47c3cfac 100644 --- a/src/vms/vms.go +++ b/src/vms/vms.go @@ -4,6 +4,7 @@ import ( "os" "sort" "sync" + "math" "errors" "path/filepath" "nexus-server/exec" @@ -23,6 +24,7 @@ type ( dir string // Base directory where VMs are stored rwlock *sync.RWMutex // RWlock to ensure the coherency of the map (m) of VMs usedPorts [65536]bool // Indicates which ports are used by the VMs + usedRAM int // Used RAM by running VMs (in MB) } ) @@ -39,7 +41,7 @@ func GetVMsInstance() *VMs { // NOTE: path is the root directory where VMs reside. func InitVMs() error { vmsDir := paths.GetInstance().VMsDir - vms = &VMs { m: make(map[string]VM), dir: vmsDir, rwlock: new(sync.RWMutex) } + vms = &VMs { m: make(map[string]VM), dir: vmsDir, rwlock: new(sync.RWMutex), usedRAM: 0 } errMsg := "Failed reading VMs directory: " dirs1, err := utils.GetSubDirs(vmsDir) @@ -170,18 +172,20 @@ func (vms *VMs)StartVM(vmID uuid.UUID) (int, string, error) { return 0, "", err } - _, availRAM, err := utils.GetRAM() + totalRAM, availRAM, err := utils.GetRAM() if err != nil { return -1, "", errors.New("Failed obtaining memory info: "+err.Error()) } - // Check there will be at least 16GB of RAM left after the VM has started, - // otherwise, we prevent the execution of the VM to avoid RAM saturation. - // Thanks to Linux's KSVM technology, pages sharing the same content are actually shared - // which dramatically reduces the amount of RAM being used (roughly ~50% in average). - log.Info("vm.RAM: ", vm.Ram) - log.Info("Available RAM: ", availRAM) - if availRAM - vm.Ram < 16*1024 { + // We estimate that KVM allows for ~30% RAM saving (due to page sharing across VMs). + estimatedVmRAM := int(math.Round(float64(vm.Ram)*(1.-consts.KsmRamSaving))) + + vms.usedRAM += estimatedVmRAM + + // Checks that at least 15% of the available RAM is left after the VM has started, + // otherwise, refuses to run it in order to avoid RAM saturation. + if availRAM - vms.usedRAM <= int(math.Round(float64(totalRAM)*(1.-consts.RamUsageLimit))) { + vms.usedRAM -= estimatedVmRAM return -1, "", errors.New("Insufficient free RAM to start VM") } @@ -205,6 +209,7 @@ func (vms *VMs)StartVM(vmID uuid.UUID) (int, string, error) { vm.mutex.Unlock() vms.rwlock.Lock() vms.usedPorts[vm.Run.Port] = false + vms.usedRAM -= estimatedVmRAM vms.updateVMMap(vm) vms.rwlock.Unlock() } @@ -213,6 +218,7 @@ func (vms *VMs)StartVM(vmID uuid.UUID) (int, string, error) { pwd, err := vm.start(port, endofExecFn) vms.updateVMMap(&vm) vm.mutex.Unlock() + return port, pwd, err }