package main import ( "flag" "fmt" "os" ) type FirewallRule struct { Type string `yaml:"type"` Action string `yaml:"action"` Protocol string `yaml:"protocol"` Dport string `yaml:"dport"` Sport string `yaml:"sport"` Source string `yaml:"source"` Dest string `yaml:"dest"` Comment string `yaml:"comment"` } type Config struct { ImageURL string `yaml:"image_url"` VMName string `yaml:"vm_name"` VMID int `yaml:"vm_id"` Storage string `yaml:"storage"` Memory int `yaml:"memory"` Cores int `yaml:"cores"` DiskSize string `yaml:"disk_size"` Bridge string `yaml:"bridge"` VlanTag int `yaml:"vlan_tag"` SSHKey string `yaml:"ssh_key"` ProxmoxHost string `yaml:"proxmox_host"` ProxmoxUser string `yaml:"proxmox_user"` ProxmoxPass string `yaml:"proxmox_pass"` GuestAgent bool `yaml:"guest_agent"` Firewall bool `yaml:"firewall"` FirewallRules []FirewallRule `yaml:"firewall_rules"` } func main() { config := &Config{} var configFile string var batchFile string var listStorage bool flag.StringVar(&configFile, "config", "", "Config file path (YAML)") 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 or local file path") 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.StringVar(&config.Storage, "storage", "", "Proxmox storage name (auto-detect if empty)") flag.IntVar(&config.Memory, "memory", 2048, "Memory in MB") flag.IntVar(&config.Cores, "cores", 2, "CPU cores") flag.StringVar(&config.DiskSize, "disk-size", "20G", "Disk size (e.g., 20G)") flag.StringVar(&config.Bridge, "bridge", "vmbr0", "Network bridge") flag.IntVar(&config.VlanTag, "vlan-tag", 0, "VLAN tag (optional, 0 = no VLAN)") flag.StringVar(&config.SSHKey, "ssh-key", "", "SSH public key file path") 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.ProxmoxPass, "proxmox-pass", "", "Proxmox password") flag.BoolVar(&config.GuestAgent, "guest-agent", true, "Enable QEMU guest agent") flag.BoolVar(&config.Firewall, "firewall", false, "Enable firewall") 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 if batchFile != "" { if err := runBatch(batchFile); err != nil { fmt.Fprintf(os.Stderr, "Batch error: %v\n", err) os.Exit(1) } fmt.Println("\nAll templates created successfully!") return } if configFile != "" { if err := loadConfigFile(configFile, config); err != nil { fmt.Fprintf(os.Stderr, "Error loading config file: %v\n", err) os.Exit(1) } } if config.ImageURL == "" { fmt.Println("Error: -image-url is required") flag.Usage() os.Exit(1) } if config.ProxmoxHost == "" { fmt.Println("Error: -proxmox-host is required") flag.Usage() os.Exit(1) } if err := run(config); err != nil { fmt.Fprintf(os.Stderr, "Error: %v\n", err) os.Exit(1) } fmt.Println("Template created successfully!") } func run(config *Config) error { if config.VMID == 0 { fmt.Println("Auto-finding available VM ID starting from 10000...") vmid, err := findAvailableVMID(config) if err != nil { return fmt.Errorf("failed to find available VM ID: %w", err) } config.VMID = vmid fmt.Printf("Found available VM ID: %d\n", vmid) } fmt.Printf("Creating template %s (ID: %d) on Proxmox host %s\n", config.VMName, config.VMID, config.ProxmoxHost) if err := downloadOrCopyImage(config); err != nil { return fmt.Errorf("failed to prepare image: %w", err) } if err := customizeImage(config); err != nil { return fmt.Errorf("failed to customize image: %w", err) } if err := createProxmoxVM(config); err != nil { return fmt.Errorf("failed to create Proxmox template: %w", err) } return nil }