207 lines
3.6 KiB
Go
207 lines
3.6 KiB
Go
|
package daemon
|
||
|
|
||
|
import (
|
||
|
"errors"
|
||
|
"runtime"
|
||
|
"sync"
|
||
|
"syscall"
|
||
|
|
||
|
"github.com/rs/zerolog/log"
|
||
|
|
||
|
"code.dumpstack.io/tools/out-of-tree/api"
|
||
|
)
|
||
|
|
||
|
type Resources struct {
|
||
|
initialized bool
|
||
|
|
||
|
CPU *CPUResource
|
||
|
RAM *RAMResources
|
||
|
}
|
||
|
|
||
|
func NewResources() (r *Resources) {
|
||
|
r = &Resources{}
|
||
|
r.CPU = NewCPUResources()
|
||
|
r.RAM = NewRAMResources()
|
||
|
r.initialized = true
|
||
|
return
|
||
|
}
|
||
|
|
||
|
func (r *Resources) Allocate(job api.Job) (err error) {
|
||
|
if !r.initialized {
|
||
|
err = errors.New("resources not initialized")
|
||
|
return
|
||
|
}
|
||
|
|
||
|
if job.Artifact.Qemu.Cpus == 0 {
|
||
|
err = errors.New("no cpus requested")
|
||
|
return
|
||
|
}
|
||
|
|
||
|
if job.Artifact.Qemu.Memory == 0 {
|
||
|
err = errors.New("no memory requested")
|
||
|
return
|
||
|
}
|
||
|
|
||
|
origRam := r.RAM.GetSpent()
|
||
|
origCPU := r.CPU.GetSpent()
|
||
|
|
||
|
err = r.CPU.Allocate(job.Artifact.Qemu.Cpus)
|
||
|
if err != nil {
|
||
|
return
|
||
|
}
|
||
|
|
||
|
err = r.RAM.Allocate(job.Artifact.Qemu.Memory)
|
||
|
if err != nil {
|
||
|
r.CPU.Release(job.Artifact.Qemu.Cpus)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
log.Debug().Msgf("allocated %d cpus, %d MB ram",
|
||
|
r.CPU.GetSpent()-origCPU,
|
||
|
r.RAM.GetSpent()-origRam)
|
||
|
|
||
|
return
|
||
|
}
|
||
|
|
||
|
func (r *Resources) Release(job api.Job) {
|
||
|
if !r.initialized {
|
||
|
log.Error().Msg("resources not initialized")
|
||
|
return
|
||
|
}
|
||
|
|
||
|
r.CPU.Release(job.Artifact.Qemu.Cpus)
|
||
|
r.RAM.Release(job.Artifact.Qemu.Memory)
|
||
|
|
||
|
log.Debug().Msgf("released %d cpus, %d MB ram",
|
||
|
job.Artifact.Qemu.Cpus,
|
||
|
job.Artifact.Qemu.Memory)
|
||
|
}
|
||
|
|
||
|
type CPUResource struct {
|
||
|
num int
|
||
|
overcommit float64
|
||
|
|
||
|
mu *sync.Mutex
|
||
|
spent int
|
||
|
}
|
||
|
|
||
|
const (
|
||
|
Allocation = iota
|
||
|
Release
|
||
|
)
|
||
|
|
||
|
func NewCPUResources() (cpur *CPUResource) {
|
||
|
cpur = &CPUResource{}
|
||
|
cpur.mu = &sync.Mutex{}
|
||
|
cpur.num = runtime.NumCPU()
|
||
|
cpur.overcommit = 1
|
||
|
log.Debug().Msgf("total cpus: %d", cpur.num)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
func (cpur *CPUResource) SetOvercommit(oc float64) {
|
||
|
log.Info().Int("cpus", cpur.num).
|
||
|
Int("result", int(float64(cpur.num)*oc)).
|
||
|
Msgf("%.02f", oc)
|
||
|
cpur.overcommit = oc
|
||
|
}
|
||
|
|
||
|
func (cpur *CPUResource) GetSpent() int {
|
||
|
cpur.mu.Lock()
|
||
|
defer cpur.mu.Unlock()
|
||
|
return cpur.spent
|
||
|
}
|
||
|
|
||
|
var ErrNotEnoughCpu = errors.New("not enough cpu")
|
||
|
|
||
|
func (cpur *CPUResource) Allocate(cpu int) (err error) {
|
||
|
cpur.mu.Lock()
|
||
|
defer cpur.mu.Unlock()
|
||
|
|
||
|
if cpur.spent+cpu > int(float64(cpur.num)*cpur.overcommit) {
|
||
|
err = ErrNotEnoughCpu
|
||
|
return
|
||
|
}
|
||
|
|
||
|
cpur.spent += cpu
|
||
|
return
|
||
|
}
|
||
|
|
||
|
func (cpur *CPUResource) Release(cpu int) (err error) {
|
||
|
cpur.mu.Lock()
|
||
|
defer cpur.mu.Unlock()
|
||
|
|
||
|
if cpur.spent < cpu {
|
||
|
err = ErrFreeingMoreThanAllocated
|
||
|
return
|
||
|
}
|
||
|
|
||
|
cpur.spent -= cpu
|
||
|
return
|
||
|
}
|
||
|
|
||
|
type RAMResources struct {
|
||
|
mb int
|
||
|
overcommit float64
|
||
|
|
||
|
mu *sync.Mutex
|
||
|
spent int
|
||
|
}
|
||
|
|
||
|
func NewRAMResources() (ramr *RAMResources) {
|
||
|
ramr = &RAMResources{}
|
||
|
ramr.mu = &sync.Mutex{}
|
||
|
ramr.overcommit = 1
|
||
|
|
||
|
var info syscall.Sysinfo_t
|
||
|
syscall.Sysinfo(&info)
|
||
|
ramr.mb = int(info.Totalram / 1024 / 1024)
|
||
|
log.Debug().Msgf("total ram: %d MB", ramr.mb)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
func (ramr *RAMResources) SetOvercommit(oc float64) {
|
||
|
log.Info().Int("ram", ramr.mb).
|
||
|
Int("result", int(float64(ramr.mb)*oc)).
|
||
|
Msgf("%.02f", oc)
|
||
|
ramr.overcommit = oc
|
||
|
}
|
||
|
|
||
|
func (ramr RAMResources) GetSpent() int {
|
||
|
ramr.mu.Lock()
|
||
|
defer ramr.mu.Unlock()
|
||
|
return ramr.spent
|
||
|
}
|
||
|
|
||
|
var ErrNotEnoughRam = errors.New("not enough ram")
|
||
|
|
||
|
func (ramr *RAMResources) Allocate(mb int) (err error) {
|
||
|
ramr.mu.Lock()
|
||
|
defer ramr.mu.Unlock()
|
||
|
|
||
|
ocmem := int(float64(ramr.mb) * ramr.overcommit)
|
||
|
|
||
|
if mb > ocmem-ramr.spent {
|
||
|
err = ErrNotEnoughRam
|
||
|
return
|
||
|
}
|
||
|
|
||
|
ramr.spent += mb
|
||
|
return
|
||
|
}
|
||
|
|
||
|
var ErrFreeingMoreThanAllocated = errors.New("freeing more than allocated")
|
||
|
|
||
|
func (ramr *RAMResources) Release(mb int) (err error) {
|
||
|
ramr.mu.Lock()
|
||
|
defer ramr.mu.Unlock()
|
||
|
|
||
|
if ramr.spent < mb {
|
||
|
err = ErrFreeingMoreThanAllocated
|
||
|
return
|
||
|
}
|
||
|
|
||
|
ramr.spent -= mb
|
||
|
return
|
||
|
}
|