Skip to content
Snippets Groups Projects
Commit 7e84887d authored by Florent Gluck's avatar Florent Gluck
Browse files

ongoing work on new config system

parent 2ce6d8cd
No related branches found
No related tags found
No related merge requests found
# Port the API listens to (must be > 1024 and < 65535)
APIDefaultPort = 1077
[core]
# Define the range of ports used by each VM for their spice server
VMSpiceMinPort = 1100
VMSpiceMaxPort = 65535
# Supported levels: debug, info, warn, error, fatal, panic.
log_level = info
# Log level
# Supported levels: panic, fatal, error, warn, info, debug
LogLevel = info
# Port the API listens to (must be > 1024 and < 65535).
api_default_port = 1077
# Absolute path to QEMU system binary
QemuSystem = /usr/bin/qemu-system-x86_64
# Define the range of ports reserved for spice.
# Each running VM will use a port on the server.
vm_spice_min_port = 1100
vm_spice_max_port = 65535
# Absolute path to QEMU image binary
QemuImg = /usr/bin/qemu-img
# Directory where temporary files are created.
tmp_dir = /tmp
# Absolute path to guestfish binary
Guestfish = /usr/bin/guestfish
[limits]
# Directory where temporary files are created
TmpDir = /tmp
MaxUploadSize = 30G
max_upload_size = 30G
# We estimate that KVM allows for this amount of RAM saving in % (due to page sharing across VMs).
# 30% seems to be a pretty conservative estimate.
KsmRamSaving = 0.3
ksm_ram_saving = 0.3
# To prevent RAM saturation, we refuse running new VMs if more than
# this amount of memory is being used (in %).
RamUsageLimit = 0.85
ram_usage_limit = 0.85
[tools]
# Absolute path to QEMU system binary.
qemu_system = /usr/bin/qemu-system-x86_64
# Absolute path to QEMU image binary.
qemu_img = /usr/bin/qemu-img
# Absolute path to guestfish binary.
guestfish = /usr/bin/guestfish
package config
import (
"os"
"strings"
"path/filepath"
"nexus-server/logger"
"gopkg.in/ini.v1"
"github.com/sirupsen/logrus"
)
type Config struct {
Core struct {
TmpDir string `ini:"tmp_dir"`
LogLevel string `ini:"log_level"`
APIDefaultPort int `ini:"api_default_port"`
VMSpiceMinPort int `ini:"vm_spice_min_port"`
VMSpiceMaxPort int `ini:"vm_spice_max_port"`
} `ini:"core"`
Limits struct {
MaxUploadSize string `ini:"max_upload_sizeize"`
KsmRamSaving float64 `ini:"ksm_ram_saving"`
RamUsageLimit float64 `ini:"ram_usage_limit"`
} `ini:"limits"`
Tools struct {
QemuSystem string `ini:"qemu_system"`
QemuImg string `ini:"qemu_img"`
Guestfish string `ini:"guestfish"`
} `ini:"tools"`
// Unix permissions when creating directories: when creating templates and VMs and for tmp directory.
MkDirPerm os.FileMode
VMPwdLength int
VMPwdDigitCount int
VMPwdSymbolCount int
VMPwdRepeatChars bool
UsersFile string
DataDir string
VMsDir string
TemplatesDir string
CertsDir string
}
var log = logger.GetInstance()
var config *Config
func GetInstance() *Config {
return config
}
func Init() {
certsDir := "../certs"
configDir := "../config"
dataDir := "../dataDir"
conf, err := ini.LoadSources(ini.LoadOptions{
Loose: false,
}, filepath.Join(configDir, "nexus.conf"))
if err != nil {
log.Fatal("Failed reading config file: "+err.Error())
}
config = &Config {}
err = conf.StrictMapTo(config)
if err != nil {
log.Fatal("Failed parsing config file: "+err.Error())
}
// Limits struct {
// MaxUploadSize string `ini:"max_upload_sizeize"`
// KsmRamSaving float64 `ini:"ksm_ram_saving"`
// RamUsageLimit float64 `ini:"ram_usage_limit"`
// } `ini:"limits"`
// Tools struct {
// QemuSystem string `ini:"qemu_system"`
// QemuImg string `ini:"qemu_img"`
// Guestfish string `ini:"guestfish"`
// } `ini:"tools"`
if config.Core.TmpDir == "" {
log.Fatal("Config file error: tmp_dir must be defined")
}
_, err = os.Stat(config.Core.TmpDir)
if err != nil {
log.Fatal("Config file error: tmp_dir \""+config.Core.TmpDir+"\": "+err.Error())
}
if config.Core.LogLevel == "" {
log.Fatal("Config file error: log_level must be defined")
}
loglevelStr := strings.ToLower(config.Core.LogLevel)
switch loglevelStr {
case "debug":
log.SetLevel(logrus.DebugLevel)
case "info":
log.SetLevel(logrus.InfoLevel)
case "warn":
log.SetLevel(logrus.WarnLevel)
case "error":
log.SetLevel(logrus.ErrorLevel)
case "fatal":
log.SetLevel(logrus.FatalLevel)
case "panic":
log.SetLevel(logrus.PanicLevel)
default:
log.Fatal("Config file error: invalid log_level")
}
if config.Core.APIDefaultPort < 1025 || config.Core.APIDefaultPort > 65535 {
log.Fatal("Config file error: invalid api_default_port value")
}
if config.Core.VMSpiceMinPort < 1025 || config.Core.VMSpiceMinPort > 65535 {
log.Fatal("Config file error: invalid vm_spice_min_port value")
}
if config.Core.VMSpiceMaxPort < 1025 || config.Core.VMSpiceMaxPort > 65535 {
log.Fatal("Config file error: invalid vm_spice_max_port value")
}
if config.Core.VMSpiceMaxPort <= config.Core.VMSpiceMinPort {
log.Fatal("Config file error: vm_spice_max_port must be > vm_spice_min_port value")
}
config.MkDirPerm = 0750
config.VMPwdLength = 8
config.VMPwdDigitCount = 4
config.VMPwdSymbolCount = 0
config.VMPwdRepeatChars = false
config.UsersFile = filepath.Join(configDir, "/users.json")
config.DataDir = dataDir
config.VMsDir = filepath.Join(dataDir, "/vms")
config.TemplatesDir = filepath.Join(dataDir, "/templates")
config.CertsDir = certsDir
log.Info("TmpDir: ", config.Core.TmpDir)
os.Exit(1)
// Deletes temp directory and its content.
if err := os.RemoveAll(config.Core.TmpDir); err != nil {
log.Fatal("Failed deleting tmp directory: "+err.Error())
}
if err := os.Mkdir(config.Core.TmpDir, config.MkDirPerm); err != nil {
log.Fatal("Failed creating tmp dir: "+err.Error())
}
}
module nexus-server/config
go 1.18
package consts
const (
DefaultLogLevel = "info"
APIDefaultPort = 1077
VMSpiceMinPort = 1100
VMSpiceMaxPort = 65535
VMPwdLength = 8
VMPwdDigitCount = 4
VMPwdSymbolCount = 0
VMPwdRepeatChars = false
MaxUploadSize = "30G"
// We estimate that KVM allows for this amount of RAM saving in % (due to page sharing across VMs).
// 30% seems to be a pretty conservative estimate.
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
)
module nexus-server/consts
go 1.18
......@@ -10,8 +10,6 @@ replace nexus-common/params => ../common/params
replace nexus-common/caps => ../common/caps
replace nexus-server/consts => ./consts
replace nexus-server/users => ./users
replace nexus-server/logger => ./logger
......@@ -24,6 +22,8 @@ replace nexus-server/utils => ./utils
replace nexus-server/paths => ./paths
replace nexus-server/config => ./config
replace nexus-server/cleaner => ./cleaner
replace nexus-server/exec => ./exec
......@@ -35,7 +35,6 @@ replace nexus-server/version => ./version
require (
github.com/sirupsen/logrus v1.9.0
nexus-server/cleaner v0.0.0-00010101000000-000000000000
nexus-server/consts v0.0.0-00010101000000-000000000000
nexus-server/logger v0.0.0-00010101000000-000000000000
nexus-server/paths v0.0.0-00010101000000-000000000000
nexus-server/router v0.0.0-00010101000000-000000000000
......@@ -63,10 +62,12 @@ require (
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 // indirect
golang.org/x/text v0.3.7 // indirect
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
nexus-common/caps v0.0.0-00010101000000-000000000000 // indirect
nexus-common/params v0.0.0-00010101000000-000000000000 // indirect
nexus-common/template v0.0.0-00010101000000-000000000000 // indirect
nexus-common/vm v0.0.0-00010101000000-000000000000 // indirect
nexus-server/config v0.0.0-00010101000000-000000000000 // indirect
nexus-server/exec v0.0.0-00010101000000-000000000000 // indirect
nexus-server/qga v0.0.0-00010101000000-000000000000 // indirect
nexus-server/version v0.0.0-00010101000000-000000000000 // indirect
......
......@@ -72,6 +72,8 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
......@@ -2,10 +2,7 @@ package main
import (
"os"
"fmt"
"path"
"flag"
"strings"
"nexus-server/vms"
"nexus-server/exec"
"nexus-server/paths"
......@@ -13,7 +10,7 @@ import (
"nexus-server/utils"
"nexus-server/router"
"nexus-server/logger"
"nexus-server/consts"
"nexus-server/config"
"nexus-server/cleaner"
"nexus-server/version"
"github.com/sirupsen/logrus"
......@@ -28,6 +25,7 @@ func main() {
log.Info(appname+" version "+version.Get().String())
// These two lines must be executed very early on (basically, here).
config.Init()
paths.Init()
utils.RandInit()
......@@ -41,32 +39,6 @@ func main() {
log.Fatal(err)
}
usage := func() {
fmt.Println("Usage of "+appname+":")
flag.PrintDefaults()
os.Exit(1)
}
loglevelFlag := flag.String("l", consts.DefaultLogLevel, "Log level: debug, info, warn, error, fatal")
flag.Parse()
loglevelStr := strings.ToLower(*loglevelFlag)
switch loglevelStr {
case "debug":
log.SetLevel(logrus.DebugLevel)
case "info":
log.SetLevel(logrus.InfoLevel)
case "warn":
log.SetLevel(logrus.WarnLevel)
case "error":
log.SetLevel(logrus.ErrorLevel)
case "fatal":
log.SetLevel(logrus.FatalLevel)
default:
log.Error("Invalid log level!")
usage()
}
err := users.InitUsers()
if err != nil {
log.Fatal(err.Error())
......@@ -85,5 +57,5 @@ func main() {
cleaner.Start()
router.New().Start(consts.APIDefaultPort)
router.New().Start(config.GetInstance().Core.APIDefaultPort)
}
......@@ -6,7 +6,6 @@ import (
)
type Paths struct {
ConfigDir string
UsersFile string
DataDir string
VMsDir string
......@@ -24,11 +23,10 @@ func GetInstance() *Paths {
func Init() {
certs := "../certs"
config := "../config"
conf := "../config"
data := "../data"
paths = &Paths {
ConfigDir: config,
UsersFile: filepath.Join(config, "/users.json"),
UsersFile: filepath.Join(conf, "/users.json"),
DataDir: data,
VMsDir: filepath.Join(data, "/vms"),
TemplatesDir: filepath.Join(data, "/templates"),
......
......@@ -12,7 +12,7 @@ import (
"nexus-server/paths"
"nexus-server/users"
"nexus-server/logger"
"nexus-server/consts"
"nexus-server/config"
"github.com/labstack/echo/v4"
"github.com/labstack/echo/v4/middleware"
)
......@@ -27,6 +27,7 @@ type Router struct {
}
var log = logger.GetInstance()
var conf = config.GetInstance()
func New() *Router {
return &Router{
......@@ -45,7 +46,7 @@ func (router *Router)Start(port int) {
}
router.echo.HideBanner = true
router.echo.Use(middleware.BodyLimit(consts.MaxUploadSize))
router.echo.Use(middleware.BodyLimit(conf.Limits.MaxUploadSize))
// Login management.
router.echo.POST("/login", auth.Login)
......
......@@ -56,7 +56,7 @@ func NewTemplateFromVM(name, owner, access string, vm *VM) (*Template, error) {
// Creates the template directory.
templateDir := template.getTemplateDir()
if err := os.Mkdir(templateDir, 0750); err != nil {
if err := os.Mkdir(templateDir, conf.MkDirPerm); err != nil {
msg := "Failed creating template dir: " + err.Error()
log.Error(msg)
vm.mutex.Unlock()
......@@ -109,7 +109,7 @@ func NewTemplateFromQCOW(name, owner, access, qcowFile string) (*Template, error
// Creates the template directory.
templateDir := template.getTemplateDir()
if err := os.Mkdir(templateDir, 0750); err != nil {
if err := os.Mkdir(templateDir, conf.MkDirPerm); err != nil {
msg := "Failed creating template dir: " + err.Error()
log.Error(msg)
return nil, errors.New(msg)
......
......@@ -255,7 +255,7 @@ func (vm *VM)writeFiles() error {
}
// Creates the 3-directory structure.
if err = os.MkdirAll(vm.dir, 0750); err != nil {
if err = os.MkdirAll(vm.dir, conf.MkDirPerm); err != nil {
msg := "Failed creating VM dirs: "+err.Error()
log.Error(msg)
return errors.New(msg)
......
......@@ -19,7 +19,7 @@ import (
"nexus-server/utils"
"nexus-server/users"
"nexus-server/logger"
c "nexus-server/consts"
"nexus-server/config"
"github.com/google/uuid"
)
......@@ -36,6 +36,7 @@ type (
)
var log = logger.GetInstance()
var conf = config.GetInstance()
var vms *VMs
// Returns a VMs singleton to access this module's public functions.
......@@ -51,7 +52,7 @@ func GetVMsInstance() *VMs {
func InitVMs() error {
vmsDir := paths.GetInstance().VMsDir
vms = &VMs { m: make(map[string]*VM), dir: vmsDir, rwlock: new(sync.RWMutex), usedRAM: 0 }
vms.usedPorts[c.APIDefaultPort] = true
vms.usedPorts[conf.Core.APIDefaultPort] = true
errMsg := "Failed reading VMs directory: "
dirs1, err := utils.GetSubDirs(vmsDir)
......@@ -229,13 +230,13 @@ func (vms *VMs)StartVMWithCreds(vmID uuid.UUID, port int, checkPort bool, pwd st
}
// We estimate ~30% of RAM saving thanks to KSM (due to page sharing across VMs).
estimatedVmRAM := int(math.Round(float64(vm.v.Ram)*(1.-c.KsmRamSaving)))
estimatedVmRAM := int(math.Round(float64(vm.v.Ram)*(1.-conf.Limits.KsmRamSaving)))
vms.usedRAM += estimatedVmRAM
// Checks that enough available RAM would be 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.-c.RamUsageLimit))) {
if availRAM - vms.usedRAM <= int(math.Round(float64(totalRAM)*(1.-conf.Limits.RamUsageLimit))) {
vms.usedRAM -= estimatedVmRAM
return errors.New(prefix+"insufficient free RAM")
}
......@@ -332,7 +333,7 @@ func (vms *VMs)allocateFreeRandomPort() int {
defer vms.rwlock.Unlock()
for {
port := utils.Rand(c.VMSpiceMinPort, c.VMSpiceMaxPort)
port := utils.Rand(conf.Core.VMSpiceMinPort, conf.Core.VMSpiceMaxPort)
if !vms.usedPorts[port] {
if utils.IsPortAvailable(port) {
vms.usedPorts[port] = true
......@@ -350,7 +351,7 @@ func (vms *VMs)StartVM(vmID uuid.UUID) (int, string, error) {
// Randomly generates a 8 characters long password with 4 digits, 0 symbols,
// allowing upper and lower case letters, disallowing repeat characters.
pwd, err := passwordGen.Generate(c.VMPwdLength, c.VMPwdDigitCount, c.VMPwdSymbolCount, false, c.VMPwdRepeatChars)
pwd, err := passwordGen.Generate(conf.VMPwdLength, conf.VMPwdDigitCount, conf.VMPwdSymbolCount, false, conf.VMPwdRepeatChars)
if err != nil {
msg := "Failed starting VM: password generation error: "+err.Error()
log.Error(msg)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment