1 Star 0 Fork 0

albert1231/proxmox-api-go

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
client.go 40.95 KB
一键复制 编辑 原始数据 按行查看 历史
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510
package proxmox
// inspired by https://github.com/Telmate/vagrant-proxmox/blob/master/lib/vagrant-proxmox/proxmox/connection.rb
import (
"bytes"
"crypto/tls"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"log"
"mime/multipart"
"net"
"net/http"
"os"
"regexp"
"strconv"
"strings"
"time"
)
// TaskStatusCheckInterval - time between async checks in seconds
const TaskStatusCheckInterval = 2
const exitStatusSuccess = "OK"
// Client - URL, user and password to specifc Proxmox node
type Client struct {
session *Session
ApiUrl string
Username string
Password string
Otp string
TaskTimeout int
}
// VmRef - virtual machine ref parts
// map[type:qemu node:proxmox1-xx id:qemu/132 diskread:5.57424738e+08 disk:0 netin:5.9297450593e+10 mem:3.3235968e+09 uptime:1.4567097e+07 vmid:132 template:0 maxcpu:2 netout:6.053310416e+09 maxdisk:3.4359738368e+10 maxmem:8.592031744e+09 diskwrite:1.49663619584e+12 status:running cpu:0.00386980694947209 name:appt-app1-dev.xxx.xx]
type VmRef struct {
vmId int
node string
pool string
vmType string
haState string
haGroup string
}
func (vmr *VmRef) SetNode(node string) {
vmr.node = node
}
func (vmr *VmRef) SetPool(pool string) {
vmr.pool = pool
}
func (vmr *VmRef) SetVmType(vmType string) {
vmr.vmType = vmType
}
func (vmr *VmRef) GetVmType() string {
return vmr.vmType
}
func (vmr *VmRef) VmId() int {
return vmr.vmId
}
func (vmr *VmRef) Node() string {
return vmr.node
}
func (vmr *VmRef) Pool() string {
return vmr.pool
}
func (vmr *VmRef) HaState() string {
return vmr.haState
}
func (vmr *VmRef) HaGroup() string {
return vmr.haGroup
}
func NewVmRef(vmId int) (vmr *VmRef) {
vmr = &VmRef{vmId: vmId, node: "", vmType: ""}
return
}
func NewClient(apiUrl string, hclient *http.Client, tls *tls.Config, proxyString string, taskTimeout int) (client *Client, err error) {
var sess *Session
sess, err = NewSession(apiUrl, hclient, proxyString, tls)
if err == nil {
client = &Client{session: sess, ApiUrl: apiUrl, TaskTimeout: taskTimeout}
}
return client, err
}
// SetAPIToken specifies a pair of user identifier and token UUID to use
// for authenticating API calls.
// If this is set, a ticket from calling `Login` will not be used.
//
// - `userID` is expected to be in the form `USER@REALM!TOKENID`
// - `token` is just the UUID you get when initially creating the token
//
// See https://pve.proxmox.com/wiki/User_Management#pveum_tokens
func (c *Client) SetAPIToken(userID, token string) {
c.session.SetAPIToken(userID, token)
}
func (c *Client) Login(username string, password string, otp string) (err error) {
c.Username = username
c.Password = password
c.Otp = otp
return c.session.Login(username, password, otp)
}
func (c *Client) GetVersion() (data map[string]interface{}, err error) {
resp, err := c.session.Get("/version", nil, nil)
if err != nil {
return nil, err
}
return ResponseJSON(resp)
}
func (c *Client) GetJsonRetryable(url string, data *map[string]interface{}, tries int) error {
var statErr error
for ii := 0; ii < tries; ii++ {
_, statErr = c.session.GetJSON(url, nil, nil, data)
if statErr == nil {
return nil
}
log.Printf("[DEBUG][GetJsonRetryable] Sleeping for %d seconds before asking url %s", ii+1, url)
time.Sleep(time.Duration(ii+1) * time.Second)
}
return statErr
}
func (c *Client) GetNodeList() (list map[string]interface{}, err error) {
err = c.GetJsonRetryable("/nodes", &list, 3)
return
}
func (c *Client) GetVmList() (list map[string]interface{}, err error) {
err = c.GetJsonRetryable("/cluster/resources?type=vm", &list, 3)
return
}
func (c *Client) CheckVmRef(vmr *VmRef) (err error) {
if vmr.node == "" || vmr.vmType == "" {
_, err = c.GetVmInfo(vmr)
}
return
}
func (c *Client) GetVmInfo(vmr *VmRef) (vmInfo map[string]interface{}, err error) {
resp, err := c.GetVmList()
vms := resp["data"].([]interface{})
for vmii := range vms {
vm := vms[vmii].(map[string]interface{})
if int(vm["vmid"].(float64)) == vmr.vmId {
vmInfo = vm
vmr.node = vmInfo["node"].(string)
vmr.vmType = vmInfo["type"].(string)
vmr.pool = ""
if vmInfo["pool"] != nil {
vmr.pool = vmInfo["pool"].(string)
}
if vmInfo["hastate"] != nil {
vmr.haState = vmInfo["hastate"].(string)
}
return
}
}
return nil, fmt.Errorf("vm '%d' not found", vmr.vmId)
}
func (c *Client) GetVmRefByName(vmName string) (vmr *VmRef, err error) {
vmrs, err := c.GetVmRefsByName(vmName)
if err != nil {
return nil, err
}
return vmrs[0], nil
}
func (c *Client) GetVmRefsByName(vmName string) (vmrs []*VmRef, err error) {
resp, err := c.GetVmList()
vms := resp["data"].([]interface{})
for vmii := range vms {
vm := vms[vmii].(map[string]interface{})
if vm["name"] != nil && vm["name"].(string) == vmName {
vmr := NewVmRef(int(vm["vmid"].(float64)))
vmr.node = vm["node"].(string)
vmr.vmType = vm["type"].(string)
vmr.pool = ""
if vm["pool"] != nil {
vmr.pool = vm["pool"].(string)
}
if vm["hastate"] != nil {
vmr.haState = vm["hastate"].(string)
}
vmrs = append(vmrs, vmr)
}
}
if len(vmrs) == 0 {
return nil, fmt.Errorf("vm '%s' not found", vmName)
} else {
return
}
}
func (c *Client) GetVmState(vmr *VmRef) (vmState map[string]interface{}, err error) {
err = c.CheckVmRef(vmr)
if err != nil {
return nil, err
}
var data map[string]interface{}
url := fmt.Sprintf("/nodes/%s/%s/%d/status/current", vmr.node, vmr.vmType, vmr.vmId)
err = c.GetJsonRetryable(url, &data, 3)
if err != nil {
return nil, err
}
if data["data"] == nil {
return nil, fmt.Errorf("vm STATE not readable")
}
vmState = data["data"].(map[string]interface{})
return
}
func (c *Client) GetVmConfig(vmr *VmRef) (vmConfig map[string]interface{}, err error) {
err = c.CheckVmRef(vmr)
if err != nil {
return nil, err
}
var data map[string]interface{}
url := fmt.Sprintf("/nodes/%s/%s/%d/config", vmr.node, vmr.vmType, vmr.vmId)
err = c.GetJsonRetryable(url, &data, 3)
if err != nil {
return nil, err
}
if data["data"] == nil {
return nil, fmt.Errorf("vm CONFIG not readable")
}
vmConfig = data["data"].(map[string]interface{})
return
}
func (c *Client) GetStorageStatus(vmr *VmRef, storageName string) (storageStatus map[string]interface{}, err error) {
err = c.CheckVmRef(vmr)
if err != nil {
return nil, err
}
var data map[string]interface{}
url := fmt.Sprintf("/nodes/%s/storage/%s/status", vmr.node, storageName)
err = c.GetJsonRetryable(url, &data, 3)
if err != nil {
return nil, err
}
if data["data"] == nil {
return nil, fmt.Errorf("storage STATUS not readable")
}
storageStatus = data["data"].(map[string]interface{})
return
}
func (c *Client) GetStorageContent(vmr *VmRef, storageName string) (data map[string]interface{}, err error) {
err = c.CheckVmRef(vmr)
if err != nil {
return nil, err
}
url := fmt.Sprintf("/nodes/%s/storage/%s/content", vmr.node, storageName)
err = c.GetJsonRetryable(url, &data, 3)
if err != nil {
return nil, err
}
if data["data"] == nil {
return nil, fmt.Errorf("storage Content not readable")
}
return
}
func (c *Client) GetVmSpiceProxy(vmr *VmRef) (vmSpiceProxy map[string]interface{}, err error) {
err = c.CheckVmRef(vmr)
if err != nil {
return nil, err
}
var data map[string]interface{}
url := fmt.Sprintf("/nodes/%s/%s/%d/spiceproxy", vmr.node, vmr.vmType, vmr.vmId)
_, err = c.session.PostJSON(url, nil, nil, nil, &data)
if err != nil {
return nil, err
}
if data["data"] == nil {
return nil, fmt.Errorf("vm SpiceProxy not readable")
}
vmSpiceProxy = data["data"].(map[string]interface{})
return
}
type AgentNetworkInterface struct {
MACAddress string
IPAddresses []net.IP
Name string
Statistics map[string]int64
}
func (a *AgentNetworkInterface) UnmarshalJSON(b []byte) error {
var intermediate struct {
HardwareAddress string `json:"hardware-address"`
IPAddresses []struct {
IPAddress string `json:"ip-address"`
IPAddressType string `json:"ip-address-type"`
Prefix int `json:"prefix"`
} `json:"ip-addresses"`
Name string `json:"name"`
Statistics map[string]int64 `json:"statistics"`
}
err := json.Unmarshal(b, &intermediate)
if err != nil {
return err
}
a.IPAddresses = make([]net.IP, len(intermediate.IPAddresses))
for idx, ip := range intermediate.IPAddresses {
a.IPAddresses[idx] = net.ParseIP((strings.Split(ip.IPAddress, "%"))[0])
if a.IPAddresses[idx] == nil {
return fmt.Errorf("could not parse %s as IP", ip.IPAddress)
}
}
a.MACAddress = intermediate.HardwareAddress
a.Name = intermediate.Name
a.Statistics = intermediate.Statistics
return nil
}
func (c *Client) GetVmAgentNetworkInterfaces(vmr *VmRef) ([]AgentNetworkInterface, error) {
var ifs []AgentNetworkInterface
err := c.doAgentGet(vmr, "network-get-interfaces", &ifs)
return ifs, err
}
func (c *Client) doAgentGet(vmr *VmRef, command string, output interface{}) error {
err := c.CheckVmRef(vmr)
if err != nil {
return err
}
url := fmt.Sprintf("/nodes/%s/%s/%d/agent/%s", vmr.node, vmr.vmType, vmr.vmId, command)
resp, err := c.session.Get(url, nil, nil)
if err != nil {
return err
}
return TypedResponse(resp, output)
}
func (c *Client) CreateTemplate(vmr *VmRef) error {
err := c.CheckVmRef(vmr)
if err != nil {
return err
}
url := fmt.Sprintf("/nodes/%s/%s/%d/template", vmr.node, vmr.vmType, vmr.vmId)
_, err = c.session.Post(url, nil, nil, nil)
if err != nil {
return err
}
return nil
}
func (c *Client) MonitorCmd(vmr *VmRef, command string) (monitorRes map[string]interface{}, err error) {
err = c.CheckVmRef(vmr)
if err != nil {
return nil, err
}
reqbody := ParamsToBody(map[string]interface{}{"command": command})
url := fmt.Sprintf("/nodes/%s/%s/%d/monitor", vmr.node, vmr.vmType, vmr.vmId)
resp, err := c.session.Post(url, nil, nil, &reqbody)
if err != nil {
return nil, err
}
monitorRes, err = ResponseJSON(resp)
return
}
func (c *Client) Sendkey(vmr *VmRef, qmKey string) error {
err := c.CheckVmRef(vmr)
if err != nil {
return err
}
reqbody := ParamsToBody(map[string]interface{}{"key": qmKey})
url := fmt.Sprintf("/nodes/%s/%s/%d/sendkey", vmr.node, vmr.vmType, vmr.vmId)
// No return, even for errors: https://bugzilla.proxmox.com/show_bug.cgi?id=2275
_, err = c.session.Put(url, nil, nil, &reqbody)
return err
}
// WaitForCompletion - poll the API for task completion
func (c *Client) WaitForCompletion(taskResponse map[string]interface{}) (waitExitStatus string, err error) {
if taskResponse["errors"] != nil {
errJSON, _ := json.MarshalIndent(taskResponse["errors"], "", " ")
return string(errJSON), fmt.Errorf("error reponse")
}
if taskResponse["data"] == nil {
return "", nil
}
waited := 0
taskUpid := taskResponse["data"].(string)
for waited < c.TaskTimeout {
exitStatus, statErr := c.GetTaskExitstatus(taskUpid)
if statErr != nil {
if statErr != io.ErrUnexpectedEOF { // don't give up on ErrUnexpectedEOF
return "", statErr
}
}
if exitStatus != nil {
waitExitStatus = exitStatus.(string)
return
}
time.Sleep(TaskStatusCheckInterval * time.Second)
waited = waited + TaskStatusCheckInterval
}
return "", fmt.Errorf("Wait timeout for:" + taskUpid)
}
var rxTaskNode = regexp.MustCompile("UPID:(.*?):")
func (c *Client) GetTaskExitstatus(taskUpid string) (exitStatus interface{}, err error) {
node := rxTaskNode.FindStringSubmatch(taskUpid)[1]
url := fmt.Sprintf("/nodes/%s/tasks/%s/status", node, taskUpid)
var data map[string]interface{}
_, err = c.session.GetJSON(url, nil, nil, &data)
if err == nil {
exitStatus = data["data"].(map[string]interface{})["exitstatus"]
}
if exitStatus != nil && exitStatus != exitStatusSuccess {
err = fmt.Errorf(exitStatus.(string))
}
return
}
func (c *Client) StatusChangeVm(vmr *VmRef, setStatus string) (exitStatus string, err error) {
err = c.CheckVmRef(vmr)
if err != nil {
return "", err
}
url := fmt.Sprintf("/nodes/%s/%s/%d/status/%s", vmr.node, vmr.vmType, vmr.vmId, setStatus)
var taskResponse map[string]interface{}
for i := 0; i < 3; i++ {
_, err = c.session.PostJSON(url, nil, nil, nil, &taskResponse)
exitStatus, err = c.WaitForCompletion(taskResponse)
if exitStatus == "" {
time.Sleep(TaskStatusCheckInterval * time.Second)
} else {
return
}
}
return
}
func (c *Client) StartVm(vmr *VmRef) (exitStatus string, err error) {
return c.StatusChangeVm(vmr, "start")
}
func (c *Client) StopVm(vmr *VmRef) (exitStatus string, err error) {
return c.StatusChangeVm(vmr, "stop")
}
func (c *Client) ShutdownVm(vmr *VmRef) (exitStatus string, err error) {
return c.StatusChangeVm(vmr, "shutdown")
}
func (c *Client) ResetVm(vmr *VmRef) (exitStatus string, err error) {
return c.StatusChangeVm(vmr, "reset")
}
func (c *Client) SuspendVm(vmr *VmRef) (exitStatus string, err error) {
return c.StatusChangeVm(vmr, "suspend")
}
func (c *Client) ResumeVm(vmr *VmRef) (exitStatus string, err error) {
return c.StatusChangeVm(vmr, "resume")
}
func (c *Client) DeleteVm(vmr *VmRef) (exitStatus string, err error) {
return c.DeleteVmParams(vmr, nil)
}
func (c *Client) DeleteVmParams(vmr *VmRef, params map[string]interface{}) (exitStatus string, err error) {
err = c.CheckVmRef(vmr)
if err != nil {
return "", err
}
//Remove HA if required
if vmr.haState != "" {
url := fmt.Sprintf("/cluster/ha/resources/%d", vmr.vmId)
resp, err := c.session.Delete(url, nil, nil)
if err == nil {
taskResponse, err := ResponseJSON(resp)
if err != nil {
return "", err
}
exitStatus, err = c.WaitForCompletion(taskResponse)
if err != nil {
return "", err
}
}
}
values := ParamsToValues(params)
url := fmt.Sprintf("/nodes/%s/%s/%d", vmr.node, vmr.vmType, vmr.vmId)
var taskResponse map[string]interface{}
if len(values) != 0 {
_, err = c.session.RequestJSON("DELETE", url, &values, nil, nil, &taskResponse)
} else {
_, err = c.session.RequestJSON("DELETE", url, nil, nil, nil, &taskResponse)
}
if err != nil {
return
}
exitStatus, err = c.WaitForCompletion(taskResponse)
return
}
func (c *Client) CreateQemuVm(node string, vmParams map[string]interface{}) (exitStatus string, err error) {
// Create VM disks first to ensure disks names.
createdDisks, createdDisksErr := c.createVMDisks(node, vmParams)
if createdDisksErr != nil {
return "", createdDisksErr
}
// Then create the VM itself.
reqbody := ParamsToBody(vmParams)
url := fmt.Sprintf("/nodes/%s/qemu", node)
var resp *http.Response
resp, err = c.session.Post(url, nil, nil, &reqbody)
if err != nil {
defer resp.Body.Close()
// This might not work if we never got a body. We'll ignore errors in trying to read,
// but extract the body if possible to give any error information back in the exitStatus
b, _ := ioutil.ReadAll(resp.Body)
exitStatus = string(b)
return exitStatus, err
}
taskResponse, err := ResponseJSON(resp)
if err != nil {
return "", err
}
exitStatus, err = c.WaitForCompletion(taskResponse)
// Delete VM disks if the VM didn't create.
if exitStatus != "OK" {
deleteDisksErr := c.DeleteVMDisks(node, createdDisks)
if deleteDisksErr != nil {
return "", deleteDisksErr
}
}
return
}
func (c *Client) CreateLxcContainer(node string, vmParams map[string]interface{}) (exitStatus string, err error) {
reqbody := ParamsToBody(vmParams)
url := fmt.Sprintf("/nodes/%s/lxc", node)
var resp *http.Response
resp, err = c.session.Post(url, nil, nil, &reqbody)
if err != nil {
defer resp.Body.Close()
// This might not work if we never got a body. We'll ignore errors in trying to read,
// but extract the body if possible to give any error information back in the exitStatus
b, _ := ioutil.ReadAll(resp.Body)
exitStatus = string(b)
return exitStatus, err
}
taskResponse, err := ResponseJSON(resp)
if err != nil {
return "", err
}
exitStatus, err = c.WaitForCompletion(taskResponse)
return
}
func (c *Client) CloneLxcContainer(vmr *VmRef, vmParams map[string]interface{}) (exitStatus string, err error) {
reqbody := ParamsToBody(vmParams)
url := fmt.Sprintf("/nodes/%s/lxc/%s/clone", vmr.node, vmParams["vmid"])
resp, err := c.session.Post(url, nil, nil, &reqbody)
if err == nil {
taskResponse, err := ResponseJSON(resp)
if err != nil {
return "", err
}
exitStatus, err = c.WaitForCompletion(taskResponse)
}
return
}
func (c *Client) CloneQemuVm(vmr *VmRef, vmParams map[string]interface{}) (exitStatus string, err error) {
reqbody := ParamsToBody(vmParams)
url := fmt.Sprintf("/nodes/%s/qemu/%d/clone", vmr.node, vmr.vmId)
resp, err := c.session.Post(url, nil, nil, &reqbody)
if err == nil {
taskResponse, err := ResponseJSON(resp)
if err != nil {
return "", err
}
exitStatus, err = c.WaitForCompletion(taskResponse)
}
return
}
func (c *Client) CreateQemuSnapshot(vmr *VmRef, snapshotName string) (exitStatus string, err error) {
err = c.CheckVmRef(vmr)
snapshotParams := map[string]interface{}{
"snapname": snapshotName,
}
reqbody := ParamsToBody(snapshotParams)
if err != nil {
return "", err
}
url := fmt.Sprintf("/nodes/%s/%s/%d/snapshot/", vmr.node, vmr.vmType, vmr.vmId)
resp, err := c.session.Post(url, nil, nil, &reqbody)
if err == nil {
taskResponse, err := ResponseJSON(resp)
if err != nil {
return "", err
}
exitStatus, err = c.WaitForCompletion(taskResponse)
}
return
}
func (c *Client) DeleteQemuSnapshot(vmr *VmRef, snapshotName string) (exitStatus string, err error) {
err = c.CheckVmRef(vmr)
if err != nil {
return "", err
}
url := fmt.Sprintf("/nodes/%s/%s/%d/snapshot/%s", vmr.node, vmr.vmType, vmr.vmId, snapshotName)
resp, err := c.session.Delete(url, nil, nil)
if err == nil {
taskResponse, err := ResponseJSON(resp)
if err != nil {
return "", err
}
exitStatus, err = c.WaitForCompletion(taskResponse)
}
return
}
func (c *Client) ListQemuSnapshot(vmr *VmRef) (taskResponse map[string]interface{}, exitStatus string, err error) {
err = c.CheckVmRef(vmr)
if err != nil {
return nil, "", err
}
url := fmt.Sprintf("/nodes/%s/%s/%d/snapshot/", vmr.node, vmr.vmType, vmr.vmId)
resp, err := c.session.Get(url, nil, nil)
if err == nil {
taskResponse, err := ResponseJSON(resp)
if err != nil {
return nil, "", err
}
return taskResponse, "", nil
}
return
}
func (c *Client) RollbackQemuVm(vmr *VmRef, snapshot string) (exitStatus string, err error) {
err = c.CheckVmRef(vmr)
if err != nil {
return "", err
}
url := fmt.Sprintf("/nodes/%s/%s/%d/snapshot/%s/rollback", vmr.node, vmr.vmType, vmr.vmId, snapshot)
var taskResponse map[string]interface{}
_, err = c.session.PostJSON(url, nil, nil, nil, &taskResponse)
exitStatus, err = c.WaitForCompletion(taskResponse)
return
}
// SetVmConfig - send config options
func (c *Client) SetVmConfig(vmr *VmRef, vmParams map[string]interface{}) (exitStatus interface{}, err error) {
reqbody := ParamsToBody(vmParams)
url := fmt.Sprintf("/nodes/%s/%s/%d/config", vmr.node, vmr.vmType, vmr.vmId)
resp, err := c.session.Post(url, nil, nil, &reqbody)
if err == nil {
taskResponse, err := ResponseJSON(resp)
if err != nil {
return nil, err
}
exitStatus, err = c.WaitForCompletion(taskResponse)
}
return
}
// SetLxcConfig - send config options
func (c *Client) SetLxcConfig(vmr *VmRef, vmParams map[string]interface{}) (exitStatus interface{}, err error) {
reqbody := ParamsToBody(vmParams)
url := fmt.Sprintf("/nodes/%s/%s/%d/config", vmr.node, vmr.vmType, vmr.vmId)
resp, err := c.session.Put(url, nil, nil, &reqbody)
if err == nil {
taskResponse, err := ResponseJSON(resp)
if err != nil {
return nil, err
}
exitStatus, err = c.WaitForCompletion(taskResponse)
}
return
}
// MigrateNode - Migrate a VM
func (c *Client) MigrateNode(vmr *VmRef, newTargetNode string, online bool) (exitStatus interface{}, err error) {
reqbody := ParamsToBody(map[string]interface{}{"target": newTargetNode, "online": online, "with-local-disks": true})
url := fmt.Sprintf("/nodes/%s/%s/%d/migrate", vmr.node, vmr.vmType, vmr.vmId)
resp, err := c.session.Post(url, nil, nil, &reqbody)
if err == nil {
taskResponse, err := ResponseJSON(resp)
if err != nil {
return nil, err
}
exitStatus, err = c.WaitForCompletion(taskResponse)
return exitStatus, err
}
return nil, err
}
// ResizeQemuDisk allows the caller to increase the size of a disk by the indicated number of gigabytes
func (c *Client) ResizeQemuDisk(vmr *VmRef, disk string, moreSizeGB int) (exitStatus interface{}, err error) {
size := fmt.Sprintf("+%dG", moreSizeGB)
return c.ResizeQemuDiskRaw(vmr, disk, size)
}
// ResizeQemuDiskRaw allows the caller to provide the raw resize string to be send to proxmox.
// See the proxmox API documentation for full information, but the short version is if you prefix
// your desired size with a '+' character it will ADD size to the disk. If you just specify the size by
// itself it will do an absolute resizing to the specified size. Permitted suffixes are K, M, G, T
// to indicate order of magnitude (kilobyte, megabyte, etc). Decrease of disk size is not permitted.
func (c *Client) ResizeQemuDiskRaw(vmr *VmRef, disk string, size string) (exitStatus interface{}, err error) {
// PUT
//disk:virtio0
//size:+2G
if disk == "" {
disk = "virtio0"
}
reqbody := ParamsToBody(map[string]interface{}{"disk": disk, "size": size})
url := fmt.Sprintf("/nodes/%s/%s/%d/resize", vmr.node, vmr.vmType, vmr.vmId)
resp, err := c.session.Put(url, nil, nil, &reqbody)
if err == nil {
taskResponse, err := ResponseJSON(resp)
if err != nil {
return nil, err
}
exitStatus, err = c.WaitForCompletion(taskResponse)
}
return
}
func (c *Client) MoveLxcDisk(vmr *VmRef, disk string, storage string) (exitStatus interface{}, err error) {
reqbody := ParamsToBody(map[string]interface{}{"disk": disk, "storage": storage, "delete": true})
url := fmt.Sprintf("/nodes/%s/%s/%d/move_volume", vmr.node, vmr.vmType, vmr.vmId)
resp, err := c.session.Post(url, nil, nil, &reqbody)
if err == nil {
taskResponse, err := ResponseJSON(resp)
if err != nil {
return nil, err
}
exitStatus, err = c.WaitForCompletion(taskResponse)
}
return
}
func (c *Client) MoveQemuDisk(vmr *VmRef, disk string, storage string) (exitStatus interface{}, err error) {
if disk == "" {
disk = "virtio0"
}
reqbody := ParamsToBody(map[string]interface{}{"disk": disk, "storage": storage, "delete": true})
url := fmt.Sprintf("/nodes/%s/%s/%d/move_disk", vmr.node, vmr.vmType, vmr.vmId)
resp, err := c.session.Post(url, nil, nil, &reqbody)
if err == nil {
taskResponse, err := ResponseJSON(resp)
if err != nil {
return nil, err
}
exitStatus, err = c.WaitForCompletion(taskResponse)
}
return
}
// GetNextID - Get next free VMID
func (c *Client) GetNextID(currentID int) (nextID int, err error) {
var data map[string]interface{}
var url string
if currentID >= 100 {
url = fmt.Sprintf("/cluster/nextid?vmid=%d", currentID)
} else {
url = "/cluster/nextid"
}
_, err = c.session.GetJSON(url, nil, nil, &data)
if err == nil {
if data["errors"] != nil {
if currentID >= 100 {
return c.GetNextID(currentID + 1)
} else {
return -1, fmt.Errorf("error using /cluster/nextid")
}
}
nextID, err = strconv.Atoi(data["data"].(string))
} else if strings.HasPrefix(err.Error(), "400 ") {
return c.GetNextID(currentID + 1)
}
return
}
// CreateVMDisk - Create single disk for VM on host node.
func (c *Client) CreateVMDisk(
nodeName string,
storageName string,
fullDiskName string,
diskParams map[string]interface{},
) error {
reqbody := ParamsToBody(diskParams)
url := fmt.Sprintf("/nodes/%s/storage/%s/content", nodeName, storageName)
resp, err := c.session.Post(url, nil, nil, &reqbody)
if err == nil {
taskResponse, err := ResponseJSON(resp)
if err != nil {
return err
}
if diskName, containsData := taskResponse["data"]; !containsData || diskName != fullDiskName {
return fmt.Errorf("cannot create VM disk %s - %s", fullDiskName, diskName)
}
} else {
return err
}
return nil
}
var rxStorageModels = regexp.MustCompile(`(ide|sata|scsi|virtio)\d+`)
// createVMDisks - Make disks parameters and create all VM disks on host node.
func (c *Client) createVMDisks(
node string,
vmParams map[string]interface{},
) (disks []string, err error) {
var createdDisks []string
vmID := vmParams["vmid"].(int)
for deviceName, deviceConf := range vmParams {
if matched := rxStorageModels.MatchString(deviceName); matched {
deviceConfMap := ParsePMConf(deviceConf.(string), "")
// This if condition to differentiate between `disk` and `cdrom`.
if media, containsFile := deviceConfMap["media"]; containsFile && media == "disk" {
fullDiskName := deviceConfMap["file"].(string)
storageName, volumeName := getStorageAndVolumeName(fullDiskName, ":")
diskParams := map[string]interface{}{
"vmid": vmID,
"filename": volumeName,
"size": deviceConfMap["size"],
}
err := c.CreateVMDisk(node, storageName, fullDiskName, diskParams)
if err != nil {
return createdDisks, err
} else {
createdDisks = append(createdDisks, fullDiskName)
}
}
}
}
return createdDisks, nil
}
// DeleteVMDisks - Delete VM disks from host node.
// By default the VM disks are deteled when the VM is deleted,
// so mainly this is used to delete the disks in case VM creation didn't complete.
func (c *Client) DeleteVMDisks(
node string,
disks []string,
) error {
for _, fullDiskName := range disks {
storageName, volumeName := getStorageAndVolumeName(fullDiskName, ":")
url := fmt.Sprintf("/nodes/%s/storage/%s/content/%s", node, storageName, volumeName)
_, err := c.session.Post(url, nil, nil, nil)
if err != nil {
return err
}
}
return nil
}
// VzDump - Create backup
func (c *Client) VzDump(vmr *VmRef, params map[string]interface{}) (exitStatus interface{}, err error) {
err = c.CheckVmRef(vmr)
if err != nil {
return nil, err
}
reqbody := ParamsToBody(params)
url := fmt.Sprintf("/nodes/%s/vzdump", vmr.node)
resp, err := c.session.Post(url, nil, nil, &reqbody)
if err == nil {
taskResponse, err := ResponseJSON(resp)
if err != nil {
return nil, err
}
exitStatus, err = c.WaitForCompletion(taskResponse)
}
return
}
func (c *Client) GetVzDump() (pools map[string]interface{}, err error) {
resp, err := c.session.Get("/nodes/bve-node1/storage/local/prunebackups", nil, nil)
if err != nil {
return nil, err
}
return ResponseJSON(resp)
}
// DeleteVolume - Delete volume
func (c *Client) DeleteVolume(vmr *VmRef, storageName string, volumeName string) (exitStatus interface{}, err error) {
err = c.CheckVmRef(vmr)
if err != nil {
return nil, err
}
url := fmt.Sprintf("/nodes/%s/storage/%s/content/%s", vmr.node, storageName, volumeName)
resp, err := c.session.Delete(url, nil, nil)
if err == nil {
taskResponse, err := ResponseJSON(resp)
if err != nil {
return nil, err
}
exitStatus, err = c.WaitForCompletion(taskResponse)
}
return
}
// CreateVNCProxy - Creates a TCP VNC proxy connections
func (c *Client) CreateVNCProxy(vmr *VmRef, params map[string]interface{}) (vncProxyRes map[string]interface{}, err error) {
err = c.CheckVmRef(vmr)
if err != nil {
return nil, err
}
reqbody := ParamsToBody(params)
url := fmt.Sprintf("/nodes/%s/qemu/%d/vncproxy", vmr.node, vmr.vmId)
resp, err := c.session.Post(url, nil, nil, &reqbody)
if err != nil {
return nil, err
}
vncProxyRes, err = ResponseJSON(resp)
if err != nil {
return nil, err
}
if vncProxyRes["data"] == nil {
return nil, fmt.Errorf("VNC Proxy not readable")
}
vncProxyRes = vncProxyRes["data"].(map[string]interface{})
return
}
// QemuAgentPing - Execute ping.
func (c *Client) QemuAgentPing(vmr *VmRef) (pingRes map[string]interface{}, err error) {
err = c.CheckVmRef(vmr)
if err != nil {
return nil, err
}
url := fmt.Sprintf("/nodes/%s/qemu/%d/agent/ping", vmr.node, vmr.vmId)
resp, err := c.session.Post(url, nil, nil, nil)
if err == nil {
taskResponse, err := ResponseJSON(resp)
if err != nil {
return nil, err
}
if taskResponse["data"] == nil {
return nil, fmt.Errorf("qemu agent ping not readable")
}
pingRes = taskResponse["data"].(map[string]interface{})
}
return
}
// QemuAgentFileWrite - Writes the given file via guest agent.
func (c *Client) QemuAgentFileWrite(vmr *VmRef, params map[string]interface{}) (err error) {
err = c.CheckVmRef(vmr)
if err != nil {
return err
}
reqbody := ParamsToBody(params)
url := fmt.Sprintf("/nodes/%s/qemu/%d/agent/file-write", vmr.node, vmr.vmId)
_, err = c.session.Post(url, nil, nil, &reqbody)
return
}
// QemuAgentSetUserPassword - Sets the password for the given user to the given password.
func (c *Client) QemuAgentSetUserPassword(vmr *VmRef, params map[string]interface{}) (result map[string]interface{}, err error) {
err = c.CheckVmRef(vmr)
if err != nil {
return nil, err
}
reqbody := ParamsToBody(params)
url := fmt.Sprintf("/nodes/%s/qemu/%d/agent/set-user-password", vmr.node, vmr.vmId)
resp, err := c.session.Post(url, nil, nil, &reqbody)
if err == nil {
taskResponse, err := ResponseJSON(resp)
if err != nil {
return nil, err
}
if taskResponse["data"] == nil {
return nil, fmt.Errorf("qemu agent set user password not readable")
}
result = taskResponse["data"].(map[string]interface{})
}
return
}
// QemuAgentExec - Executes the given command in the vm via the guest-agent and returns an object with the pid.
func (c *Client) QemuAgentExec(vmr *VmRef, params map[string]interface{}) (result map[string]interface{}, err error) {
err = c.CheckVmRef(vmr)
if err != nil {
return nil, err
}
reqbody := ParamsToBody(params)
url := fmt.Sprintf("/nodes/%s/qemu/%d/agent/exec", vmr.node, vmr.vmId)
resp, err := c.session.Post(url, nil, nil, &reqbody)
if err == nil {
taskResponse, err := ResponseJSON(resp)
if err != nil {
return nil, err
}
if taskResponse["data"] == nil {
return nil, fmt.Errorf("qemu agent exec not readable")
}
result = taskResponse["data"].(map[string]interface{})
}
return
}
// GetExecStatus - Gets the status of the given pid started by the guest-agent
func (c *Client) GetExecStatus(vmr *VmRef, pid string) (status map[string]interface{}, err error) {
err = c.CheckVmRef(vmr)
if err != nil {
return nil, err
}
err = c.GetJsonRetryable(fmt.Sprintf("/nodes/%s/%s/%d/agent/exec-status?pid=%s", vmr.node, vmr.vmType, vmr.vmId, pid), &status, 3)
if err == nil {
status = status["data"].(map[string]interface{})
}
return
}
// SetQemuFirewallOptions - Set Firewall options.
func (c *Client) SetQemuFirewallOptions(vmr *VmRef, fwOptions map[string]interface{}) (exitStatus interface{}, err error) {
err = c.CheckVmRef(vmr)
if err != nil {
return nil, err
}
reqbody := ParamsToBody(fwOptions)
url := fmt.Sprintf("/nodes/%s/qemu/%d/firewall/options", vmr.node, vmr.vmId)
resp, err := c.session.Put(url, nil, nil, &reqbody)
if err == nil {
taskResponse, err := ResponseJSON(resp)
if err != nil {
return nil, err
}
exitStatus, err = c.WaitForCompletion(taskResponse)
}
return
}
// GetQemuFirewallOptions - Get VM firewall options.
func (c *Client) GetQemuFirewallOptions(vmr *VmRef) (firewallOptions map[string]interface{}, err error) {
err = c.CheckVmRef(vmr)
if err != nil {
return nil, err
}
url := fmt.Sprintf("/nodes/%s/qemu/%d/firewall/options", vmr.node, vmr.vmId)
resp, err := c.session.Get(url, nil, nil)
if err == nil {
firewallOptions, err := ResponseJSON(resp)
if err != nil {
return nil, err
}
return firewallOptions, nil
}
return
}
// CreateQemuIPSet - Create new IPSet
func (c *Client) CreateQemuIPSet(vmr *VmRef, params map[string]interface{}) (exitStatus interface{}, err error) {
err = c.CheckVmRef(vmr)
if err != nil {
return nil, err
}
reqbody := ParamsToBody(params)
url := fmt.Sprintf("/nodes/%s/qemu/%d/firewall/ipset", vmr.node, vmr.vmId)
resp, err := c.session.Post(url, nil, nil, &reqbody)
if err == nil {
taskResponse, err := ResponseJSON(resp)
if err != nil {
return nil, err
}
exitStatus, err = c.WaitForCompletion(taskResponse)
}
return
}
// AddQemuIPSet - Add IP or Network to IPSet.
func (c *Client) AddQemuIPSet(vmr *VmRef, name string, params map[string]interface{}) (exitStatus interface{}, err error) {
err = c.CheckVmRef(vmr)
if err != nil {
return nil, err
}
reqbody := ParamsToBody(params)
url := fmt.Sprintf("/nodes/%s/qemu/%d/firewall/ipset/%s", vmr.node, vmr.vmId, name)
resp, err := c.session.Post(url, nil, nil, &reqbody)
if err == nil {
taskResponse, err := ResponseJSON(resp)
if err != nil {
return nil, err
}
exitStatus, err = c.WaitForCompletion(taskResponse)
}
return
}
// GetQemuIPSet - List IPSets
func (c *Client) GetQemuIPSet(vmr *VmRef) (ipsets map[string]interface{}, err error) {
err = c.CheckVmRef(vmr)
if err != nil {
return nil, err
}
url := fmt.Sprintf("/nodes/%s/qemu/%d/firewall/ipset", vmr.node, vmr.vmId)
resp, err := c.session.Get(url, nil, nil)
if err == nil {
ipsets, err := ResponseJSON(resp)
if err != nil {
return nil, err
}
return ipsets, nil
}
return
}
// DeleteQemuIPSet - Delete IPSet
func (c *Client) DeleteQemuIPSet(vmr *VmRef, IPSetName string) (exitStatus interface{}, err error) {
err = c.CheckVmRef(vmr)
if err != nil {
return nil, err
}
url := fmt.Sprintf("/nodes/%s/qemu/%d/firewall/ipset/%s", vmr.node, vmr.vmId, IPSetName)
resp, err := c.session.Delete(url, nil, nil)
if err == nil {
taskResponse, err := ResponseJSON(resp)
if err != nil {
return nil, err
}
exitStatus, err = c.WaitForCompletion(taskResponse)
}
return
}
// DeleteQemuIPSetNetwork - Remove IP or Network from IPSet.
func (c *Client) DeleteQemuIPSetNetwork(vmr *VmRef, IPSetName string, network string, params map[string]interface{}) (exitStatus interface{}, err error) {
err = c.CheckVmRef(vmr)
if err != nil {
return nil, err
}
values := ParamsToValues(params)
url := fmt.Sprintf("/nodes/%s/qemu/%d/firewall/ipset/%s/%s", vmr.node, vmr.vmId, IPSetName, network)
resp, err := c.session.Delete(url, &values, nil)
if err == nil {
taskResponse, err := ResponseJSON(resp)
if err != nil {
return nil, err
}
exitStatus, err = c.WaitForCompletion(taskResponse)
}
return
}
func (c *Client) Upload(node string, storage string, contentType string, filename string, file io.Reader) error {
var doStreamingIO bool
var fileSize int64
var contentLength int64
if f, ok := file.(*os.File); ok {
doStreamingIO = true
fileInfo, err := f.Stat()
if err != nil {
return err
}
fileSize = fileInfo.Size()
}
var body io.Reader
var mimetype string
var err error
if doStreamingIO {
body, mimetype, contentLength, err = createStreamedUploadBody(contentType, filename, fileSize, file)
} else {
body, mimetype, err = createUploadBody(contentType, filename, file)
}
if err != nil {
return err
}
url := fmt.Sprintf("%s/nodes/%s/storage/%s/upload", c.session.ApiUrl, node, storage)
req, err := c.session.NewRequest(http.MethodPost, url, nil, body)
if err != nil {
return err
}
req.Header.Add("Content-Type", mimetype)
req.Header.Add("Accept", "application/json")
if doStreamingIO {
req.ContentLength = contentLength
}
resp, err := c.session.Do(req)
if err != nil {
return err
}
taskResponse, err := ResponseJSON(resp)
if err != nil {
return err
}
exitStatus, err := c.WaitForCompletion(taskResponse)
if err != nil {
return err
}
if exitStatus != exitStatusSuccess {
return fmt.Errorf("moving file to destination failed: %v", exitStatus)
}
return nil
}
func createUploadBody(contentType string, filename string, r io.Reader) (io.Reader, string, error) {
var buf bytes.Buffer
w := multipart.NewWriter(&buf)
err := w.WriteField("content", contentType)
if err != nil {
return nil, "", err
}
fw, err := w.CreateFormFile("filename", filename)
if err != nil {
return nil, "", err
}
_, err = io.Copy(fw, r)
if err != nil {
return nil, "", err
}
err = w.Close()
if err != nil {
return nil, "", err
}
return &buf, w.FormDataContentType(), nil
}
// createStreamedUploadBody - Use MultiReader to create the multipart body from the file reader,
// avoiding allocation of large files in memory before upload (useful e.g. for Windows ISOs).
func createStreamedUploadBody(contentType string, filename string, fileSize int64, r io.Reader) (io.Reader, string, int64, error) {
var buf bytes.Buffer
w := multipart.NewWriter(&buf)
err := w.WriteField("content", contentType)
if err != nil {
return nil, "", 0, err
}
_, err = w.CreateFormFile("filename", filename)
if err != nil {
return nil, "", 0, err
}
headerSize := buf.Len()
err = w.Close()
if err != nil {
return nil, "", 0, err
}
mr := io.MultiReader(bytes.NewReader(buf.Bytes()[:headerSize]),
r,
bytes.NewReader(buf.Bytes()[headerSize:]))
contentLength := int64(buf.Len()) + fileSize
return mr, w.FormDataContentType(), contentLength, nil
}
// getStorageAndVolumeName - Extract disk storage and disk volume, since disk name is saved
// in Proxmox with its storage.
func getStorageAndVolumeName(
fullDiskName string,
separator string,
) (storageName string, diskName string) {
storageAndVolumeName := strings.Split(fullDiskName, separator)
storageName, volumeName := storageAndVolumeName[0], storageAndVolumeName[1]
// when disk type is dir, volumeName is `file=local:100/vm-100-disk-0.raw`
re := regexp.MustCompile(`\d+/(?P<filename>\S+.\S+)`)
match := re.FindStringSubmatch(volumeName)
if len(match) == 2 {
volumeName = match[1]
}
return storageName, volumeName
}
func (c *Client) UpdateVMPool(vmr *VmRef, pool string) (exitStatus interface{}, err error) {
// Same pool
if vmr.pool == pool {
return
}
// Remove from old pool
if vmr.pool != "" {
paramMap := map[string]interface{}{
"vms": vmr.vmId,
"delete": 1,
}
reqbody := ParamsToBody(paramMap)
url := fmt.Sprintf("/pools/%s", vmr.pool)
resp, err := c.session.Put(url, nil, nil, &reqbody)
if err == nil {
taskResponse, err := ResponseJSON(resp)
if err != nil {
return nil, err
}
exitStatus, err = c.WaitForCompletion(taskResponse)
if err != nil {
return nil, err
}
}
}
// Add to the new pool
if pool != "" {
paramMap := map[string]interface{}{
"vms": vmr.vmId,
}
reqbody := ParamsToBody(paramMap)
url := fmt.Sprintf("/pools/%s", pool)
resp, err := c.session.Put(url, nil, nil, &reqbody)
if err == nil {
taskResponse, err := ResponseJSON(resp)
if err != nil {
return nil, err
}
exitStatus, err = c.WaitForCompletion(taskResponse)
} else {
return nil, err
}
}
return
}
func (c *Client) ReadVMHA(vmr *VmRef) (err error) {
var list map[string]interface{}
url := fmt.Sprintf("/cluster/ha/resources/%d", vmr.vmId)
err = c.GetJsonRetryable(url, &list, 3)
if err == nil {
list = list["data"].(map[string]interface{})
for elem, value := range list {
if elem == "group" {
vmr.haGroup = value.(string)
}
if elem == "state" {
vmr.haState = value.(string)
}
}
}
return
}
func (c *Client) UpdateVMHA(vmr *VmRef, haState string, haGroup string) (exitStatus interface{}, err error) {
// Same hastate & hagroup
if vmr.haState == haState && vmr.haGroup == haGroup {
return
}
//Remove HA
if haState == "" {
url := fmt.Sprintf("/cluster/ha/resources/%d", vmr.vmId)
resp, err := c.session.Delete(url, nil, nil)
if err == nil {
taskResponse, err := ResponseJSON(resp)
if err != nil {
return nil, err
}
exitStatus, err = c.WaitForCompletion(taskResponse)
}
return nil, err
}
//Activate HA
if vmr.haState == "" {
paramMap := map[string]interface{}{
"sid": vmr.vmId,
}
if haGroup != "" {
paramMap["group"] = haGroup
}
reqbody := ParamsToBody(paramMap)
resp, err := c.session.Post("/cluster/ha/resources", nil, nil, &reqbody)
if err == nil {
taskResponse, err := ResponseJSON(resp)
if err != nil {
return nil, err
}
exitStatus, err = c.WaitForCompletion(taskResponse)
if err != nil {
return nil, err
}
}
}
//Set wanted state
paramMap := map[string]interface{}{
"state": haState,
"group": haGroup,
}
reqbody := ParamsToBody(paramMap)
url := fmt.Sprintf("/cluster/ha/resources/%d", vmr.vmId)
resp, err := c.session.Put(url, nil, nil, &reqbody)
if err == nil {
taskResponse, err := ResponseJSON(resp)
if err != nil {
return nil, err
}
exitStatus, err = c.WaitForCompletion(taskResponse)
}
return
}
func (c *Client) GetPoolList() (pools map[string]interface{}, err error) {
resp, err := c.session.Get("/pools", nil, nil)
if err != nil {
return nil, err
}
return ResponseJSON(resp)
}
func (c *Client) GetPoolInfo(poolid string) (poolInfo map[string]interface{}, err error) {
url := fmt.Sprintf("/pools/%s", poolid)
resp, err := c.session.Get(url, nil, nil)
if err != nil {
return nil, err
}
return ResponseJSON(resp)
}
func (c *Client) CreatePool(poolid string, comment string) error {
paramMap := map[string]interface{}{
"poolid": poolid,
"comment": comment,
}
reqbody := ParamsToBody(paramMap)
_, err := c.session.Post("/pools", nil, nil, &reqbody)
if err != nil {
return err
}
return nil
}
func (c *Client) UpdatePoolComment(poolid string, comment string) error {
paramMap := map[string]interface{}{
"poolid": poolid,
"comment": comment,
}
reqbody := ParamsToBodyWithEmpty(paramMap, []string{"comment"})
url := fmt.Sprintf("/pools/%s", poolid)
_, err := c.session.Put(url, nil, nil, &reqbody)
if err != nil {
return err
}
return nil
}
func (c *Client) DeletePool(poolid string) error {
url := fmt.Sprintf("/pools/%s", poolid)
_, err := c.session.Delete(url, nil, nil)
if err != nil {
return err
}
return nil
}
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/albert1231/proxmox-api-go.git
git@gitee.com:albert1231/proxmox-api-go.git
albert1231
proxmox-api-go
proxmox-api-go
0ecebf2e5b8b

搜索帮助