diff --git a/cli/command/commands/commands.go b/cli/command/commands/commands.go index 31e787aa..57bbfa18 100644 --- a/cli/command/commands/commands.go +++ b/cli/command/commands/commands.go @@ -6,6 +6,8 @@ import ( _ "github.com/Telmate/proxmox-api-go/cli/command/example" _ "github.com/Telmate/proxmox-api-go/cli/command/get" _ "github.com/Telmate/proxmox-api-go/cli/command/get/id" + _ "github.com/Telmate/proxmox-api-go/cli/command/guest" + _ "github.com/Telmate/proxmox-api-go/cli/command/guest/qemu" _ "github.com/Telmate/proxmox-api-go/cli/command/list" _ "github.com/Telmate/proxmox-api-go/cli/command/set" _ "github.com/Telmate/proxmox-api-go/cli/command/update" diff --git a/cli/command/guest/guest-shutdown.go b/cli/command/guest/guest-shutdown.go new file mode 100644 index 00000000..4eb880d3 --- /dev/null +++ b/cli/command/guest/guest-shutdown.go @@ -0,0 +1,25 @@ +package guest + +import ( + "github.com/Telmate/proxmox-api-go/cli" + "github.com/Telmate/proxmox-api-go/proxmox" + "github.com/spf13/cobra" +) + +var guest_shutdownCmd = &cobra.Command{ + Use: "shutdown GUESTID", + Short: "Shuts the speciefid guest down", + RunE: func(cmd *cobra.Command, args []string) (err error) { + vmr := proxmox.NewVmRef(cli.ValidateIntIDset(args, "GuestID")) + c := cli.NewClient() + _, err = c.ShutdownVm(vmr) + if err == nil { + cli.PrintGuestStatus(GuestCmd.OutOrStdout(), vmr.VmId(), "stopped") + } + return + }, +} + +func init() { + GuestCmd.AddCommand(guest_shutdownCmd) +} diff --git a/cli/command/guest/guest-start.go b/cli/command/guest/guest-start.go new file mode 100644 index 00000000..2a5ffc87 --- /dev/null +++ b/cli/command/guest/guest-start.go @@ -0,0 +1,25 @@ +package guest + +import ( + "github.com/Telmate/proxmox-api-go/cli" + "github.com/Telmate/proxmox-api-go/proxmox" + "github.com/spf13/cobra" +) + +var guest_statusCmd = &cobra.Command{ + Use: "start GUESTID", + Short: "Starts the speciefid guest", + RunE: func(cmd *cobra.Command, args []string) (err error) { + vmr := proxmox.NewVmRef(cli.ValidateIntIDset(args, "GuestID")) + c := cli.NewClient() + _, err = c.StartVm(vmr) + if err == nil { + cli.PrintGuestStatus(GuestCmd.OutOrStdout(), vmr.VmId(), "started") + } + return + }, +} + +func init() { + GuestCmd.AddCommand(guest_statusCmd) +} diff --git a/cli/command/guest/guest-status.go b/cli/command/guest/guest-status.go new file mode 100644 index 00000000..b5d7e524 --- /dev/null +++ b/cli/command/guest/guest-status.go @@ -0,0 +1,27 @@ +package guest + +import ( + "fmt" + + "github.com/Telmate/proxmox-api-go/cli" + "github.com/Telmate/proxmox-api-go/proxmox" + "github.com/spf13/cobra" +) + +var guest_startCmd = &cobra.Command{ + Use: "status GUESTID", + Short: "Gets the status of the speciefid guest", + RunE: func(cmd *cobra.Command, args []string) (err error) { + vmr := proxmox.NewVmRef(cli.ValidateIntIDset(args, "GuestID")) + c := cli.NewClient() + vmState, err := c.GetVmState(vmr) + if err == nil { + fmt.Fprintf(GuestCmd.OutOrStdout(), "Status of guest with id (%d) is %s\n", vmr.VmId(), vmState["status"].(string)) + } + return + }, +} + +func init() { + GuestCmd.AddCommand(guest_startCmd) +} diff --git a/cli/command/guest/guest-stop.go b/cli/command/guest/guest-stop.go new file mode 100644 index 00000000..296a88de --- /dev/null +++ b/cli/command/guest/guest-stop.go @@ -0,0 +1,25 @@ +package guest + +import ( + "github.com/Telmate/proxmox-api-go/cli" + "github.com/Telmate/proxmox-api-go/proxmox" + "github.com/spf13/cobra" +) + +var guest_stopCmd = &cobra.Command{ + Use: "stop GUESTID", + Short: "Stops the speciefid guest", + RunE: func(cmd *cobra.Command, args []string) (err error) { + vmr := proxmox.NewVmRef(cli.ValidateIntIDset(args, "GuestID")) + c := cli.NewClient() + _, err = c.StopVm(vmr) + if err == nil { + cli.PrintGuestStatus(GuestCmd.OutOrStdout(), vmr.VmId(), "stopped") + } + return + }, +} + +func init() { + GuestCmd.AddCommand(guest_stopCmd) +} diff --git a/cli/command/guest/guest-uptime.go b/cli/command/guest/guest-uptime.go new file mode 100644 index 00000000..acd9992c --- /dev/null +++ b/cli/command/guest/guest-uptime.go @@ -0,0 +1,27 @@ +package guest + +import ( + "fmt" + + "github.com/Telmate/proxmox-api-go/cli" + "github.com/Telmate/proxmox-api-go/proxmox" + "github.com/spf13/cobra" +) + +var guest_uptimeCmd = &cobra.Command{ + Use: "uptime GUESTID", + Short: "Gets the uptime of the speciefid guest", + RunE: func(cmd *cobra.Command, args []string) (err error) { + vmr := proxmox.NewVmRef(cli.ValidateIntIDset(args, "GuestID")) + c := cli.NewClient() + vmState, err := c.GetVmState(vmr) + if err == nil { + fmt.Fprintf(GuestCmd.OutOrStdout(), "Uptime of guest with id (%d) is %d\n", vmr.VmId(), int(vmState["uptime"].(float64))) + } + return + }, +} + +func init() { + GuestCmd.AddCommand(guest_uptimeCmd) +} diff --git a/cli/command/guest/guest.go b/cli/command/guest/guest.go new file mode 100644 index 00000000..a0680ed6 --- /dev/null +++ b/cli/command/guest/guest.go @@ -0,0 +1,15 @@ +package guest + +import ( + "github.com/Telmate/proxmox-api-go/cli" + "github.com/spf13/cobra" +) + +var GuestCmd = &cobra.Command{ + Use: "guest", + Short: "Commands to interact with the qemu and LXC guests on Proxmox", +} + +func init() { + cli.RootCmd.AddCommand(GuestCmd) +} diff --git a/cli/command/guest/qemu/guest-qemu-hibernate.go b/cli/command/guest/qemu/guest-qemu-hibernate.go new file mode 100644 index 00000000..95740b26 --- /dev/null +++ b/cli/command/guest/qemu/guest-qemu-hibernate.go @@ -0,0 +1,25 @@ +package qemu + +import ( + "github.com/Telmate/proxmox-api-go/cli" + "github.com/Telmate/proxmox-api-go/proxmox" + "github.com/spf13/cobra" +) + +var qemu_hibernateCmd = &cobra.Command{ + Use: "hibernate GUESTID", + Short: "Hibernates the speciefid guest", + RunE: func(cmd *cobra.Command, args []string) (err error) { + vmr := proxmox.NewVmRef(cli.ValidateIntIDset(args, "GuestID")) + c := cli.NewClient() + _, err = c.HibernateVm(vmr) + if err == nil { + cli.PrintGuestStatus(qemuCmd.OutOrStdout(), vmr.VmId(), "suspended to disk") + } + return + }, +} + +func init() { + qemuCmd.AddCommand(qemu_hibernateCmd) +} diff --git a/cli/command/guest/qemu/guest-qemu-pause.go b/cli/command/guest/qemu/guest-qemu-pause.go new file mode 100644 index 00000000..7d0c42bf --- /dev/null +++ b/cli/command/guest/qemu/guest-qemu-pause.go @@ -0,0 +1,25 @@ +package qemu + +import ( + "github.com/Telmate/proxmox-api-go/cli" + "github.com/Telmate/proxmox-api-go/proxmox" + "github.com/spf13/cobra" +) + +var qemu_pauseCmd = &cobra.Command{ + Use: "pause GUESTID", + Short: "Pauses the speciefid guest", + RunE: func(cmd *cobra.Command, args []string) (err error) { + vmr := proxmox.NewVmRef(cli.ValidateIntIDset(args, "GuestID")) + c := cli.NewClient() + _, err = c.PauseVm(vmr) + if err == nil { + cli.PrintGuestStatus(qemuCmd.OutOrStdout(), vmr.VmId(), "paused") + } + return + }, +} + +func init() { + qemuCmd.AddCommand(qemu_pauseCmd) +} diff --git a/cli/command/guest/qemu/guest-qemu-reset.go b/cli/command/guest/qemu/guest-qemu-reset.go new file mode 100644 index 00000000..192cd5b3 --- /dev/null +++ b/cli/command/guest/qemu/guest-qemu-reset.go @@ -0,0 +1,25 @@ +package qemu + +import ( + "github.com/Telmate/proxmox-api-go/cli" + "github.com/Telmate/proxmox-api-go/proxmox" + "github.com/spf13/cobra" +) + +var qemu_resetCmd = &cobra.Command{ + Use: "reset GUESTID", + Short: "Resets the speciefid guest", + RunE: func(cmd *cobra.Command, args []string) (err error) { + vmr := proxmox.NewVmRef(cli.ValidateIntIDset(args, "GuestID")) + c := cli.NewClient() + _, err = c.StartVm(vmr) + if err == nil { + cli.PrintGuestStatus(qemuCmd.OutOrStdout(), vmr.VmId(), "reset") + } + return + }, +} + +func init() { + qemuCmd.AddCommand(qemu_resetCmd) +} diff --git a/cli/command/guest/qemu/guest-qemu-resume.go b/cli/command/guest/qemu/guest-qemu-resume.go new file mode 100644 index 00000000..5bca51ff --- /dev/null +++ b/cli/command/guest/qemu/guest-qemu-resume.go @@ -0,0 +1,25 @@ +package qemu + +import ( + "github.com/Telmate/proxmox-api-go/cli" + "github.com/Telmate/proxmox-api-go/proxmox" + "github.com/spf13/cobra" +) + +var qemu_resumeCmd = &cobra.Command{ + Use: "resume GUESTID", + Short: "Resumes the speciefid guest", + RunE: func(cmd *cobra.Command, args []string) (err error) { + vmr := proxmox.NewVmRef(cli.ValidateIntIDset(args, "GuestID")) + c := cli.NewClient() + _, err = c.ResumeVm(vmr) + if err == nil { + cli.PrintGuestStatus(qemuCmd.OutOrStdout(), vmr.VmId(), "resumed") + } + return + }, +} + +func init() { + qemuCmd.AddCommand(qemu_resumeCmd) +} diff --git a/cli/command/guest/qemu/guest-qemu.go b/cli/command/guest/qemu/guest-qemu.go new file mode 100644 index 00000000..dafc2779 --- /dev/null +++ b/cli/command/guest/qemu/guest-qemu.go @@ -0,0 +1,15 @@ +package qemu + +import ( + "github.com/Telmate/proxmox-api-go/cli/command/guest" + "github.com/spf13/cobra" +) + +var qemuCmd = &cobra.Command{ + Use: "qemu", + Short: "Commands to interact with the qemu guests on Proxmox", +} + +func init() { + guest.GuestCmd.AddCommand(qemuCmd) +} diff --git a/cli/print.go b/cli/print.go index 8f61d8af..10646df1 100644 --- a/cli/print.go +++ b/cli/print.go @@ -1,35 +1,39 @@ package cli import ( + "encoding/json" "fmt" "io" - "encoding/json" ) -func PrintItemCreated(out io.Writer, id, text string){ +func PrintGuestStatus(out io.Writer, id int, text string) { + fmt.Fprintf(out, "Guest with id (%d) has been %s\n", id, text) +} + +func PrintItemCreated(out io.Writer, id, text string) { fmt.Fprintf(out, "%s (%s) has been created\n", text, id) } -func PrintItemUpdated(out io.Writer, id, text string){ +func PrintItemUpdated(out io.Writer, id, text string) { fmt.Fprintf(out, "%s (%s) has been updated\n", text, id) } -func PrintItemDeleted(out io.Writer, id, text string){ +func PrintItemDeleted(out io.Writer, id, text string) { fmt.Fprintf(out, "%s (%s) has been deleted\n", text, id) } -func PrintItemSet(out io.Writer, id, text string){ +func PrintItemSet(out io.Writer, id, text string) { fmt.Fprintf(out, "%s (%s) has been configured\n", text, id) } -func PrintRawJson(out io.Writer, input interface{}){ +func PrintRawJson(out io.Writer, input interface{}) { list, err := json.Marshal(input) LogFatalError(err) - fmt.Fprintln(out,string(list)) + fmt.Fprintln(out, string(list)) } -func PrintFormattedJson(out io.Writer, input interface{}){ +func PrintFormattedJson(out io.Writer, input interface{}) { list, err := json.MarshalIndent(input, "", " ") LogFatalError(err) - fmt.Fprintln(out,string(list)) -} \ No newline at end of file + fmt.Fprintln(out, string(list)) +} diff --git a/proxmox/client.go b/proxmox/client.go index 2af1ad58..0ab75580 100644 --- a/proxmox/client.go +++ b/proxmox/client.go @@ -238,17 +238,7 @@ func (c *Client) GetVmState(vmr *VmRef) (vmState map[string]interface{}, err err if err != nil { return nil, err } - var data map[string]interface{} - url := fmt.Sprintf("/nodes/%s/%s/%d/status/current", vmr.node, vmr.vmType, vmr.vmId) - err = c.GetJsonRetryable(url, &data, 3) - if err != nil { - return nil, err - } - if data["data"] == nil { - return nil, fmt.Errorf("vm STATE not readable") - } - vmState = data["data"].(map[string]interface{}) - return + return c.GetItemConfigMapStringInterface(fmt.Sprintf("/nodes/%s/%s/%d/status/current", vmr.node, vmr.vmType, vmr.vmId), "vm", "STATE") } func (c *Client) GetVmConfig(vmr *VmRef) (vmConfig map[string]interface{}, err error) { @@ -465,18 +455,15 @@ func (c *Client) GetTaskExitstatus(taskUpid string) (exitStatus interface{}, err return } -func (c *Client) StatusChangeVm(vmr *VmRef, setStatus string) (exitStatus string, err error) { +func (c *Client) StatusChangeVm(vmr *VmRef, params map[string]interface{}, setStatus string) (exitStatus string, err error) { err = c.CheckVmRef(vmr) if err != nil { - return "", err + return } - url := fmt.Sprintf("/nodes/%s/%s/%d/status/%s", vmr.node, vmr.vmType, vmr.vmId, setStatus) - var taskResponse map[string]interface{} for i := 0; i < 3; i++ { - _, err = c.session.PostJSON(url, nil, nil, nil, &taskResponse) - exitStatus, err = c.WaitForCompletion(taskResponse) - if exitStatus == "" { + exitStatus, err = c.CreateItemWithTask(params, url) + if err != nil { time.Sleep(TaskStatusCheckInterval * time.Second) } else { return @@ -486,27 +473,34 @@ func (c *Client) StatusChangeVm(vmr *VmRef, setStatus string) (exitStatus string } func (c *Client) StartVm(vmr *VmRef) (exitStatus string, err error) { - return c.StatusChangeVm(vmr, "start") + return c.StatusChangeVm(vmr, nil, "start") } func (c *Client) StopVm(vmr *VmRef) (exitStatus string, err error) { - return c.StatusChangeVm(vmr, "stop") + return c.StatusChangeVm(vmr, nil, "stop") } func (c *Client) ShutdownVm(vmr *VmRef) (exitStatus string, err error) { - return c.StatusChangeVm(vmr, "shutdown") + return c.StatusChangeVm(vmr, nil, "shutdown") } func (c *Client) ResetVm(vmr *VmRef) (exitStatus string, err error) { - return c.StatusChangeVm(vmr, "reset") + return c.StatusChangeVm(vmr, nil, "reset") } -func (c *Client) SuspendVm(vmr *VmRef) (exitStatus string, err error) { - return c.StatusChangeVm(vmr, "suspend") +func (c *Client) PauseVm(vmr *VmRef) (exitStatus string, err error) { + return c.StatusChangeVm(vmr, nil, "suspend") +} + +func (c *Client) HibernateVm(vmr *VmRef) (exitStatus string, err error) { + params := map[string]interface{}{ + "todisk": true, + } + return c.StatusChangeVm(vmr, params, "suspend") } func (c *Client) ResumeVm(vmr *VmRef) (exitStatus string, err error) { - return c.StatusChangeVm(vmr, "resume") + return c.StatusChangeVm(vmr, nil, "resume") } func (c *Client) DeleteVm(vmr *VmRef) (exitStatus string, err error) { @@ -1517,7 +1511,7 @@ func (c *Client) GetPoolList() (pools map[string]interface{}, err error) { func (c *Client) GetPoolInfo(poolid string) (poolInfo map[string]interface{}, err error) { url := fmt.Sprintf("/pools/%s", poolid) - return c.GetItemConfigMapStringInterface(url, "pool") + return c.GetItemConfigMapStringInterface(url, "pool", "CONFIG") } func (c *Client) CreatePool(poolid string, comment string) error { @@ -1544,7 +1538,7 @@ func (c *Client) DeletePool(poolid string) error { //User func (c *Client) GetUserConfig(id string) (config map[string]interface{}, err error) { - return c.GetItemConfigMapStringInterface("/access/users/"+id, "user") + return c.GetItemConfigMapStringInterface("/access/users/"+id, "user", "CONFIG") } func (c *Client) GetUserList() (users map[string]interface{}, err error) { @@ -1595,7 +1589,7 @@ func (c *Client) DeleteUser(id string) (err error) { //ACME func (c *Client) GetAcmeDirectoriesUrl() (url []string, err error) { - config, err := c.GetItemConfigInterfaceArray("/cluster/acme/directories", "Acme directories") + config, err := c.GetItemConfigInterfaceArray("/cluster/acme/directories", "Acme directories", "CONFIG") url = make([]string, len(config)) for i, element := range config { url[i] = element.(map[string]interface{})["url"].(string) @@ -1604,7 +1598,7 @@ func (c *Client) GetAcmeDirectoriesUrl() (url []string, err error) { } func (c *Client) GetAcmeTosUrl() (url string, err error) { - return c.GetItemConfigString("/cluster/acme/tos", "Acme T.O.S.") + return c.GetItemConfigString("/cluster/acme/tos", "Acme T.O.S.", "CONFIG") } //ACME Account @@ -1613,7 +1607,7 @@ func (c *Client) GetAcmeAccountList() (accounts map[string]interface{}, err erro } func (c *Client) GetAcmeAccountConfig(id string) (config map[string]interface{}, err error) { - return c.GetItemConfigMapStringInterface("/cluster/acme/account/"+id, "acme") + return c.GetItemConfigMapStringInterface("/cluster/acme/account/"+id, "acme", "CONFIG") } func (c *Client) CreateAcmeAccount(params map[string]interface{}) (exitStatus string, err error) { @@ -1638,7 +1632,7 @@ func (c *Client) GetAcmePluginList() (accounts map[string]interface{}, err error } func (c *Client) GetAcmePluginConfig(id string) (config map[string]interface{}, err error) { - return c.GetItemConfigMapStringInterface("/cluster/acme/plugins/"+id, "acme plugin") + return c.GetItemConfigMapStringInterface("/cluster/acme/plugins/"+id, "acme plugin", "CONFIG") } func (c *Client) CreateAcmePlugin(params map[string]interface{}) error { @@ -1661,7 +1655,7 @@ func (c *Client) DeleteAcmePlugin(id string) (err error) { //Metrics func (c *Client) GetMetricServerConfig(id string) (config map[string]interface{}, err error) { - return c.GetItemConfigMapStringInterface("/cluster/metrics/server/"+id, "metrics server") + return c.GetItemConfigMapStringInterface("/cluster/metrics/server/"+id, "metrics server", "CONFIG") } func (c *Client) GetMetricsServerList() (metricServers map[string]interface{}, err error) { @@ -1699,7 +1693,7 @@ func (c *Client) GetStorageList() (metricServers map[string]interface{}, err err } func (c *Client) GetStorageConfig(id string) (config map[string]interface{}, err error) { - return c.GetItemConfigMapStringInterface("/storage/"+id, "storage") + return c.GetItemConfigMapStringInterface("/storage/"+id, "storage", "CONFIG") } func (c *Client) CreateStorage(id string, params map[string]interface{}) error { @@ -1721,37 +1715,37 @@ func (c *Client) DeleteStorage(id string) error { } //Shared -func (c *Client) GetItemConfigMapStringInterface(url, text string) (map[string]interface{}, error) { - data, err := c.GetItemConfig(url, text) +func (c *Client) GetItemConfigMapStringInterface(url, text, message string) (map[string]interface{}, error) { + data, err := c.GetItemConfig(url, text, message) if err != nil { return nil, err } return data["data"].(map[string]interface{}), err } -func (c *Client) GetItemConfigString(url, text string) (string, error) { - data, err := c.GetItemConfig(url, text) +func (c *Client) GetItemConfigString(url, text, message string) (string, error) { + data, err := c.GetItemConfig(url, text, message) if err != nil { return "", err } return data["data"].(string), err } -func (c *Client) GetItemConfigInterfaceArray(url, text string) ([]interface{}, error) { - data, err := c.GetItemConfig(url, text) +func (c *Client) GetItemConfigInterfaceArray(url, text, message string) ([]interface{}, error) { + data, err := c.GetItemConfig(url, text, message) if err != nil { return nil, err } return data["data"].([]interface{}), err } -func (c *Client) GetItemConfig(url, text string) (config map[string]interface{}, err error) { +func (c *Client) GetItemConfig(url, text, message string) (config map[string]interface{}, err error) { err = c.GetJsonRetryable(url, &config, 3) if err != nil { return nil, err } if config["data"] == nil { - return nil, fmt.Errorf(text + " CONFIG not readable") + return nil, fmt.Errorf(text + " " + message + " not readable") } return }