From fbc1faa839d075558c65432f882af536625350e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alp=20Eren=20=C3=87elik?= <80721144+alperencelik@users.noreply.github.com> Date: Thu, 7 Mar 2024 10:24:52 +0300 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8feat:=20Enhance=20support=20for=20cont?= =?UTF-8?q?ainers=20(#126)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * ✨feat: Enhance support for containers * fix: Remove test belongs to VirtualMachines --- containers.go | 187 +++++++++++++++++++++++++++- containers_test.go | 249 +++++++++++++++++++++++++++++++++++++ nodes.go | 23 ++++ tests/mocks/pve7x/nodes.go | 140 +++++++++++++++++++++ types.go | 41 ++++++ 5 files changed, 635 insertions(+), 5 deletions(-) create mode 100644 containers_test.go diff --git a/containers.go b/containers.go index c5b9aa7..d53757f 100644 --- a/containers.go +++ b/containers.go @@ -52,24 +52,28 @@ func (c *Container) Start(ctx context.Context) (status string, err error) { return status, c.client.Post(ctx, fmt.Sprintf("/nodes/%s/lxc/%d/status/start", c.Node, c.VMID), nil, &status) } -func (c *Container) Stop(ctx context.Context) (status *ContainerStatus, err error) { +func (c *Container) Stop(ctx context.Context) (status string, err error) { return status, c.client.Post(ctx, fmt.Sprintf("/nodes/%s/lxc/%d/status/stop", c.Node, c.VMID), nil, &status) } -func (c *Container) Suspend(ctx context.Context) (status *ContainerStatus, err error) { +func (c *Container) Suspend(ctx context.Context) (status string, err error) { return status, c.client.Post(ctx, fmt.Sprintf("/nodes/%s/lxc/%d/status/suspend", c.Node, c.VMID), nil, &status) } -func (c *Container) Reboot(ctx context.Context) (status *ContainerStatus, err error) { +func (c *Container) Reboot(ctx context.Context) (status string, err error) { return status, c.client.Post(ctx, fmt.Sprintf("/nodes/%s/lxc/%d/status/reboot", c.Node, c.VMID), nil, &status) } -func (c *Container) Resume(ctx context.Context) (status *ContainerStatus, err error) { +func (c *Container) Resume(ctx context.Context) (status string, err error) { return status, c.client.Post(ctx, fmt.Sprintf("/nodes/%s/lxc/%d/status/resume", c.Node, c.VMID), nil, &status) } +func (c *Container) Shutdown(ctx context.Context, force bool, timeout int) (status string, err error) { + return status, c.client.Post(ctx, fmt.Sprintf("/nodes/%s/lxc/%d/status/shutdown", c.Node, c.VMID), map[string]interface{}{"force": force, "timeout": timeout}, &status) +} + func (c *Container) TermProxy(ctx context.Context) (vnc *VNC, err error) { - return vnc, c.client.Post(ctx, fmt.Sprintf("/nodes/%s/lxk/%d/termproxy", c.Node, c.VMID), nil, &vnc) + return vnc, c.client.Post(ctx, fmt.Sprintf("/nodes/%s/lxc/%d/termproxy", c.Node, c.VMID), nil, &vnc) } func (c *Container) VNCWebSocket(vnc *VNC) (chan string, chan string, chan error, func() error, error) { @@ -78,3 +82,176 @@ func (c *Container) VNCWebSocket(vnc *VNC) (chan string, chan string, chan error return c.client.VNCWebSocket(p, vnc) } + +func (c *Container) Feature(ctx context.Context) (hasFeature bool, err error) { + var feature struct { + HasFeature bool `json:"hasFeature"` + } + err = c.client.Get(ctx, fmt.Sprintf("/nodes/%s/lxc/%d/feature", c.Node, c.VMID), &feature) + return feature.HasFeature, err +} + +// This seems broken on the proxmox side: https://pve.proxmox.com/pve-docs/api-viewer/index.html#/nodes/{node}/lxc/{vmid}/interfaces +// I wasn't able to make it work with the API. Tested on {"release":"7.3","repoid":"c3928077","version":"7.3-3"} +// func (c *Container) Interfaces(ctx context.Context) (interfaces []string, err error) { +// err = c.client.Get(ctx, fmt.Sprintf("/nodes/%s/lxc/%d/interfaces", c.Node, c.VMID), &interfaces) +// return interfaces, err +// } + +func (c *Container) Migrate(ctx context.Context, params *ContainerMigrateOptions) (task *Task, err error) { + var upid UPID + if err := c.client.Post(ctx, fmt.Sprintf("/nodes/%s/lxc/%d/migrate", c.Node, c.VMID), params, &upid); err != nil { + return nil, err + } + return NewTask(upid, c.client), nil +} + +func (c *Container) Resize(ctx context.Context, disk, size string) (task *Task, err error) { + var upid UPID + if err := c.client.Post(ctx, fmt.Sprintf("/nodes/%s/lxc/%d/resize", c.Node, c.VMID), map[string]interface{}{"disk": disk, "size": size}, &upid); err != nil { + return nil, err + } + return NewTask(upid, c.client), nil +} + +func (c *Container) MoveVolume(ctx context.Context, params *VirtualMachineMoveDiskOptions) (task *Task, err error) { + var upid UPID + if err := c.client.Post(ctx, fmt.Sprintf("/nodes/%s/lxc/%d/move_volume", c.Node, c.VMID), params, &upid); err != nil { + return nil, err + } + return NewTask(upid, c.client), nil +} + +func (c *Container) RRDData(ctx context.Context, timeframe Timeframe, consolidationFunction ConsolidationFunction) (rrddata []*RRDData, err error) { + u := url.URL{Path: fmt.Sprintf("/lxc/%s/qemu/%d/rrddata", c.Node, c.VMID)} + + // consolidation functions are variadic because they're optional, putting everything into one string and sending that + params := url.Values{} + if len(consolidationFunction) > 0 { + + f := "" + for _, cf := range consolidationFunction { + f = f + string(cf) + } + params.Add("cf", f) + } + + params.Add("timeframe", string(timeframe)) + u.RawQuery = params.Encode() + + err = c.client.Get(ctx, u.String(), &rrddata) + return +} + +func (c *Container) Template(ctx context.Context) error { + return c.client.Post(ctx, fmt.Sprintf("/nodes/%s/lxc/%d/template", c.Node, c.VMID), nil, nil) +} + +func (c *Container) VNCProxy(ctx context.Context, vncOptions VNCProxyOptions) (vnc *VNC, err error) { + return vnc, c.client.Post(ctx, fmt.Sprintf("/nodes/%s/lxc/%d/vncproxy", c.Node, c.VMID), vncOptions, &vnc) +} + +func (c *Container) Snapshots(ctx context.Context) (snapshots []*ContainerSnapshot, err error) { + err = c.client.Get(ctx, fmt.Sprintf("/nodes/%s/lxc/%d/snapshot", c.Node, c.VMID), &snapshots) + return +} + +func (c *Container) NewSnapshot(ctx context.Context, snapName string) (task *Task, err error) { + var upid UPID + if err := c.client.Post(ctx, fmt.Sprintf("/nodes/%s/lxc/%d/snapshot", c.Node, c.VMID), map[string]interface{}{"snapname": snapName}, &upid); err != nil { + return nil, err + } + return NewTask(upid, c.client), nil +} + +func (c *Container) GetSnapshot(ctx context.Context, snapshot string) (snap []*ContainerSnapshot, err error) { + return snap, c.client.Get(ctx, fmt.Sprintf("/nodes/%s/lxc/%d/snapshot/%s", c.Node, c.VMID, snapshot), &snap) +} + +func (c *Container) DeleteSnapshot(ctx context.Context, snapshot string) (task *Task, err error) { + var upid UPID + if err := c.client.Delete(ctx, fmt.Sprintf("/nodes/%s/lxc/%d/snapshot/%s", c.Node, c.VMID, snapshot), &upid); err != nil { + return nil, err + } + return NewTask(upid, c.client), nil +} + +func (c *Container) RollbackSnapshot(ctx context.Context, snapshot string, start bool) (task *Task, err error) { + var upid UPID + if err := c.client.Post(ctx, fmt.Sprintf("/nodes/%s/lxc/%d/snapshot/%s/rollback", c.Node, c.VMID, snapshot), map[string]interface{}{"start": start}, &upid); err != nil { + return nil, err + } + return NewTask(upid, c.client), nil +} + +func (c *Container) GetSnapshotConfig(ctx context.Context, snapshot string) (config map[string]interface{}, err error) { + return config, c.client.Get(ctx, fmt.Sprintf("/nodes/%s/lxc/%d/snapshot/%s/config", c.Node, c.VMID, snapshot), &config) +} + +func (c *Container) UpdateSnapshot(ctx context.Context, snapshot string) error { + return c.client.Put(ctx, fmt.Sprintf("/nodes/%s/lxc/%d/snapshot/%s/config", c.Node, c.VMID, snapshot), nil, nil) +} + +func (c *Container) Firewall(ctx context.Context) (firewall *Firewall, err error) { + return firewall, c.client.Get(ctx, fmt.Sprintf("/nodes/%s/lxc/%d/firewall", c.Node, c.VMID), &firewall) +} + +func (c *Container) GetFirewallAliases(ctx context.Context) (aliases []*FirewallAlias, err error) { + return aliases, c.client.Get(ctx, fmt.Sprintf("/nodes/%s/lxc/%d/firewall/aliases", c.Node, c.VMID), &aliases) +} + +func (c *Container) NewFirewallAlias(ctx context.Context, alias *FirewallAlias) error { + return c.client.Post(ctx, fmt.Sprintf("/nodes/%s/lxc/%d/firewall/aliases", c.Node, c.VMID), alias, nil) +} + +func (c *Container) GetFirewallAlias(ctx context.Context, name string) (alias *FirewallAlias, err error) { + return alias, c.client.Get(ctx, fmt.Sprintf("/nodes/%s/lxc/%d/firewall/aliases/%s", c.Node, c.VMID, name), &alias) +} + +func (c *Container) UpdateFirewallAlias(ctx context.Context, name string, alias *FirewallAlias) error { + return c.client.Put(ctx, fmt.Sprintf("/nodes/%s/lxc/%d/firewall/aliases/%s", c.Node, c.VMID, name), alias, nil) +} + +func (c *Container) DeleteFirewallAlias(ctx context.Context, name string) error { + return c.client.Delete(ctx, fmt.Sprintf("/nodes/%s/lxc/%d/firewall/aliases/%s", c.Node, c.VMID, name), nil) +} + +func (c *Container) GetFirewallIPSet(ctx context.Context) (ipsets []*FirewallIPSet, err error) { + return ipsets, c.client.Get(ctx, fmt.Sprintf("/nodes/%s/lxc/%d/firewall/ipset", c.Node, c.VMID), &ipsets) +} + +func (c *Container) NewFirewallIPSet(ctx context.Context, ipset *FirewallIPSet) error { + return c.client.Post(ctx, fmt.Sprintf("/nodes/%s/lxc/%d/firewall/ipset", c.Node, c.VMID), ipset, nil) +} + +func (c *Container) DeleteFirewallIPSet(ctx context.Context, name string, force bool) error { + return c.client.Delete(ctx, fmt.Sprintf("/nodes/%s/lxc/%d/firewall/ipset/%s", c.Node, c.VMID, name), map[string]interface{}{"force": force}) +} + +func (c *Container) FirewallRules(ctx context.Context) (rules []*FirewallRule, err error) { + return rules, c.client.Get(ctx, fmt.Sprintf("/nodes/%s/lxc/%d/firewall/rules", c.Node, c.VMID), &rules) +} + +func (c *Container) NewFirewallRule(ctx context.Context, rule *FirewallRule) error { + return c.client.Post(ctx, fmt.Sprintf("/nodes/%s/lxc/%d/firewall/rules", c.Node, c.VMID), rule, nil) +} + +func (c *Container) GetFirewallRule(ctx context.Context, rulePos int) (rule *FirewallRule, err error) { + return rule, c.client.Get(ctx, fmt.Sprintf("/nodes/%s/lxc/%d/firewall/rules/%d", c.Node, c.VMID, rulePos), &rule) +} + +func (c *Container) UpdateFirewallRule(ctx context.Context, rulePos int, rule *FirewallRule) error { + return c.client.Put(ctx, fmt.Sprintf("/nodes/%s/lxc/%d/firewall/rules/%d", c.Node, c.VMID, rulePos), rule, nil) +} + +func (c *Container) DeleteFirewallRule(ctx context.Context, rulePos int) error { + return c.client.Delete(ctx, fmt.Sprintf("/nodes/%s/lxc/%d/firewall/rules/%d", c.Node, c.VMID, rulePos), nil) +} + +func (c *Container) GetFirewallOptions(ctx context.Context) (options *FirewallVirtualMachineOption, err error) { + return options, c.client.Get(ctx, fmt.Sprintf("/nodes/%s/lxc/%d/firewall/options", c.Node, c.VMID), &options) +} + +func (c *Container) UpdateFirewallOptions(ctx context.Context, options *FirewallVirtualMachineOption) error { + return c.client.Put(ctx, fmt.Sprintf("/nodes/%s/lxc/%d/firewall/options", c.Node, c.VMID), options, nil) +} diff --git a/containers_test.go b/containers_test.go new file mode 100644 index 0000000..246cf53 --- /dev/null +++ b/containers_test.go @@ -0,0 +1,249 @@ +package proxmox + +import ( + "context" + "testing" + + "github.com/luthermonson/go-proxmox/tests/mocks" + "github.com/stretchr/testify/assert" +) + +func TestContainers(t *testing.T) { + mocks.On(mockConfig) + defer mocks.Off() + client := mockClient() + ctx := context.Background() + node := Node{ + client: client, + Name: "node1", + } + containers, err := node.Containers(ctx) + assert.Nil(t, err) + assert.Len(t, containers, 3) +} + +func TestContainerClone(t *testing.T) { + mocks.On(mockConfig) + defer mocks.Off() + client := mockClient() + ctx := context.Background() + container := Container{ + client: client, + Node: "node1", + VMID: 101, + } + cloneOptions := ContainerCloneOptions{ + NewID: 102, + } + _, _, err := container.Clone(ctx, &cloneOptions) + assert.Nil(t, err) + +} + +func TestContainerDelete(t *testing.T) { + mocks.On(mockConfig) + defer mocks.Off() + client := mockClient() + ctx := context.Background() + container := Container{ + client: client, + Node: "node1", + VMID: 101, + } + task, err := container.Delete(ctx) + assert.Nil(t, err) + assert.NotEmpty(t, task) +} + +func TestContainerConfig(t *testing.T) { + mocks.On(mockConfig) + defer mocks.Off() + client := mockClient() + ctx := context.Background() + container := Container{ + client: client, + Node: "node1", + VMID: 101, + } + _, err := container.Config(ctx) + assert.Nil(t, err) +} + +func TestContainerStart(t *testing.T) { + mocks.On(mockConfig) + defer mocks.Off() + client := mockClient() + ctx := context.Background() + container := Container{ + client: client, + Node: "node1", + VMID: 101, + } + task, err := container.Start(ctx) + assert.Nil(t, err) + assert.NotEmpty(t, task) +} + +func TestContainerStop(t *testing.T) { + mocks.On(mockConfig) + defer mocks.Off() + client := mockClient() + ctx := context.Background() + container := Container{ + client: client, + Node: "node1", + VMID: 101, + } + task, err := container.Stop(ctx) + assert.Nil(t, err) + assert.NotEmpty(t, task) +} + +func TestContainerSuspend(t *testing.T) { + mocks.On(mockConfig) + defer mocks.Off() + client := mockClient() + ctx := context.Background() + container := Container{ + client: client, + Node: "node1", + VMID: 101, + } + task, err := container.Suspend(ctx) + assert.Nil(t, err) + assert.NotEmpty(t, task) +} + +func TestContainerReboot(t *testing.T) { + mocks.On(mockConfig) + defer mocks.Off() + client := mockClient() + ctx := context.Background() + container := Container{ + client: client, + Node: "node1", + VMID: 101, + } + task, err := container.Reboot(ctx) + assert.Nil(t, err) + assert.NotEmpty(t, task) +} + +func TestContainerResume(t *testing.T) { + mocks.On(mockConfig) + defer mocks.Off() + client := mockClient() + ctx := context.Background() + container := Container{ + client: client, + Node: "node1", + VMID: 101, + } + task, err := container.Resume(ctx) + assert.Nil(t, err) + assert.NotEmpty(t, task) +} + +func TestContainerShutdown(t *testing.T) { + mocks.On(mockConfig) + defer mocks.Off() + client := mockClient() + ctx := context.Background() + container := Container{ + client: client, + Node: "node1", + VMID: 101, + } + task, err := container.Shutdown(ctx, false, 60) + assert.Nil(t, err) + assert.NotEmpty(t, task) +} + +func TestContainerTemplate(t *testing.T) { + mocks.On(mockConfig) + defer mocks.Off() + client := mockClient() + ctx := context.Background() + container := Container{ + client: client, + Node: "node1", + VMID: 101, + } + err := container.Template(ctx) + assert.Nil(t, err) +} + +func TestContainerSnapshots(t *testing.T) { + mocks.On(mockConfig) + defer mocks.Off() + client := mockClient() + ctx := context.Background() + container := Container{ + client: client, + Node: "node1", + VMID: 101, + } + snapshots, err := container.Snapshots(ctx) + assert.Nil(t, err) + assert.Len(t, snapshots, 3) +} + +func TestContainerNewSnapshot(t *testing.T) { + mocks.On(mockConfig) + defer mocks.Off() + client := mockClient() + ctx := context.Background() + container := Container{ + client: client, + Node: "node1", + VMID: 101, + } + task, err := container.NewSnapshot(ctx, "snapshot1") + assert.Nil(t, err) + assert.NotEmpty(t, task) +} + +func TestContainerGetSnapshot(t *testing.T) { + mocks.On(mockConfig) + defer mocks.Off() + client := mockClient() + ctx := context.Background() + container := Container{ + client: client, + Node: "node1", + VMID: 101, + } + snapshot, err := container.GetSnapshot(ctx, "snapshot1") + assert.Nil(t, err) + assert.NotNil(t, snapshot) +} + +func TestContainerDeleteSnapshot(t *testing.T) { + mocks.On(mockConfig) + defer mocks.Off() + client := mockClient() + ctx := context.Background() + container := Container{ + client: client, + Node: "node1", + VMID: 101, + } + task, err := container.DeleteSnapshot(ctx, "snapshot1") + assert.Nil(t, err) + assert.NotEmpty(t, task) +} + +func TestContainerRollbackSnapshot(t *testing.T) { + mocks.On(mockConfig) + defer mocks.Off() + client := mockClient() + ctx := context.Background() + container := Container{ + client: client, + Node: "node1", + VMID: 101, + } + task, err := container.RollbackSnapshot(ctx, "snapshot1", true) + assert.Nil(t, err) + assert.NotEmpty(t, task) +} diff --git a/nodes.go b/nodes.go index 8f5aaa7..8080ac9 100644 --- a/nodes.go +++ b/nodes.go @@ -104,6 +104,29 @@ func (n *Node) Container(ctx context.Context, vmid int) (*Container, error) { return &c, nil } +func (n *Node) NewContainer(ctx context.Context, vmid int, options ...ContainerOption) (*Task, error) { + var upid UPID + data := make(map[string]interface{}) + if vmid <= 0 { + cluster, err := n.client.Cluster(ctx) + if err != nil { + return nil, err + } + vmid, err = cluster.NextID(ctx) + if err != nil { + return nil, err + } + } + data["vmid"] = vmid + + for _, option := range options { + data[option.Name] = option.Value + } + + err := n.client.Post(ctx, fmt.Sprintf("/nodes/%s/lxc", n.Name), data, &upid) + return NewTask(upid, n.client), err +} + func (n *Node) Appliances(ctx context.Context) (appliances Appliances, err error) { err = n.client.Get(ctx, fmt.Sprintf("/nodes/%s/aplinfo", n.Name), &appliances) if err != nil { diff --git a/tests/mocks/pve7x/nodes.go b/tests/mocks/pve7x/nodes.go index db00b7a..6b97e64 100644 --- a/tests/mocks/pve7x/nodes.go +++ b/tests/mocks/pve7x/nodes.go @@ -888,4 +888,144 @@ func nodes() { ] }`) + // LXC + + gock.New(config.C.URI). + Get("^/nodes/node1/lxc"). + Reply(200). + JSON(`{ +"data": [{"cpu":0,"cpus":1,"disk":640397312,"diskread":273694720,"diskwrite":200982528,"maxdisk":8350298112,"maxmem":536870912,"maxswap":536870912,"mem":34304000,"name":"test","netin":94558593,"netout":1618542,"pid":248173,"status":"running","swap":0,"type":"lxc","uptime":919760,"vmid":"106"},{"cpu":0,"cpus":1,"disk":639303680,"diskread":283123712,"diskwrite":201687040,"maxdisk":8350298112,"maxmem":536870912,"maxswap":536870912,"mem":34508800,"name":"zort","netin":94560801,"netout":1619838,"pid":248045,"status":"running","swap":0,"type":"lxc","uptime":919761,"vmid":"105"},{"cpu":0,"cpus":1,"disk":0,"diskread":0,"diskwrite":0,"maxdisk":8589934592,"maxmem":536870912,"maxswap":536870912,"mem":0,"name":"test-container","netin":0,"netout":0,"status":"stopped","swap":0,"template":1,"type":"lxc","uptime":0,"vmid":"101"}] +}`) + + gock.New(config.C.URI). + Get("^/nodes/node1/lxc/101/status/current"). + Reply(200). + JSON(`{ + "data": { + "cpu":0, + "cpus":2, + "disk":0, + "diskread":0, + "diskwrite":0, + "ha":{"managed":0}, + "maxdisk":8589934592, + "maxmem":536870912, + "maxswap":536870912, + "mem":0, + "name":"test-container", + "netin":0, + "netout":0, + "status":"stopped", + "swap":0, + "template":1, + "type":"lxc", + "uptime":0, + "vmid":101 + } +}`) + + gock.New(config.C.URI). + Post("^/nodes/node1/lxc/101/clone"). + Reply(200). + JSON(`{ + "data": null +}`) + + gock.New(config.C.URI). + Delete("^/nodes/node1/lxc/101"). + Reply(200). + JSON(`{"data": "UPID:node1:0031B740:0645340C:23E5BA99:vzdestroy:101:root@pam:"}`) + + gock.New(config.C.URI). + Put("^/nodes/node1/lxc/101/config"). + Reply(200). + JSON(`{"data": "null"}`) + + gock.New(config.C.URI). + Post("^/nodes/node1/lxc/101/status/start"). + Reply(200). + JSON(`{"data": "UPID:node1:0031B740:0645340C:23E5BA99:vzstart:101:root"}`) + + gock.New(config.C.URI). + Post("^/nodes/node1/lxc/101/status/stop"). + Reply(200). + JSON(`{"data": "UPID:node1:0031B740:0645340C:23E5BA99:vzstop:101:root"}`) + + gock.New(config.C.URI). + Post("^/nodes/node1/lxc/101/status/suspend"). + Reply(200). + JSON(`{"data": "UPID:node1:0031B740:0645340C:23E5BA99:vzsuspend:101:root"}`) + + gock.New(config.C.URI). + Post("^/nodes/node1/lxc/101/status/reboot"). + Reply(200). + JSON(`{"data": "UPID:node1:0031B740:0645340C:23E5BA99:vzreboot:101:root"}`) + + gock.New(config.C.URI). + Post("^/nodes/node1/lxc/101/status/resume"). + Reply(200). + JSON(`{"data": "UPID:node1:0031B740:0645340C:23E5BA99:vzresume:101:root"}`) + + gock.New(config.C.URI). + Post("^/nodes/node1/lxc/101/status/shutdown"). + Reply(200). + JSON(`{"data": "UPID:node1:0031B740:0645340C:23E5BA99:vzshutdown:101:root"}`) + + gock.New(config.C.URI). + Post("^/nodes/node1/lxc/101/template"). + Reply(200). + JSON(`{"data": null}`) + + gock.New(config.C.URI). + Get("^/nodes/node1/lxc/101/snapshot"). + Reply(200). + JSON(`{ + "data": [ + { + "description": "description1", + "name":"snapshot1", + "snaptime":1709753281 + }, + { + "description":"description2", + "name":"snapshot2", + "parent":"parent1", + "snaptime":1709753290 + }, + { + "description":"You are here!", + "digest":"e2f5f35c85b2ca35e5f9ab789436b25c1d71cbad", + "name":"current", + "parent":"parent2", + "running":1 + } + ] +}`) + + gock.New(config.C.URI). + Post("^/nodes/node1/lxc/101/snapshot"). + Reply(200). + JSON(`{ + "data": "UPID:node1:0031B740:0645340C:23E5BA99:vzsnapshot:101:root" +}`) + + gock.New(config.C.URI). + Delete("^/nodes/node1/lxc/101/snapshot/snapshot1"). + Reply(200). + JSON(`{"data": "UPID:node1:0031B740:0645340C:23E5BA99:vzrmsnapshot:101:root"}`) + + gock.New(config.C.URI). + Get("^/nodes/node1/lxc/101/snapshot/snapshot1"). + Reply(200). + JSON(`{ + "data": "UPID:node1:0031B740:0645340C:23E5BA99:vzsnapshot:101:root" +}`) + + gock.New(config.C.URI). + Post("^/nodes/node1/lxc/101/snapshot/snapshot1/rollback"). + Reply(200). + JSON(`{ + "data": "UPID:node1:0031B740:0645340C:23E5BA99:vzrollback:101:root" +}`) + } diff --git a/types.go b/types.go index 53b7581..d68e772 100644 --- a/types.go +++ b/types.go @@ -546,6 +546,13 @@ type VirtualMachineMigrateOptions struct { WithLocalDisks IntOrBool `json:"with-local-disks,omitempty"` } +type ContainerMigrateOptions struct { + Target string `json:"target"` + BWLimit uint64 `json:"bwlimit,omitempty"` + Online IntOrBool `json:"online,omitempty"` + Restart IntOrBool `json:"restart,omitempty"` +} + type VirtualMachineCloneOptions struct { NewID int `json:"newid"` BWLimit uint64 `json:"bwlimit,omitempty"` @@ -1206,3 +1213,37 @@ type NewAPIToken struct { Info interface{} `json:"info,omitempty"` Value string `json:"value,omitempty"` } + +type VNCProxyOptions struct { + Websocket string `json:"websocket,omitempty"` + Height int `json:"height,omitempty"` + Width int `json:"width,omitempty"` +} + +type ContainerSnapshot struct { + Description string `json:"description,omitempty"` + Name string `json:"snapname,omitempty"` + Parent string `json:"parent,omitempty"` + SnapshotCreationTime int64 `json:"snaptime,omitempty"` +} + +type Firewall struct { + Aliases []*FirewallAlias `json:"aliases,omitempty"` + Ipset []*FirewallIPSet `json:"ipset,omitempty"` + Rules []*FirewallRule `json:"rules,omitempty"` + Options *FirewallNodeOption `json:"options,omitempty"` + // Refs map[string]string `json:"refs,omitempty"` +} + +type FirewallAlias struct { + Cidr string `json:"cidr,omitempty"` + Digest string `json:"digest,omitempty"` + Name string `json:"name,omitempty"` + Comment string `json:"comment,omitempty"` +} + +type FirewallIPSet struct { + Name string `json:"name,omitempty"` + Digest string `json:"digest,omitempty"` + Comment string `json:"comment,omitempty"` +}