feat: complete MVP epics E7-E10 (provisioning, console proxy, audit, frontend)
This commit is contained in:
@@ -228,6 +228,170 @@ func (c *Client) GetVMStatus(ctx context.Context, node string, vmid int) (VMStat
|
||||
return VMStatus{Status: body.Data.Status}, nil
|
||||
}
|
||||
|
||||
func (c *Client) CloneVM(ctx context.Context, node string, templateVMID int, newVMID int, name string) (string, error) {
|
||||
requestBody := map[string]any{
|
||||
"newid": newVMID,
|
||||
"name": name,
|
||||
}
|
||||
body, err := json.Marshal(requestBody)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
response, err := c.Post(ctx, fmt.Sprintf(
|
||||
"/nodes/%s/qemu/%d/clone",
|
||||
url.PathEscape(node),
|
||||
templateVMID,
|
||||
), body)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer response.Body.Close()
|
||||
|
||||
if response.StatusCode >= http.StatusBadRequest {
|
||||
_, _ = io.Copy(io.Discard, response.Body)
|
||||
return "", fmt.Errorf("proxmox returned %s", response.Status)
|
||||
}
|
||||
|
||||
return decodeUPID(response.Body)
|
||||
}
|
||||
|
||||
func (c *Client) ConfigureCloudInit(ctx context.Context, node string, vmid int, cfg CloudInitConfig) (string, error) {
|
||||
requestBody := map[string]any{
|
||||
"ciuser": cfg.CIUser,
|
||||
}
|
||||
if cfg.SSHKeys != "" {
|
||||
requestBody["sshkeys"] = cfg.SSHKeys
|
||||
}
|
||||
if cfg.IPConfig0 != "" {
|
||||
requestBody["ipconfig0"] = cfg.IPConfig0
|
||||
}
|
||||
if cfg.Hostname != "" {
|
||||
requestBody["searchdomain"] = cfg.Hostname
|
||||
}
|
||||
|
||||
body, err := json.Marshal(requestBody)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
response, err := c.Post(ctx, fmt.Sprintf(
|
||||
"/nodes/%s/qemu/%d/config",
|
||||
url.PathEscape(node),
|
||||
vmid,
|
||||
), body)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer response.Body.Close()
|
||||
|
||||
if response.StatusCode >= http.StatusBadRequest {
|
||||
_, _ = io.Copy(io.Discard, response.Body)
|
||||
return "", fmt.Errorf("proxmox returned %s", response.Status)
|
||||
}
|
||||
|
||||
return decodeUPID(response.Body)
|
||||
}
|
||||
|
||||
func (c *Client) StartVM(ctx context.Context, node string, vmid int) (string, error) {
|
||||
return c.PowerVM(ctx, node, vmid, "start")
|
||||
}
|
||||
|
||||
func (c *Client) StopVM(ctx context.Context, node string, vmid int) (string, error) {
|
||||
return c.PowerVM(ctx, node, vmid, "stop")
|
||||
}
|
||||
|
||||
func (c *Client) DeleteVM(ctx context.Context, node string, vmid int) (string, error) {
|
||||
response, err := c.Delete(ctx, fmt.Sprintf(
|
||||
"/nodes/%s/qemu/%d",
|
||||
url.PathEscape(node),
|
||||
vmid,
|
||||
))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer response.Body.Close()
|
||||
|
||||
if response.StatusCode >= http.StatusBadRequest {
|
||||
_, _ = io.Copy(io.Discard, response.Body)
|
||||
return "", fmt.Errorf("proxmox returned %s", response.Status)
|
||||
}
|
||||
|
||||
return decodeUPID(response.Body)
|
||||
}
|
||||
|
||||
type CloudInitConfig struct {
|
||||
CIUser string `json:"ciuser"`
|
||||
SSHKeys string `json:"sshkeys,omitempty"`
|
||||
IPConfig0 string `json:"ipconfig0,omitempty"`
|
||||
Hostname string `json:"hostname,omitempty"`
|
||||
}
|
||||
|
||||
func decodeUPID(body io.Reader) (string, error) {
|
||||
var response struct {
|
||||
Data string `json:"data"`
|
||||
}
|
||||
if err := json.NewDecoder(body).Decode(&response); err != nil {
|
||||
return "", err
|
||||
}
|
||||
if response.Data == "" {
|
||||
return "", fmt.Errorf("proxmox response missing UPID")
|
||||
}
|
||||
return response.Data, nil
|
||||
}
|
||||
|
||||
func (c *Client) GetVNCTicket(ctx context.Context, node string, vmid int) (VNCInfo, error) {
|
||||
requestBody := map[string]any{}
|
||||
body, err := json.Marshal(requestBody)
|
||||
if err != nil {
|
||||
return VNCInfo{}, err
|
||||
}
|
||||
|
||||
response, err := c.Post(ctx, fmt.Sprintf(
|
||||
"/nodes/%s/qemu/%d/vncproxy",
|
||||
url.PathEscape(node),
|
||||
vmid,
|
||||
), body)
|
||||
if err != nil {
|
||||
return VNCInfo{}, err
|
||||
}
|
||||
defer response.Body.Close()
|
||||
|
||||
if response.StatusCode >= http.StatusBadRequest {
|
||||
_, _ = io.Copy(io.Discard, response.Body)
|
||||
return VNCInfo{}, fmt.Errorf("proxmox returned %s", response.Status)
|
||||
}
|
||||
|
||||
var result struct {
|
||||
Data struct {
|
||||
Port int `json:"port"`
|
||||
Ticket string `json:"ticket"`
|
||||
User string `json:"user"`
|
||||
Cert string `json:"cert"`
|
||||
UPID string `json:"upid"`
|
||||
} `json:"data"`
|
||||
}
|
||||
if err := json.NewDecoder(response.Body).Decode(&result); err != nil {
|
||||
return VNCInfo{}, err
|
||||
}
|
||||
|
||||
return VNCInfo{
|
||||
Port: result.Data.Port,
|
||||
Ticket: result.Data.Ticket,
|
||||
User: result.Data.User,
|
||||
Cert: result.Data.Cert,
|
||||
UPID: result.Data.UPID,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type VNCInfo struct {
|
||||
Port int
|
||||
Ticket string
|
||||
User string
|
||||
Cert string
|
||||
UPID string
|
||||
}
|
||||
|
||||
func (c *Client) Do(ctx context.Context, method string, path string, body []byte) (*http.Response, error) {
|
||||
var lastErr error
|
||||
attempts := c.retries + 1
|
||||
|
||||
Reference in New Issue
Block a user