CreateSnapshot creates a new snapshot for a VM or container.
(vm *VM, name string, options *SnapshotOptions)
| 65 | |
| 66 | // CreateSnapshot creates a new snapshot for a VM or container. |
| 67 | func (c *Client) CreateSnapshot(vm *VM, name string, options *SnapshotOptions) error { |
| 68 | path := fmt.Sprintf("/nodes/%s/%s/%d/snapshot", vm.Node, vm.Type, vm.ID) |
| 69 | |
| 70 | data := map[string]interface{}{ |
| 71 | "snapname": name, |
| 72 | } |
| 73 | |
| 74 | if options != nil { |
| 75 | if options.Description != "" { |
| 76 | data["description"] = options.Description |
| 77 | } |
| 78 | if options.VMState && vm.Type == VMTypeQemu { |
| 79 | data["vmstate"] = "1" |
| 80 | } |
| 81 | } |
| 82 | |
| 83 | c.logger.Info("Creating snapshot '%s' for %s %s (ID: %d)", name, vm.Type, vm.Name, vm.ID) |
| 84 | |
| 85 | // Use direct HTTP client to get raw response |
| 86 | var result map[string]interface{} |
| 87 | err := c.httpClient.Post(context.Background(), path, data, &result) |
| 88 | if err != nil { |
| 89 | c.logger.Debug("CreateSnapshot HTTP error: %v", err) |
| 90 | // Check if the error contains the response body |
| 91 | if strings.Contains(err.Error(), "failed to parse response JSON") { |
| 92 | // Extract the response body from the error message |
| 93 | // The error format is: "failed to parse response JSON: <response body>" |
| 94 | parts := strings.SplitN(err.Error(), "failed to parse response JSON: ", 2) |
| 95 | if len(parts) == 2 { |
| 96 | responseBody := parts[1] |
| 97 | c.logger.Debug("CreateSnapshot response body: %s", responseBody) |
| 98 | // Check if the response contains an error message |
| 99 | if strings.Contains(responseBody, "snapshot feature is not available") || |
| 100 | strings.Contains(responseBody, "error") || |
| 101 | strings.Contains(responseBody, "failed") { |
| 102 | errorMsg := fmt.Sprintf("snapshot creation failed: %s", strings.TrimSpace(responseBody)) |
| 103 | c.logger.Debug("CreateSnapshot returning error: %s", errorMsg) |
| 104 | return errors.New(errorMsg) |
| 105 | } |
| 106 | } |
| 107 | } |
| 108 | c.logger.Debug("CreateSnapshot returning original error: %v", err) |
| 109 | return err |
| 110 | } |
| 111 | |
| 112 | c.logger.Debug("CreateSnapshot successful HTTP response, checking result: %+v", result) |
| 113 | |
| 114 | // Check for API-level errors in the response |
| 115 | if errMsg, ok := result["error"].(string); ok && errMsg != "" { |
| 116 | c.logger.Debug("CreateSnapshot found error in result: %s", errMsg) |
| 117 | return fmt.Errorf("snapshot creation failed: %s", errMsg) |
| 118 | } |
| 119 | |
| 120 | // Check if the response contains a UPID (task ID) - this means the operation was queued |
| 121 | if upid, ok := result["data"].(string); ok && strings.HasPrefix(upid, "UPID:") { |
| 122 | c.logger.Debug("CreateSnapshot task queued with UPID: %s", upid) |
| 123 | // Poll for task completion |
| 124 | return c.waitForTaskCompletion(upid, "snapshot creation") |
nothing calls this directly
no test coverage detected