package main import ( "context" "fmt" "os" "esxlib/api/cloud" "esxlib/pkg/pprint" "github.com/urfave/cli/v2" ) var ( vmIdFlag = cli.StringFlag{ Name: "vm.id", Value: "", Usage: "The id of the vm to fetch", } vmNameFlag = cli.StringFlag{ Name: "vm.name", Value: "", Required: true, Usage: "The id of the vm to fetch", } slugIdFlag = cli.StringFlag{ Name: "slug.id", Value: "", Required: true, Usage: "The id of the slug to use", } sshKeyFileFlag = cli.StringFlag{ Name: "ssh.hosts", Value: "", Usage: "SSH authorized host file to load keys from", } cloudInitFileFlag = cli.StringFlag{ Name: "cloudinit.file", Value: "", Usage: "CloudInit file to load", } ) var vmCommands = []*cli.Command{ { Name: "list", Aliases: []string{"ls", "list"}, Usage: "list virtual machines", Description: "list virtual machines", Flags: []cli.Flag{}, Action: vmCommandList, }, { Name: "get", Aliases: []string{"get"}, Usage: "get details of a virtual machine", Description: "get details of a virtual machines", Flags: []cli.Flag{ &vmIdFlag, }, Action: vmCommandGet, }, { Name: "power-on", Aliases: []string{"poweron", "on", "resume"}, Usage: "Power on/resume a Virtual machine", Description: "Power on/resume a Virtual machine", Flags: []cli.Flag{ &vmIdFlag, }, Action: vmCommandPowerOn, }, { Name: "power-off", Aliases: []string{"poweroff", "off"}, Usage: "Power off a Virtual machine", Description: "Power off a Virtual machine", Flags: []cli.Flag{ &vmIdFlag, }, Action: vmCommandPowerOff, }, { Name: "suspend", Aliases: []string{"suspend"}, Usage: "Suspend a Virtual machine", Description: "Suspend a Virtual machine", Flags: []cli.Flag{ &vmIdFlag, }, Action: vmCommandPowerSuspend, }, { Name: "destroy", Aliases: []string{"destroy"}, Usage: "Destroy an existing virtual machine", Description: "Destroy an existing virtual machine", Flags: []cli.Flag{ &vmIdFlag, }, Action: vmCommandDestroy, }, { Name: "create", Aliases: []string{"create"}, Usage: "Create a new virtual machine", Description: "Create a new virtual machine", Flags: []cli.Flag{ &vmNameFlag, &zoneIdFlag, &slugIdFlag, &sshKeyFileFlag, &cloudInitFileFlag, }, Action: vmCommandCreate, }, } func vmCommandList(ctx *cli.Context) error { client := mustGetClient(ctx) resp, err := client.VMList(context.Background(), &cloud.VMListRequest{}) if err != nil { return err } out := pprint.NewPrinter("human", os.Stdout) out.Headers("VMID", "Name", "State", "Address") for _, vm := range resp.Vms { address := "" // just grab the last one (we can do better later for _, a := range vm.Network { address = a.Address } out.Fields(vm.Id, vm.Name, vm.State, address) } out.Flush() return nil } func vmCommandGet(ctx *cli.Context) error { client := mustGetClient(ctx) resp, err := client.VMGet(context.Background(), &cloud.VMGetRequest{ Search: &cloud.VMGetRequest_Id{ Id: ctx.String(vmIdFlag.Name), }, }) if err != nil { return err } out := pprint.NewPrinter("human", os.Stdout) vm := resp.Vm out.Headers("VMID", "Name", "State", "Address") address := "" // just grab the last one (we can do better later for _, a := range vm.Network { address = a.Address } out.Fields(vm.Id, vm.Name, vm.State, address) out.Flush() return nil } func vmCommandPower(ctx *cli.Context, newState cloud.PowerState) error { client := mustGetClient(ctx) _, err := client.VMPower(context.Background(), &cloud.VMPowerRequest{ Id: ctx.String(vmIdFlag.Name), State: newState, }) if err != nil { return err } return nil } func vmCommandPowerOn(ctx *cli.Context) error { return vmCommandPower(ctx, cloud.PowerState_ON) } func vmCommandPowerOff(ctx *cli.Context) error { return vmCommandPower(ctx, cloud.PowerState_OFF) } func vmCommandPowerSuspend(ctx *cli.Context) error { return vmCommandPower(ctx, cloud.PowerState_SUSPENDED) } func vmCommandCreate(ctx *cli.Context) error { client := mustGetClient(ctx) sshAuthorizedHosts := "" if ctx.String(sshKeyFileFlag.Name) != "" { data, err := os.ReadFile(ctx.String(sshKeyFileFlag.Name)) if err != nil { return fmt.Errorf("Could not read hosts file: %s\n", ctx.String(sshKeyFileFlag.Name)) } sshAuthorizedHosts = string(data) } cloudInit := "" if ctx.String(cloudInitFileFlag.Name) != "" { data, err := os.ReadFile(ctx.String(cloudInitFileFlag.Name)) if err != nil { return fmt.Errorf("Could not read cloud init file: %s\n", ctx.String(cloudInitFileFlag.Name)) } cloudInit = string(data) } req := cloud.VMCreateRequest{ Name: ctx.String(vmNameFlag.Name), ZoneId: ctx.String(zoneIdFlag.Name), Slug: ctx.String(slugIdFlag.Name), InitScript: cloudInit, AuthorizedHosts: []string{sshAuthorizedHosts}, } resp, err := client.VMCreate(context.Background(), &req) if err != nil { return err } out := pprint.NewPrinter("human", os.Stdout) vm := resp.Vm out.Headers("VMID", "Name", "State", "Address") address := "" // just grab the last one (we can do better later for _, a := range vm.Network { address = a.Address } out.Fields(vm.Id, vm.Name, vm.State, address) out.Flush() return nil } func vmCommandDestroy(ctx *cli.Context) error { client := mustGetClient(ctx) _, err := client.VMDestroy(context.Background(), &cloud.VMDestroyRequest{ Id: ctx.String(vmIdFlag.Name), }) if err != nil { return err } return nil }