023 Project 23: Proxmox Batch Operations CLI
023 Build a Proxmox Batch CLI
Operate many VMs in one command (start/stop/reboot) using Proxmox API token auth.
parse vmid list -> concurrent API calls -> result summary
Full main.go
package main
import (
"crypto/tls"
"flag"
"fmt"
"net/http"
"os"
"strconv"
"strings"
"sync"
"time"
)
func action(base, token, node string, vmid int, op string) error {
hc := &http.Client{Timeout: 8 * time.Second, Transport: &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}}
url := fmt.Sprintf("%s/api2/json/nodes/%s/qemu/%d/status/%s", base, node, vmid, op)
req, _ := http.NewRequest(http.MethodPost, url, nil)
req.Header.Set("Authorization", "PVEAPIToken="+token)
resp, err := hc.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode >= 300 {
return fmt.Errorf("status %d", resp.StatusCode)
}
return nil
}
func main() {
base := flag.String("base", os.Getenv("PVE_BASE_URL"), "proxmox base url")
token := flag.String("token", os.Getenv("PVE_TOKEN"), "api token")
node := flag.String("node", "pve", "node name")
op := flag.String("op", "start", "start|stop|reboot")
ids := flag.String("ids", "", "comma list vmids")
workers := flag.Int("w", 8, "parallel workers")
flag.Parse()
if *base == "" || *token == "" || *ids == "" {
fmt.Println("need -base, -token, -ids")
os.Exit(2)
}
jobs := make(chan int)
var wg sync.WaitGroup
for i := 0; i < *workers; i++ {
wg.Add(1)
go func() {
defer wg.Done()
for id := range jobs {
err := action(*base, *token, *node, id, *op)
if err != nil {
fmt.Printf("vm %d fail: %v\n", id, err)
} else {
fmt.Printf("vm %d ok\n", id)
}
}
}()
}
for _, s := range strings.Split(*ids, ",") {
id, err := strconv.Atoi(strings.TrimSpace(s))
if err == nil {
jobs <- id
}
}
close(jobs)
wg.Wait()
}Run
go run . -node pve -op start -ids 101,102,103Step-by-Step Explanation
- Model jobs, workers, and outputs explicitly.
- Bound concurrency using worker pools and buffered channels.
- Use
sync.WaitGroupfor lifecycle control. - Aggregate worker results in one place.
- Verify behavior under both normal and failure paths.
Code Anatomy
- Producer pushes jobs into a channel.
- Workers consume jobs and emit results.
- Aggregator merges results and prints summary.
Learning Goals
- Build leak-free goroutine patterns.
- Balance throughput and resource limits.
- Understand fan-out/fan-in architecture.