Initial commit: Proxmox cloud image template tool
This commit is contained in:
143
proxmox.go
Normal file
143
proxmox.go
Normal file
@@ -0,0 +1,143 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func findAvailableVMID(config *Config) (int, error) {
|
||||
sshCmd := func(args ...string) *exec.Cmd {
|
||||
fullArgs := []string{
|
||||
"-o", "StrictHostKeyChecking=no",
|
||||
"-o", "UserKnownHostsFile=/dev/null",
|
||||
fmt.Sprintf("%s@%s", strings.Split(config.ProxmoxUser, "@")[0], config.ProxmoxHost),
|
||||
}
|
||||
fullArgs = append(fullArgs, args...)
|
||||
return exec.Command("ssh", fullArgs...)
|
||||
}
|
||||
|
||||
cmd := sshCmd("qm", "list")
|
||||
var stdout bytes.Buffer
|
||||
cmd.Stdout = &stdout
|
||||
|
||||
if err := cmd.Run(); err != nil {
|
||||
return 0, fmt.Errorf("failed to list VMs: %w", err)
|
||||
}
|
||||
|
||||
usedIDs := make(map[int]bool)
|
||||
lines := strings.Split(stdout.String(), "\n")
|
||||
for _, line := range lines[1:] {
|
||||
fields := strings.Fields(line)
|
||||
if len(fields) > 0 {
|
||||
if vmid, err := strconv.Atoi(fields[0]); err == nil {
|
||||
usedIDs[vmid] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for vmid := 10000; vmid < 20000; vmid++ {
|
||||
if !usedIDs[vmid] {
|
||||
return vmid, nil
|
||||
}
|
||||
}
|
||||
|
||||
return 0, fmt.Errorf("no available VM ID found in range 10000-19999")
|
||||
}
|
||||
|
||||
func buildNetworkConfig(config *Config) string {
|
||||
netConfig := fmt.Sprintf("virtio,bridge=%s", config.Bridge)
|
||||
if config.VlanTag > 0 {
|
||||
netConfig += fmt.Sprintf(",tag=%d", config.VlanTag)
|
||||
}
|
||||
return netConfig
|
||||
}
|
||||
|
||||
func createProxmoxVM(config *Config) error {
|
||||
fmt.Println("Creating Proxmox template...")
|
||||
|
||||
sshCmd := func(args ...string) *exec.Cmd {
|
||||
fullArgs := []string{
|
||||
"-o", "StrictHostKeyChecking=no",
|
||||
"-o", "UserKnownHostsFile=/dev/null",
|
||||
fmt.Sprintf("%s@%s", strings.Split(config.ProxmoxUser, "@")[0], config.ProxmoxHost),
|
||||
}
|
||||
fullArgs = append(fullArgs, args...)
|
||||
return exec.Command("ssh", fullArgs...)
|
||||
}
|
||||
|
||||
scpCmd := func(src, dst string) *exec.Cmd {
|
||||
return exec.Command("scp",
|
||||
"-o", "StrictHostKeyChecking=no",
|
||||
"-o", "UserKnownHostsFile=/dev/null",
|
||||
src,
|
||||
fmt.Sprintf("%s@%s:%s", strings.Split(config.ProxmoxUser, "@")[0], config.ProxmoxHost, dst),
|
||||
)
|
||||
}
|
||||
|
||||
imageName := fmt.Sprintf("vm-%d-disk-0.qcow2", config.VMID)
|
||||
remotePath := fmt.Sprintf("/tmp/%s", imageName)
|
||||
|
||||
fmt.Println("Uploading image to Proxmox host...")
|
||||
cmd := scpCmd(config.ImageURL, remotePath)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
if err := cmd.Run(); err != nil {
|
||||
return fmt.Errorf("failed to upload image: %w", err)
|
||||
}
|
||||
|
||||
fmt.Println("Creating VM and converting to template...")
|
||||
commands := [][]string{
|
||||
{"qm", "create", fmt.Sprintf("%d", config.VMID),
|
||||
"--name", config.VMName,
|
||||
"--memory", fmt.Sprintf("%d", config.Memory),
|
||||
"--cores", fmt.Sprintf("%d", config.Cores),
|
||||
"--net0", buildNetworkConfig(config),
|
||||
},
|
||||
{"qm", "importdisk", fmt.Sprintf("%d", config.VMID), remotePath, config.Storage},
|
||||
{"qm", "set", fmt.Sprintf("%d", config.VMID),
|
||||
"--scsihw", "virtio-scsi-pci",
|
||||
"--scsi0", fmt.Sprintf("%s:vm-%d-disk-0", config.Storage, config.VMID),
|
||||
},
|
||||
{"qm", "set", fmt.Sprintf("%d", config.VMID),
|
||||
"--ide2", fmt.Sprintf("%s:cloudinit", config.Storage),
|
||||
},
|
||||
{"qm", "set", fmt.Sprintf("%d", config.VMID),
|
||||
"--boot", "c",
|
||||
"--bootdisk", "scsi0",
|
||||
},
|
||||
{"qm", "set", fmt.Sprintf("%d", config.VMID),
|
||||
"--serial0", "socket",
|
||||
"--vga", "serial0",
|
||||
},
|
||||
{"qm", "template", fmt.Sprintf("%d", config.VMID)},
|
||||
{"rm", "-f", remotePath},
|
||||
}
|
||||
|
||||
for _, cmdArgs := range commands {
|
||||
fmt.Printf("Running: %s\n", strings.Join(cmdArgs, " "))
|
||||
cmd := sshCmd(cmdArgs...)
|
||||
|
||||
var stdout, stderr bytes.Buffer
|
||||
cmd.Stdout = &stdout
|
||||
cmd.Stderr = &stderr
|
||||
|
||||
if err := cmd.Run(); err != nil {
|
||||
fmt.Println(stdout.String())
|
||||
fmt.Println(stderr.String())
|
||||
return fmt.Errorf("failed to execute command '%s': %w", strings.Join(cmdArgs, " "), err)
|
||||
}
|
||||
|
||||
if stdout.Len() > 0 {
|
||||
fmt.Println(stdout.String())
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Printf("\nTemplate %s (ID: %d) created successfully!\n", config.VMName, config.VMID)
|
||||
fmt.Printf("You can clone it with: qm clone %d <new-vm-id> --name <new-vm-name>\n", config.VMID)
|
||||
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user