Add option to list storage
This commit is contained in:
@@ -160,18 +160,20 @@ proxmox-cloud-image -batch batch.txt
|
|||||||
| `-image-url` | - | URL cloud image (required) |
|
| `-image-url` | - | URL cloud image (required) |
|
||||||
| `-vm-name` | cloud-vm | Nama template |
|
| `-vm-name` | cloud-vm | Nama template |
|
||||||
| `-vm-id` | 0 | Template ID (0 = auto-find dari 10000+) |
|
| `-vm-id` | 0 | Template ID (0 = auto-find dari 10000+) |
|
||||||
| `-storage` | local-lvm | Nama storage Proxmox |
|
| `-storage` | auto-detect | Nama storage Proxmox (auto-detect jika kosong) |
|
||||||
| `-memory` | 2048 | Memory dalam MB |
|
| `-memory` | 2048 | Memory dalam MB |
|
||||||
| `-cores` | 2 | Jumlah CPU cores |
|
| `-cores` | 2 | Jumlah CPU cores |
|
||||||
| `-disk-size` | 20G | Ukuran disk |
|
| `-disk-size` | 20G | Ukuran disk |
|
||||||
| `-bridge` | vmbr0 | Network bridge |
|
| `-bridge` | vmbr0 | Network bridge |
|
||||||
| `-vlan-tag` | 0 | VLAN tag (0 = no VLAN) |
|
| `-vlan-tag` | 0 | VLAN tag (0 = no VLAN) |
|
||||||
|
| `-guest-agent` | false | Enable QEMU guest agent |
|
||||||
|
| `-firewall` | false | Enable firewall |
|
||||||
| `-ssh-key` | - | Path ke SSH public key |
|
| `-ssh-key` | - | Path ke SSH public key |
|
||||||
| `-proxmox-host` | - | IP/hostname Proxmox (required) |
|
| `-proxmox-host` | - | IP/hostname Proxmox (required) |
|
||||||
| `-proxmox-user` | root@pam | Proxmox user |
|
| `-proxmox-user` | root@pam | Proxmox user |
|
||||||
| `-proxmox-pass` | - | Proxmox password |
|
| `-proxmox-pass` | - | Proxmox password |
|
||||||
| `-guest-agent` | false | Enable QEMU Guest Agent |
|
| `-list-storage` | - | List semua storage yang tersedia |
|
||||||
| `-firewall` | false | Enable Proxmox firewall |
|
| `-ls` | - | Shorthand untuk `-list-storage` |
|
||||||
|
|
||||||
## How It Works
|
## How It Works
|
||||||
|
|
||||||
|
|||||||
2
go.mod
2
go.mod
@@ -2,4 +2,4 @@ module github.com/othman/proxmox-cloud-image
|
|||||||
|
|
||||||
go 1.22.2
|
go 1.22.2
|
||||||
|
|
||||||
require gopkg.in/yaml.v3 v3.0.1 // indirect
|
require gopkg.in/yaml.v3 v3.0.1
|
||||||
|
|||||||
1
go.sum
1
go.sum
@@ -1,3 +1,4 @@
|
|||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
|||||||
22
main.go
22
main.go
@@ -28,13 +28,16 @@ func main() {
|
|||||||
config := &Config{}
|
config := &Config{}
|
||||||
var configFile string
|
var configFile string
|
||||||
var batchFile string
|
var batchFile string
|
||||||
|
var listStorage bool
|
||||||
|
|
||||||
flag.StringVar(&configFile, "config", "", "Config file path (YAML)")
|
flag.StringVar(&configFile, "config", "", "Config file path (YAML)")
|
||||||
flag.StringVar(&batchFile, "batch", "", "Batch file with multiple config paths (one per line)")
|
flag.StringVar(&batchFile, "batch", "", "Batch file with multiple config paths (one per line)")
|
||||||
|
flag.BoolVar(&listStorage, "list-storage", false, "List available storage on Proxmox host")
|
||||||
|
flag.BoolVar(&listStorage, "ls", false, "List available storage on Proxmox host (shorthand)")
|
||||||
flag.StringVar(&config.ImageURL, "image-url", "", "Cloud image URL to download")
|
flag.StringVar(&config.ImageURL, "image-url", "", "Cloud image URL to download")
|
||||||
flag.StringVar(&config.VMName, "vm-name", "cloud-vm", "VM name")
|
flag.StringVar(&config.VMName, "vm-name", "cloud-vm", "VM name")
|
||||||
flag.IntVar(&config.VMID, "vm-id", 0, "VM ID (0 = auto-find from 10000+)")
|
flag.IntVar(&config.VMID, "vm-id", 0, "VM ID (0 = auto-find from 10000+)")
|
||||||
flag.StringVar(&config.Storage, "storage", "local-lvm", "Proxmox storage name")
|
flag.StringVar(&config.Storage, "storage", "", "Proxmox storage name (auto-detect if empty)")
|
||||||
flag.IntVar(&config.Memory, "memory", 2048, "Memory in MB")
|
flag.IntVar(&config.Memory, "memory", 2048, "Memory in MB")
|
||||||
flag.IntVar(&config.Cores, "cores", 2, "CPU cores")
|
flag.IntVar(&config.Cores, "cores", 2, "CPU cores")
|
||||||
flag.StringVar(&config.DiskSize, "disk-size", "20G", "Disk size (e.g., 20G)")
|
flag.StringVar(&config.DiskSize, "disk-size", "20G", "Disk size (e.g., 20G)")
|
||||||
@@ -44,9 +47,24 @@ func main() {
|
|||||||
flag.StringVar(&config.ProxmoxHost, "proxmox-host", "", "Proxmox host (e.g., 192.168.1.100)")
|
flag.StringVar(&config.ProxmoxHost, "proxmox-host", "", "Proxmox host (e.g., 192.168.1.100)")
|
||||||
flag.StringVar(&config.ProxmoxUser, "proxmox-user", "root@pam", "Proxmox user")
|
flag.StringVar(&config.ProxmoxUser, "proxmox-user", "root@pam", "Proxmox user")
|
||||||
flag.StringVar(&config.ProxmoxPass, "proxmox-pass", "", "Proxmox password")
|
flag.StringVar(&config.ProxmoxPass, "proxmox-pass", "", "Proxmox password")
|
||||||
|
flag.BoolVar(&config.GuestAgent, "guest-agent", false, "Enable QEMU guest agent")
|
||||||
|
flag.BoolVar(&config.Firewall, "firewall", false, "Enable firewall")
|
||||||
|
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
|
// Handle storage listing
|
||||||
|
if listStorage || flag.Lookup("ls").Value.(flag.Getter).Get().(bool) {
|
||||||
|
if config.ProxmoxHost == "" {
|
||||||
|
fmt.Println("Error: -proxmox-host is required for storage listing")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
if err := listAvailableStorage(config); err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "Error listing storage: %v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Batch mode
|
// Batch mode
|
||||||
if batchFile != "" {
|
if batchFile != "" {
|
||||||
if err := runBatch(batchFile); err != nil {
|
if err := runBatch(batchFile); err != nil {
|
||||||
@@ -56,8 +74,6 @@ func main() {
|
|||||||
fmt.Println("\nAll templates created successfully!")
|
fmt.Println("\nAll templates created successfully!")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Single config mode
|
|
||||||
if configFile != "" {
|
if configFile != "" {
|
||||||
if err := loadConfigFile(configFile, config); err != nil {
|
if err := loadConfigFile(configFile, config); err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "Error loading config file: %v\n", err)
|
fmt.Fprintf(os.Stderr, "Error loading config file: %v\n", err)
|
||||||
|
|||||||
61
proxmox.go
61
proxmox.go
@@ -2,6 +2,7 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
@@ -236,3 +237,63 @@ func createProxmoxVM(config *Config) error {
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func listAvailableStorage(config *Config) error {
|
||||||
|
fmt.Printf("Detecting available storage on %s...\n", config.ProxmoxHost)
|
||||||
|
|
||||||
|
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("pvesm", "status", "--output-format", "json")
|
||||||
|
var stdout bytes.Buffer
|
||||||
|
cmd.Stdout = &stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
|
||||||
|
if err := cmd.Run(); err != nil {
|
||||||
|
return fmt.Errorf("failed to get storage status: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse JSON output
|
||||||
|
var storageList []map[string]interface{}
|
||||||
|
if err := json.Unmarshal(stdout.Bytes(), &storageList); err != nil {
|
||||||
|
return fmt.Errorf("failed to parse storage status: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("\nAvailable storage:")
|
||||||
|
fmt.Println("================")
|
||||||
|
|
||||||
|
found := false
|
||||||
|
for _, storage := range storageList {
|
||||||
|
nameVal, ok := storage["storage"]
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
name, _ := nameVal.(string)
|
||||||
|
|
||||||
|
typeVal, _ := storage["type"]
|
||||||
|
typeStr, _ := typeVal.(string)
|
||||||
|
|
||||||
|
activeVal, _ := storage["active"]
|
||||||
|
activeFloat, _ := activeVal.(float64)
|
||||||
|
|
||||||
|
if activeFloat == 1 {
|
||||||
|
fmt.Printf("- %s (%s) - ACTIVE\n", name, typeStr)
|
||||||
|
found = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
fmt.Println("No active storage found!")
|
||||||
|
fmt.Println("\nManual check:")
|
||||||
|
fmt.Println(" ssh root@your-proxmox 'pvesm status'")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user