garm/vendor/github.com/lxc/lxd/shared/instance.go
Gabriel Adrian Samfira e7f208367b
Error if we can't remove instance from provider
Signed-off-by: Gabriel Adrian Samfira <gsamfira@cloudbasesolutions.com>
2023-03-26 22:19:20 +03:00

397 lines
13 KiB
Go

package shared
import (
"errors"
"fmt"
"strconv"
"strings"
"time"
"github.com/lxc/lxd/lxd/instance/instancetype"
"github.com/lxc/lxd/shared/units"
"github.com/lxc/lxd/shared/validate"
)
// InstanceAction indicates the type of action being performed.
type InstanceAction string
// InstanceAction types.
const (
Stop InstanceAction = "stop"
Start InstanceAction = "start"
Restart InstanceAction = "restart"
Freeze InstanceAction = "freeze"
Unfreeze InstanceAction = "unfreeze"
)
// ConfigVolatilePrefix indicates the prefix used for volatile config keys.
const ConfigVolatilePrefix = "volatile."
// IsRootDiskDevice returns true if the given device representation is configured as root disk for
// an instance. It typically get passed a specific entry of api.Instance.Devices.
func IsRootDiskDevice(device map[string]string) bool {
// Root disk devices also need a non-empty "pool" property, but we can't check that here
// because this function is used with clients talking to older servers where there was no
// concept of a storage pool, and also it is used for migrating from old to new servers.
// The validation of the non-empty "pool" property is done inside the disk device itself.
if device["type"] == "disk" && device["path"] == "/" && device["source"] == "" {
return true
}
return false
}
// ErrNoRootDisk means there is no root disk device found.
var ErrNoRootDisk = fmt.Errorf("No root device could be found")
// GetRootDiskDevice returns the instance device that is configured as root disk.
// Returns the device name and device config map.
func GetRootDiskDevice(devices map[string]map[string]string) (string, map[string]string, error) {
var devName string
var dev map[string]string
for n, d := range devices {
if IsRootDiskDevice(d) {
if devName != "" {
return "", nil, fmt.Errorf("More than one root device found")
}
devName = n
dev = d
}
}
if devName != "" {
return devName, dev, nil
}
return "", nil, ErrNoRootDisk
}
// HugePageSizeKeys is a list of known hugepage size configuration keys.
var HugePageSizeKeys = [...]string{"limits.hugepages.64KB", "limits.hugepages.1MB", "limits.hugepages.2MB", "limits.hugepages.1GB"}
// HugePageSizeSuffix contains the list of known hugepage size suffixes.
var HugePageSizeSuffix = [...]string{"64KB", "1MB", "2MB", "1GB"}
// InstanceConfigKeysAny is a map of config key to validator. (keys applying to containers AND virtual machines).
var InstanceConfigKeysAny = map[string]func(value string) error{
"boot.autostart": validate.Optional(validate.IsBool),
"boot.autostart.delay": validate.Optional(validate.IsInt64),
"boot.autostart.priority": validate.Optional(validate.IsInt64),
"boot.stop.priority": validate.Optional(validate.IsInt64),
"boot.host_shutdown_timeout": validate.Optional(validate.IsInt64),
"cloud-init.network-config": validate.Optional(validate.IsYAML),
"cloud-init.user-data": validate.Optional(validate.IsCloudInitUserData),
"cloud-init.vendor-data": validate.Optional(validate.IsCloudInitUserData),
"cluster.evacuate": validate.Optional(validate.IsOneOf("auto", "migrate", "live-migrate", "stop")),
"limits.cpu": validate.Optional(validate.IsValidCPUSet),
"limits.disk.priority": validate.Optional(validate.IsPriority),
"limits.memory": func(value string) error {
if value == "" {
return nil
}
if strings.HasSuffix(value, "%") {
num, err := strconv.ParseInt(strings.TrimSuffix(value, "%"), 10, 64)
if err != nil {
return err
}
if num == 0 {
return errors.New("Memory limit can't be 0%")
}
return nil
}
num, err := units.ParseByteSizeString(value)
if err != nil {
return err
}
if num == 0 {
return fmt.Errorf("Memory limit can't be 0")
}
return nil
},
"limits.network.priority": validate.Optional(validate.IsPriority),
// Caller is responsible for full validation of any raw.* value.
"raw.apparmor": validate.IsAny,
"security.devlxd": validate.Optional(validate.IsBool),
"security.protection.delete": validate.Optional(validate.IsBool),
"snapshots.schedule": validate.Optional(validate.IsCron([]string{"@hourly", "@daily", "@midnight", "@weekly", "@monthly", "@annually", "@yearly", "@startup", "@never"})),
"snapshots.schedule.stopped": validate.Optional(validate.IsBool),
"snapshots.pattern": validate.IsAny,
"snapshots.expiry": func(value string) error {
// Validate expression
_, err := GetExpiry(time.Time{}, value)
return err
},
// Volatile keys.
"volatile.apply_template": validate.IsAny,
"volatile.base_image": validate.IsAny,
"volatile.cloud-init.instance-id": validate.Optional(validate.IsUUID),
"volatile.evacuate.origin": validate.IsAny,
"volatile.last_state.idmap": validate.IsAny,
"volatile.last_state.power": validate.IsAny,
"volatile.last_state.ready": validate.IsBool,
"volatile.idmap.base": validate.IsAny,
"volatile.idmap.current": validate.IsAny,
"volatile.idmap.next": validate.IsAny,
"volatile.apply_quota": validate.IsAny,
"volatile.uuid": validate.Optional(validate.IsUUID),
"volatile.vsock_id": validate.Optional(validate.IsInt64),
"volatile.uuid.generation": validate.Optional(validate.IsUUID),
// Caller is responsible for full validation of any raw.* value.
"raw.idmap": validate.IsAny,
}
// InstanceConfigKeysContainer is a map of config key to validator. (keys applying to containers only).
var InstanceConfigKeysContainer = map[string]func(value string) error{
"limits.cpu.allowance": func(value string) error {
if value == "" {
return nil
}
if strings.HasSuffix(value, "%") {
// Percentage based allocation
_, err := strconv.Atoi(strings.TrimSuffix(value, "%"))
if err != nil {
return err
}
return nil
}
// Time based allocation
fields := strings.SplitN(value, "/", 2)
if len(fields) != 2 {
return fmt.Errorf("Invalid allowance: %s", value)
}
_, err := strconv.Atoi(strings.TrimSuffix(fields[0], "ms"))
if err != nil {
return err
}
_, err = strconv.Atoi(strings.TrimSuffix(fields[1], "ms"))
if err != nil {
return err
}
return nil
},
"limits.cpu.priority": validate.Optional(validate.IsPriority),
"limits.hugepages.64KB": validate.Optional(validate.IsSize),
"limits.hugepages.1MB": validate.Optional(validate.IsSize),
"limits.hugepages.2MB": validate.Optional(validate.IsSize),
"limits.hugepages.1GB": validate.Optional(validate.IsSize),
"limits.memory.enforce": validate.Optional(validate.IsOneOf("soft", "hard")),
"limits.memory.swap": validate.Optional(validate.IsBool),
"limits.memory.swap.priority": validate.Optional(validate.IsPriority),
"limits.processes": validate.Optional(validate.IsInt64),
"linux.kernel_modules": validate.IsAny,
"migration.incremental.memory": validate.Optional(validate.IsBool),
"migration.incremental.memory.iterations": validate.Optional(validate.IsUint32),
"migration.incremental.memory.goal": validate.Optional(validate.IsUint32),
"nvidia.runtime": validate.Optional(validate.IsBool),
"nvidia.driver.capabilities": validate.IsAny,
"nvidia.require.cuda": validate.IsAny,
"nvidia.require.driver": validate.IsAny,
// Caller is responsible for full validation of any raw.* value.
"raw.lxc": validate.IsAny,
"raw.seccomp": validate.IsAny,
"security.devlxd.images": validate.Optional(validate.IsBool),
"security.idmap.base": validate.Optional(validate.IsUint32),
"security.idmap.isolated": validate.Optional(validate.IsBool),
"security.idmap.size": validate.Optional(validate.IsUint32),
"security.nesting": validate.Optional(validate.IsBool),
"security.privileged": validate.Optional(validate.IsBool),
"security.protection.shift": validate.Optional(validate.IsBool),
"security.syscalls.allow": validate.IsAny,
"security.syscalls.blacklist_default": validate.Optional(validate.IsBool),
"security.syscalls.blacklist_compat": validate.Optional(validate.IsBool),
"security.syscalls.blacklist": validate.IsAny,
"security.syscalls.deny_default": validate.Optional(validate.IsBool),
"security.syscalls.deny_compat": validate.Optional(validate.IsBool),
"security.syscalls.deny": validate.IsAny,
"security.syscalls.intercept.bpf": validate.Optional(validate.IsBool),
"security.syscalls.intercept.bpf.devices": validate.Optional(validate.IsBool),
"security.syscalls.intercept.mknod": validate.Optional(validate.IsBool),
"security.syscalls.intercept.mount": validate.Optional(validate.IsBool),
"security.syscalls.intercept.mount.allowed": validate.IsAny,
"security.syscalls.intercept.mount.fuse": validate.IsAny,
"security.syscalls.intercept.mount.shift": validate.Optional(validate.IsBool),
"security.syscalls.intercept.sched_setscheduler": validate.Optional(validate.IsBool),
"security.syscalls.intercept.setxattr": validate.Optional(validate.IsBool),
"security.syscalls.intercept.sysinfo": validate.Optional(validate.IsBool),
"security.syscalls.whitelist": validate.IsAny,
}
// InstanceConfigKeysVM is a map of config key to validator. (keys applying to VM only).
var InstanceConfigKeysVM = map[string]func(value string) error{
"limits.memory.hugepages": validate.Optional(validate.IsBool),
"migration.stateful": validate.Optional(validate.IsBool),
// Caller is responsible for full validation of any raw.* value.
"raw.qemu": validate.IsAny,
"raw.qemu.conf": validate.IsAny,
"security.agent.metrics": validate.Optional(validate.IsBool),
"security.secureboot": validate.Optional(validate.IsBool),
"security.sev": validate.Optional(validate.IsBool),
"security.sev.policy.es": validate.Optional(validate.IsBool),
"security.sev.session.dh": validate.Optional(validate.IsAny),
"security.sev.session.data": validate.Optional(validate.IsAny),
"agent.nic_config": validate.Optional(validate.IsBool),
"volatile.apply_nvram": validate.Optional(validate.IsBool),
}
// ConfigKeyChecker returns a function that will check whether or not
// a provide value is valid for the associate config key. Returns an
// error if the key is not known. The checker function only performs
// syntactic checking of the value, semantic and usage checking must
// be done by the caller. User defined keys are always considered to
// be valid, e.g. user.* and environment.* keys.
func ConfigKeyChecker(key string, instanceType instancetype.Type) (func(value string) error, error) {
f, ok := InstanceConfigKeysAny[key]
if ok {
return f, nil
}
if instanceType == instancetype.Any || instanceType == instancetype.Container {
f, ok := InstanceConfigKeysContainer[key]
if ok {
return f, nil
}
}
if instanceType == instancetype.Any || instanceType == instancetype.VM {
f, ok := InstanceConfigKeysVM[key]
if ok {
return f, nil
}
}
if strings.HasPrefix(key, ConfigVolatilePrefix) {
if strings.HasSuffix(key, ".hwaddr") {
return validate.IsAny, nil
}
if strings.HasSuffix(key, ".name") {
return validate.IsAny, nil
}
if strings.HasSuffix(key, ".host_name") {
return validate.IsAny, nil
}
if strings.HasSuffix(key, ".mtu") {
return validate.IsAny, nil
}
if strings.HasSuffix(key, ".created") {
return validate.IsAny, nil
}
if strings.HasSuffix(key, ".id") {
return validate.IsAny, nil
}
if strings.HasSuffix(key, ".vlan") {
return validate.IsAny, nil
}
if strings.HasSuffix(key, ".spoofcheck") {
return validate.IsAny, nil
}
if strings.HasSuffix(key, ".last_state.vf.parent") {
return validate.IsAny, nil
}
if strings.HasSuffix(key, ".apply_quota") {
return validate.IsAny, nil
}
if strings.HasSuffix(key, ".ceph_rbd") {
return validate.IsAny, nil
}
if strings.HasSuffix(key, ".driver") {
return validate.IsAny, nil
}
if strings.HasSuffix(key, ".uuid") {
return validate.IsAny, nil
}
if strings.HasSuffix(key, ".last_state.ready") {
return validate.IsBool, nil
}
}
if strings.HasPrefix(key, "environment.") {
return validate.IsAny, nil
}
if strings.HasPrefix(key, "user.") {
return validate.IsAny, nil
}
if strings.HasPrefix(key, "image.") {
return validate.IsAny, nil
}
if strings.HasPrefix(key, "limits.kernel.") &&
(len(key) > len("limits.kernel.")) {
return validate.IsAny, nil
}
if (instanceType == instancetype.Any || instanceType == instancetype.Container) &&
strings.HasPrefix(key, "linux.sysctl.") {
return validate.IsAny, nil
}
return nil, fmt.Errorf("Unknown configuration key: %s", key)
}
// InstanceIncludeWhenCopying is used to decide whether to include a config item or not when copying an instance.
// The remoteCopy argument indicates if the copy is remote (i.e between LXD nodes) as this affects the keys kept.
func InstanceIncludeWhenCopying(configKey string, remoteCopy bool) bool {
if configKey == "volatile.base_image" {
return true // Include volatile.base_image always as it can help optimize copies.
}
if configKey == "volatile.last_state.idmap" && !remoteCopy {
return true // Include volatile.last_state.idmap when doing local copy to avoid needless remapping.
}
if strings.HasPrefix(configKey, ConfigVolatilePrefix) {
return false // Exclude all other volatile keys.
}
return true // Keep all other keys.
}