diff --git a/proxmox/qemu.go b/proxmox/qemu.go index 4c496bd..8a51c0b 100644 --- a/proxmox/qemu.go +++ b/proxmox/qemu.go @@ -12,6 +12,7 @@ import ( ) type VirtualMachine struct { + service *Service restclient *rest.RESTClient Node string VM *api.VirtualMachine @@ -52,11 +53,22 @@ func (s *Service) VirtualMachine(ctx context.Context, vmid int) (*VirtualMachine } return nil, err } - return &VirtualMachine{restclient: s.restclient, VM: vm, Node: node.Node}, nil + return &VirtualMachine{service: s, restclient: s.restclient, VM: vm, Node: node.Node}, nil } return nil, rest.NotFoundErr } +func (s *Service) CreateVirtualMachine(ctx context.Context, node string, vmid int, option api.VirtualMachineCreateOptions) (*VirtualMachine, error) { + taskid, err := s.restclient.CreateVirtualMachine(ctx, node, vmid, option) + if err != nil { + return nil, err + } + if err := s.EnsureTaskDone(ctx, node, *taskid); err != nil { + return nil, err + } + return s.VirtualMachine(ctx, vmid) +} + func (s *Service) VirtualMachineFromUUID(ctx context.Context, uuid string) (*VirtualMachine, error) { nodes, err := s.Nodes(ctx) if err != nil { @@ -72,19 +84,19 @@ func (s *Service) VirtualMachineFromUUID(ctx context.Context, uuid string) (*Vir if err != nil { return nil, err } - vmuuid, err := convertSMBiosToUUID(config.SMBios1) + vmuuid, err := ConvertSMBiosToUUID(config.SMBios1) if err != nil { return nil, err } if vmuuid == uuid { - return &VirtualMachine{restclient: s.restclient, VM: vm, Node: node.Node, config: config}, nil + return &VirtualMachine{service: s, restclient: s.restclient, VM: vm, Node: node.Node, config: config}, nil } } } return nil, rest.NotFoundErr } -func convertSMBiosToUUID(smbios string) (string, error) { +func ConvertSMBiosToUUID(smbios string) (string, error) { re := regexp.MustCompile(fmt.Sprintf("uuid=%s", UUIDFormat)) match := re.FindString(smbios) if match == "" { @@ -94,6 +106,15 @@ func convertSMBiosToUUID(smbios string) (string, error) { return strings.Split(match, "=")[1], nil } +func (c *VirtualMachine) Delete(ctx context.Context) error { + path := fmt.Sprintf("/nodes/%s/qemu/%d", c.Node, c.VM.VMID) + var upid string + if err := c.restclient.Delete(ctx, path, nil, &upid); err != nil { + return err + } + return c.service.EnsureTaskDone(ctx, c.Node, upid) +} + func (c *VirtualMachine) GetConfig(ctx context.Context) (*api.VirtualMachineConfig, error) { if c.config != nil { return c.config, nil @@ -114,3 +135,46 @@ func (c *VirtualMachine) GetOSInfo(ctx context.Context) (*api.OSInfo, error) { } return osInfo, nil } + +// size : The new size. With the `+` sign the value is added to the actual size of the volume +// and without it, the value is taken as an absolute one. +// Shrinking disk size is not supported. +// size format : \+?\d+(\.\d+)?[KMGT]?j +func (c *VirtualMachine) ResizeVolume(ctx context.Context, disk, size string) error { + path := fmt.Sprintf("/nodes/%s/qemu/%d/resize", c.Node, c.VM.VMID) + request := make(map[string]interface{}) + request["disk"] = disk + request["size"] = size + var v interface{} + if err := c.restclient.Put(ctx, path, request, &v); err != nil { + return err + } + return nil +} + +func (c *VirtualMachine) Start(ctx context.Context) error { + path := fmt.Sprintf("/nodes/%s/qemu/%d/status/start", c.Node, c.VM.VMID) + var upid string + if err := c.restclient.Post(ctx, path, nil, &upid); err != nil { + return err + } + return c.service.EnsureTaskDone(ctx, c.Node, upid) +} + +func (c *VirtualMachine) Stop(ctx context.Context) error { + path := fmt.Sprintf("/nodes/%s/qemu/%d/status/stop", c.Node, c.VM.VMID) + var upid string + if err := c.restclient.Post(ctx, path, nil, &upid); err != nil { + return err + } + return c.service.EnsureTaskDone(ctx, c.Node, upid) +} + +func (c *VirtualMachine) Resume(ctx context.Context) error { + path := fmt.Sprintf("/nodes/%s/qemu/%d/status/resume", c.Node, c.VM.VMID) + var upid string + if err := c.restclient.Post(ctx, path, nil, &upid); err != nil { + return err + } + return c.service.EnsureTaskDone(ctx, c.Node, upid) +} diff --git a/proxmox/storage.go b/proxmox/storage.go index 6d708c1..31d6b4b 100644 --- a/proxmox/storage.go +++ b/proxmox/storage.go @@ -61,3 +61,22 @@ func (s *Storage) GetContent(ctx context.Context, volumeID string) (*api.Storage } return nil, rest.NotFoundErr } + +func (s *Storage) GetVolume(ctx context.Context, volumeID string) (*api.StorageVolume, error) { + path := fmt.Sprintf("/nodes/%s/storage/%s/content/%s", s.Node, s.Storage.Storage, volumeID) + var volume *api.StorageVolume + if err := s.restclient.Get(ctx, path, &volume); err != nil { + return nil, err + } + return volume, nil +} + +// to do : taskid +func (s *Storage) DeleteVolume(ctx context.Context, volumeID string) error { + path := fmt.Sprintf("/nodes/%s/storage/%s/content/%s", s.Node, s.Storage.Storage, volumeID) + var taskid string + if err := s.restclient.Delete(ctx, path, nil, &taskid); err != nil { + return err + } + return nil +} diff --git a/proxmox/task.go b/proxmox/task.go new file mode 100644 index 0000000..5cede07 --- /dev/null +++ b/proxmox/task.go @@ -0,0 +1,37 @@ +package proxmox + +import ( + "context" + "errors" + "time" + + "github.com/sp-yduck/proxmox-go/api" + "github.com/sp-yduck/proxmox-go/rest" +) + +const ( + TaskStatusOK = "OK" +) + +func (s *Service) MustGetTask(ctx context.Context, node string, upid string) (*api.Task, error) { + for i := 0; i < 10; i++ { + task, err := s.restclient.GetTask(ctx, node, upid) + if rest.IsNotFound(err) { + time.Sleep(time.Second * 1) + continue + } + return task, nil + } + return nil, errors.New("task wait deadline exceeded") +} + +func (s *Service) EnsureTaskDone(ctx context.Context, node, upid string) error { + task, err := s.MustGetTask(ctx, node, upid) + if err != nil { + return err + } + if task.Status != TaskStatusOK { + return errors.New(task.Status) + } + return nil +}