diff --git a/api/qemu_type.go b/api/qemu_type.go index 1ad73b7..f44ed4a 100644 --- a/api/qemu_type.go +++ b/api/qemu_type.go @@ -242,3 +242,40 @@ type VirtualMachineConfig struct { // Cloud-init interfaces // IPConfig } + +type VirtualMachineRebootOption struct { + TimeOut int `json:"timeout,omitempty"` +} + +type VirtualMachineResumeOption struct { + NoCheck bool `json:"nocheck,omitempty"` + SkipLock bool `json:"skiplock,omitempty"` +} + +type VirtualMachineStartOption struct { + // override qemu's -cpu argument with the given string + ForceCPU string `json:"force-cpu,omitempty"` + // specifies the qemu machine type + Machine string `json:"machine,omitempty"` + // cluster node name + MigratedFroom string `json:"migratedfrom,omitempty"` + // cidr of (sub) network that is used for migration + MigrationNetwork string `json:"migration_network,omitempty"` + // migration traffic is ecrypted using an SSH tunnel by default. + // On secure, completely private networks this can be disabled to increase performance. + MigrationType string `json:"migration_type,omitempty"` + SkipLock bool `json:"skiplock,omitempty"` + // some command save/restore state from this location + StateURI string `json:"stateuri,omitempty"` + // Mapping from source to target storages. Providing only a single storage ID maps all source storages to that storage. + // Providing the special value '1' will map each source storage to itself. + TargetStoraage string `json:"targetstorage,omitempty"` + TimeOut int `json:"timeout,omitempty"` +} + +type VirtualMachineStopOption struct { + KeepActive bool `json:"keepActive,omitempty"` + MigratedFrom string `json:"migratedfrom,omitempty"` + SkipLock bool `json:"skiplock,omitempty"` + TimeOut int `json:"timeout,omitempty"` +} diff --git a/proxmox/qemu.go b/proxmox/qemu.go index 8a51c0b..dd32c85 100644 --- a/proxmox/qemu.go +++ b/proxmox/qemu.go @@ -152,28 +152,28 @@ func (c *VirtualMachine) ResizeVolume(ctx context.Context, disk, size string) er return nil } -func (c *VirtualMachine) Start(ctx context.Context) error { +func (c *VirtualMachine) Start(ctx context.Context, option api.VirtualMachineStartOption) 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 { + if err := c.restclient.Post(ctx, path, option, &upid); err != nil { return err } return c.service.EnsureTaskDone(ctx, c.Node, upid) } -func (c *VirtualMachine) Stop(ctx context.Context) error { +func (c *VirtualMachine) Stop(ctx context.Context, option api.VirtualMachineStopOption) 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 { + if err := c.restclient.Post(ctx, path, option, &upid); err != nil { return err } return c.service.EnsureTaskDone(ctx, c.Node, upid) } -func (c *VirtualMachine) Resume(ctx context.Context) error { +func (c *VirtualMachine) Resume(ctx context.Context, option api.VirtualMachineResumeOption) 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 { + if err := c.restclient.Post(ctx, path, option, &upid); err != nil { return err } return c.service.EnsureTaskDone(ctx, c.Node, upid) diff --git a/proxmox/qemu_test.go b/proxmox/qemu_test.go new file mode 100644 index 0000000..c7059ae --- /dev/null +++ b/proxmox/qemu_test.go @@ -0,0 +1,44 @@ +package proxmox + +import ( + "context" + + "github.com/sp-yduck/proxmox-go/api" +) + +func (s *TestSuite) TestVirtualMachine() { + _, testVM := s.getTestVirtualMachine() + vm, err := s.service.VirtualMachine(context.TODO(), testVM.VM.VMID) + if err != nil { + s.T().Fatalf("failed to get vm(vmid=%d): %v", testVM.VM.VMID, err) + } + s.Assert().Equal(*vm, *testVM) +} + +func (s *TestSuite) TestStop() { + _, testVM := s.getTestVirtualMachine() + if err := testVM.Stop(context.TODO(), api.VirtualMachineStopOption{}); err != nil { + s.T().Fatalf("failed to stop vm: %v", err) + } +} + +func (s *TestSuite) getTestNode() *api.Node { + nodes, err := s.service.restclient.GetNodes(context.TODO()) + if err != nil { + s.T().Fatalf("failed to get nodes: %v", err) + } + return nodes[0] +} + +func (s *TestSuite) getTestVirtualMachine() (*api.Node, *VirtualMachine) { + node := s.getTestNode() + vms, err := s.service.restclient.GetVirtualMachines(context.TODO(), node.Node) + if err != nil { + s.T().Fatalf("failed to get vms: %v", err) + } + vm, err := s.service.VirtualMachine(context.TODO(), vms[0].VMID) + if err != nil { + s.T().Fatalf("failed to get vm: %v", err) + } + return node, vm +} diff --git a/proxmox/suite_test.go b/proxmox/suite_test.go new file mode 100644 index 0000000..718f62c --- /dev/null +++ b/proxmox/suite_test.go @@ -0,0 +1,41 @@ +package proxmox + +import ( + "os" + "testing" + + "github.com/stretchr/testify/suite" +) + +type TestSuite struct { + suite.Suite + service Service +} + +func (s *TestSuite) SetupSuite() { + url := os.Getenv("PROXMOX_URL") + user := os.Getenv("PROXMOX_USERNAME") + password := os.Getenv("PROXMOX_PASSWORD") + tokeid := os.Getenv("PROXMOX_TOKENID") + secret := os.Getenv("PROXMOX_SECRET") + if url == "" { + s.T().Fatal("url must not be empty") + } + + authConfig := AuthConfig{ + Username: user, + Password: password, + TokenID: tokeid, + Secret: secret, + } + + service, err := NewService(url, authConfig, true) + if err != nil { + s.T().Fatalf("failed to create new service: %v", err) + } + s.service = *service +} + +func TestSuiteIntegration(t *testing.T) { + suite.Run(t, new(TestSuite)) +} diff --git a/proxmox/task.go b/proxmox/task.go index 5cede07..cd6512d 100644 --- a/proxmox/task.go +++ b/proxmox/task.go @@ -16,9 +16,12 @@ const ( 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 + if err != nil { + if rest.IsNotFound(err) { + time.Sleep(time.Second * 1) + continue + } + return nil, err } return task, nil } diff --git a/proxmox/task_test.go b/proxmox/task_test.go new file mode 100644 index 0000000..147f4bb --- /dev/null +++ b/proxmox/task_test.go @@ -0,0 +1,33 @@ +package proxmox + +import ( + "context" + + "github.com/sp-yduck/proxmox-go/api" +) + +func (s *TestSuite) TestMustGetTask() { + testNode, testTask := s.getTestTask() + task, err := s.service.MustGetTask(context.TODO(), testNode.Node, testTask.UPID) + if err != nil { + s.T().Fatalf("failed to get task: %v", err) + } + s.Assert().Equal(*task, *testTask) +} + +func (s *TestSuite) TestEnsureTaskDone() { + testNode, testTask := s.getTestTask() + err := s.service.EnsureTaskDone(context.TODO(), testNode.Node, testTask.UPID) + if err != nil { + s.T().Fatalf("failed to get task: %v", err) + } +} + +func (s *TestSuite) getTestTask() (*api.Node, *api.Task) { + node := s.getTestNode() + tasks, err := s.service.restclient.GetTasks(context.TODO(), node.Node) + if err != nil { + s.T().Fatalf("failed to get tasks: %v", err) + } + return node, tasks[0] +} diff --git a/rest/errors.go b/rest/errors.go index 42c0d20..57d4673 100644 --- a/rest/errors.go +++ b/rest/errors.go @@ -22,9 +22,15 @@ func (e *Error) Error() string { } func IsNotFound(err error) bool { + if err == nil { + return false + } return err.Error() == NotFound } func IsNotAuthorized(err error) bool { + if err == nil { + return false + } return err.Error() == NotAuthorized }