436 lines
13 KiB
Go
436 lines
13 KiB
Go
package proxmox
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"log"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
// LXC options for the Proxmox API
|
|
type configLxc struct {
|
|
Ostemplate string `json:"ostemplate"`
|
|
Arch string `json:"arch"`
|
|
BWLimit int `json:"bwlimit,omitempty"`
|
|
CMode string `json:"cmode"`
|
|
Console bool `json:"console"`
|
|
Cores int `json:"cores,omitempty"`
|
|
CPULimit int `json:"cpulimit"`
|
|
CPUUnits int `json:"cpuunits"`
|
|
Description string `json:"description,omitempty"`
|
|
Features QemuDevice `json:"features,omitempty"`
|
|
Force bool `json:"force,omitempty"`
|
|
Hookscript string `json:"hookscript,omitempty"`
|
|
Hostname string `json:"hostname,omitempty"`
|
|
IgnoreUnpackErrors bool `json:"ignore-unpack-errors,omitempty"`
|
|
Lock string `json:"lock,omitempty"`
|
|
Memory int `json:"memory"`
|
|
Mountpoints QemuDevices `json:"mountpoints,omitempty"`
|
|
Nameserver string `json:"nameserver,omitempty"`
|
|
Networks QemuDevices `json:"networks,omitempty"`
|
|
OnBoot bool `json:"onboot"`
|
|
OsType string `json:"ostype,omitempty"`
|
|
Password string `json:"password,omitempty"`
|
|
Pool string `json:"pool,omitempty"`
|
|
Protection bool `json:"protection"`
|
|
Restore bool `json:"restore,omitempty"`
|
|
RootFs string `json:"rootfs,omitempty"`
|
|
SearchDomain string `json:"searchdomain,omitempty"`
|
|
SSHPublicKeys string `json:"ssh-public-keys,omitempty"`
|
|
Start bool `json:"start"`
|
|
Startup string `json:"startup,omitempty"`
|
|
Storage string `json:"storage"`
|
|
Swap int `json:"swap"`
|
|
Template bool `json:"template,omitempty"`
|
|
Tty int `json:"tty"`
|
|
Unique bool `json:"unique,omitempty"`
|
|
Unprivileged bool `json:"unprivileged"`
|
|
Unused []string `json:"unused,omitempty"`
|
|
}
|
|
|
|
func NewConfigLxc() configLxc {
|
|
return configLxc{
|
|
Arch: "amd64",
|
|
CMode: "tty",
|
|
Console: true,
|
|
CPULimit: 0,
|
|
CPUUnits: 1024,
|
|
Memory: 512,
|
|
OnBoot: false,
|
|
Protection: false,
|
|
Start: false,
|
|
Storage: "local",
|
|
Swap: 512,
|
|
Template: false,
|
|
Tty: 2,
|
|
Unprivileged: false,
|
|
}
|
|
}
|
|
|
|
func NewConfigLxcFromJson(io io.Reader) (config configLxc, err error) {
|
|
config = NewConfigLxc()
|
|
err = json.NewDecoder(io).Decode(config)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
return config, err
|
|
}
|
|
log.Println(config)
|
|
return
|
|
}
|
|
|
|
func NewConfigLxcFromApi(vmr *VmRef, client *Client) (config *configLxc, err error) {
|
|
// prepare json map to receive the information from the api
|
|
var lxcConfig map[string]interface{}
|
|
lxcConfig, err = client.GetVmConfig(vmr)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
return nil, err
|
|
}
|
|
|
|
// prepare a new lxc config to store and return\
|
|
// the information from api
|
|
newConfig := NewConfigLxc()
|
|
config = &newConfig
|
|
|
|
arch := ""
|
|
if _, isSet := lxcConfig["arch"]; isSet {
|
|
arch = lxcConfig["arch"].(string)
|
|
}
|
|
cmode := ""
|
|
if _, isSet := lxcConfig["cmode"]; isSet {
|
|
cmode = lxcConfig["cmode"].(string)
|
|
}
|
|
console := true
|
|
if _, isSet := lxcConfig["console"]; isSet {
|
|
console = Itob(int(lxcConfig["console"].(float64)))
|
|
}
|
|
cores := 1
|
|
if _, isSet := lxcConfig["cores"]; isSet {
|
|
cores = int(lxcConfig["cores"].(float64))
|
|
}
|
|
cpulimit := 0
|
|
if _, isSet := lxcConfig["cpulimit"]; isSet {
|
|
cpulimit, _ = strconv.Atoi(lxcConfig["cpulimit"].(string))
|
|
}
|
|
cpuunits := 1024
|
|
if _, isSet := lxcConfig["cpuunits"]; isSet {
|
|
cpuunits = int(lxcConfig["cpuunits"].(float64))
|
|
}
|
|
description := ""
|
|
if _, isSet := lxcConfig["description"]; isSet {
|
|
description = lxcConfig["description"].(string)
|
|
}
|
|
|
|
// add features, if any
|
|
if features, isSet := lxcConfig["features"]; isSet {
|
|
featureList := strings.Split(features.(string), ",")
|
|
|
|
// create new device map to store features
|
|
featureMap := QemuDevice{}
|
|
// add all features to device map
|
|
featureMap.readDeviceConfig(featureList)
|
|
// prepare empty feature map
|
|
if config.Features == nil {
|
|
config.Features = QemuDevice{}
|
|
}
|
|
// and device config to networks
|
|
if len(featureMap) > 0 {
|
|
config.Features = featureMap
|
|
}
|
|
}
|
|
|
|
hookscript := ""
|
|
if _, isSet := lxcConfig["hookscript"]; isSet {
|
|
hookscript = lxcConfig["hookscript"].(string)
|
|
}
|
|
hostname := ""
|
|
if _, isSet := lxcConfig["hostname"]; isSet {
|
|
hostname = lxcConfig["hostname"].(string)
|
|
}
|
|
lock := ""
|
|
if _, isSet := lxcConfig["lock"]; isSet {
|
|
lock = lxcConfig["lock"].(string)
|
|
}
|
|
memory := 512
|
|
if _, isSet := lxcConfig["memory"]; isSet {
|
|
memory = int(lxcConfig["memory"].(float64))
|
|
}
|
|
|
|
// add mountpoints
|
|
mpNames := []string{}
|
|
|
|
for k, _ := range lxcConfig {
|
|
if mpName := rxMpName.FindStringSubmatch(k); len(mpName) > 0 {
|
|
mpNames = append(mpNames, mpName[0])
|
|
}
|
|
}
|
|
|
|
for _, mpName := range mpNames {
|
|
mpConfStr := lxcConfig[mpName]
|
|
mpConfList := strings.Split(mpConfStr.(string), ",")
|
|
|
|
id := rxDeviceID.FindStringSubmatch(mpName)
|
|
mpID, _ := strconv.Atoi(id[0])
|
|
// add mp id
|
|
mpConfMap := QemuDevice{
|
|
"id": mpID,
|
|
}
|
|
// add rest of device config
|
|
mpConfMap.readDeviceConfig(mpConfList)
|
|
// prepare empty mountpoint map
|
|
if config.Mountpoints == nil {
|
|
config.Mountpoints = QemuDevices{}
|
|
}
|
|
// and device config to mountpoints
|
|
if len(mpConfMap) > 0 {
|
|
config.Mountpoints[mpID] = mpConfMap
|
|
}
|
|
}
|
|
|
|
nameserver := ""
|
|
if _, isSet := lxcConfig["nameserver"]; isSet {
|
|
nameserver = lxcConfig["nameserver"].(string)
|
|
}
|
|
|
|
// add networks
|
|
nicNames := []string{}
|
|
|
|
for k, _ := range lxcConfig {
|
|
if nicName := rxNicName.FindStringSubmatch(k); len(nicName) > 0 {
|
|
nicNames = append(nicNames, nicName[0])
|
|
}
|
|
}
|
|
|
|
for _, nicName := range nicNames {
|
|
nicConfStr := lxcConfig[nicName]
|
|
nicConfList := strings.Split(nicConfStr.(string), ",")
|
|
|
|
id := rxDeviceID.FindStringSubmatch(nicName)
|
|
nicID, _ := strconv.Atoi(id[0])
|
|
// add nic id
|
|
nicConfMap := QemuDevice{
|
|
"id": nicID,
|
|
}
|
|
// add rest of device config
|
|
nicConfMap.readDeviceConfig(nicConfList)
|
|
// prepare empty network map
|
|
if config.Networks == nil {
|
|
config.Networks = QemuDevices{}
|
|
}
|
|
// and device config to networks
|
|
if len(nicConfMap) > 0 {
|
|
config.Networks[nicID] = nicConfMap
|
|
}
|
|
}
|
|
|
|
onboot := false
|
|
if _, isSet := lxcConfig["onboot"]; isSet {
|
|
onboot = Itob(int(lxcConfig["onboot"].(float64)))
|
|
}
|
|
ostype := ""
|
|
if _, isSet := lxcConfig["ostype"]; isSet {
|
|
ostype = lxcConfig["ostype"].(string)
|
|
}
|
|
protection := false
|
|
if _, isSet := lxcConfig["protection"]; isSet {
|
|
protection = Itob(int(lxcConfig["protection"].(float64)))
|
|
}
|
|
rootfs := ""
|
|
if _, isSet := lxcConfig["rootfs"]; isSet {
|
|
rootfs = lxcConfig["rootfs"].(string)
|
|
}
|
|
searchdomain := ""
|
|
if _, isSet := lxcConfig["searchdomain"]; isSet {
|
|
searchdomain = lxcConfig["searchdomain"].(string)
|
|
}
|
|
startup := ""
|
|
if _, isSet := lxcConfig["startup"]; isSet {
|
|
startup = lxcConfig["startup"].(string)
|
|
}
|
|
swap := 512
|
|
if _, isSet := lxcConfig["swap"]; isSet {
|
|
swap = int(lxcConfig["swap"].(float64))
|
|
}
|
|
template := false
|
|
if _, isSet := lxcConfig["template"]; isSet {
|
|
template = Itob(int(lxcConfig["template"].(float64)))
|
|
}
|
|
tty := 2
|
|
if _, isSet := lxcConfig["tty"]; isSet {
|
|
tty = int(lxcConfig["tty"].(float64))
|
|
}
|
|
unprivileged := false
|
|
if _, isset := lxcConfig["unprivileged"]; isset {
|
|
unprivileged = Itob(int(lxcConfig["unprivileged"].(float64)))
|
|
}
|
|
var unused []string
|
|
if _, isset := lxcConfig["unused"]; isset {
|
|
unused = lxcConfig["unused"].([]string)
|
|
}
|
|
|
|
config.Arch = arch
|
|
config.CMode = cmode
|
|
config.Console = console
|
|
config.Cores = cores
|
|
config.CPULimit = cpulimit
|
|
config.CPUUnits = cpuunits
|
|
config.Description = description
|
|
config.OnBoot = onboot
|
|
config.Hookscript = hookscript
|
|
config.Hostname = hostname
|
|
config.Lock = lock
|
|
config.Memory = memory
|
|
config.Nameserver = nameserver
|
|
config.OnBoot = onboot
|
|
config.OsType = ostype
|
|
config.Protection = protection
|
|
config.RootFs = rootfs
|
|
config.SearchDomain = searchdomain
|
|
config.Startup = startup
|
|
config.Swap = swap
|
|
config.Template = template
|
|
config.Tty = tty
|
|
config.Unprivileged = unprivileged
|
|
config.Unused = unused
|
|
|
|
return
|
|
}
|
|
|
|
// create LXC container using the Proxmox API
|
|
func (config configLxc) CreateLxc(vmr *VmRef, client *Client) (err error) {
|
|
vmr.SetVmType("lxc")
|
|
|
|
// convert config to map
|
|
params, _ := json.Marshal(&config)
|
|
var paramMap map[string]interface{}
|
|
json.Unmarshal(params, ¶mMap)
|
|
|
|
// build list of features
|
|
// add features as parameter list to lxc parameters
|
|
// this overwrites the orginal formatting with a
|
|
// comma separated list of "key=value" pairs
|
|
featuresParam := QemuDeviceParam{}
|
|
featuresParam = featuresParam.createDeviceParam(config.Features, nil)
|
|
if len(featuresParam) > 0 {
|
|
paramMap["features"] = strings.Join(featuresParam, ",")
|
|
}
|
|
|
|
// build list of mountpoints
|
|
// this does the same as for the feature list
|
|
// except that there can be multiple of these mountpoint sets
|
|
// and each mountpoint set comes with a new id
|
|
for mpID, mpConfMap := range config.Mountpoints {
|
|
mpConfParam := QemuDeviceParam{}
|
|
mpConfParam = mpConfParam.createDeviceParam(mpConfMap, nil)
|
|
|
|
// add mp to lxc parameters
|
|
mpName := fmt.Sprintf("mp%v", mpID)
|
|
paramMap[mpName] = strings.Join(mpConfParam, ",")
|
|
}
|
|
|
|
// build list of network parameters
|
|
for nicID, nicConfMap := range config.Networks {
|
|
nicConfParam := QemuDeviceParam{}
|
|
nicConfParam = nicConfParam.createDeviceParam(nicConfMap, nil)
|
|
|
|
// add nic to lxc parameters
|
|
nicName := fmt.Sprintf("net%v", nicID)
|
|
paramMap[nicName] = strings.Join(nicConfParam, ",")
|
|
}
|
|
|
|
// build list of unused volumes for sake of completenes,
|
|
// even if it is not recommended to change these volumes manually
|
|
for volID, vol := range config.Unused {
|
|
// add volume to lxc parameters
|
|
volName := fmt.Sprintf("unused%v", volID)
|
|
paramMap[volName] = vol
|
|
}
|
|
|
|
// now that we concatenated the key value parameter
|
|
// list for the networks, mountpoints and unused volumes,
|
|
// remove the original keys, since the Proxmox API does
|
|
// not know how to handle this key
|
|
delete(paramMap, "networks")
|
|
delete(paramMap, "mountpoints")
|
|
delete(paramMap, "unused")
|
|
|
|
// amend vmid
|
|
paramMap["vmid"] = vmr.vmId
|
|
|
|
exitStatus, err := client.CreateLxcContainer(vmr.node, paramMap)
|
|
if err != nil {
|
|
return fmt.Errorf("Error creating LXC container: %v, error status: %s (params: %v)", err, exitStatus, params)
|
|
}
|
|
return
|
|
}
|
|
|
|
func (config configLxc) UpdateConfig(vmr *VmRef, client *Client) (err error) {
|
|
// convert config to map
|
|
params, _ := json.Marshal(&config)
|
|
var paramMap map[string]interface{}
|
|
json.Unmarshal(params, ¶mMap)
|
|
|
|
// build list of features
|
|
// add features as parameter list to lxc parameters
|
|
// this overwrites the orginal formatting with a
|
|
// comma separated list of "key=value" pairs
|
|
featuresParam := QemuDeviceParam{}
|
|
featuresParam = featuresParam.createDeviceParam(config.Features, nil)
|
|
paramMap["features"] = strings.Join(featuresParam, ",")
|
|
|
|
// build list of mountpoints
|
|
// this does the same as for the feature list
|
|
// except that there can be multiple of these mountpoint sets
|
|
// and each mountpoint set comes with a new id
|
|
for mpID, mpConfMap := range config.Mountpoints {
|
|
mpConfParam := QemuDeviceParam{}
|
|
mpConfParam = mpConfParam.createDeviceParam(mpConfMap, nil)
|
|
|
|
// add mp to lxc parameters
|
|
mpName := fmt.Sprintf("mp%v", mpID)
|
|
paramMap[mpName] = strings.Join(mpConfParam, ",")
|
|
}
|
|
|
|
// build list of network parameters
|
|
for nicID, nicConfMap := range config.Networks {
|
|
nicConfParam := QemuDeviceParam{}
|
|
nicConfParam = nicConfParam.createDeviceParam(nicConfMap, nil)
|
|
|
|
// add nic to lxc parameters
|
|
nicName := fmt.Sprintf("net%v", nicID)
|
|
paramMap[nicName] = strings.Join(nicConfParam, ",")
|
|
}
|
|
|
|
// build list of unused volumes for sake of completenes,
|
|
// even if it is not recommended to change these volumes manually
|
|
for volID, vol := range config.Unused {
|
|
// add volume to lxc parameters
|
|
volName := fmt.Sprintf("unused%v", volID)
|
|
paramMap[volName] = vol
|
|
}
|
|
|
|
// now that we concatenated the key value parameter
|
|
// list for the networks, mountpoints and unused volumes,
|
|
// remove the original keys, since the Proxmox API does
|
|
// not know how to handle this key
|
|
delete(paramMap, "networks")
|
|
delete(paramMap, "mountpoints")
|
|
delete(paramMap, "unused")
|
|
|
|
// delete parameters wich are not supported in updated operations
|
|
delete(paramMap, "pool")
|
|
delete(paramMap, "storage")
|
|
delete(paramMap, "password")
|
|
delete(paramMap, "ostemplate")
|
|
delete(paramMap, "start")
|
|
// even though it is listed as a PUT option in the API documentation
|
|
// we remove it here because "it should not be modified manually";
|
|
// also, error "500 unable to modify read-only option: 'unprivileged'"
|
|
delete(paramMap, "unprivileged")
|
|
|
|
_, err = client.SetLxcConfig(vmr, paramMap)
|
|
return err
|
|
}
|