mirror of
https://github.com/1Panel-dev/1Panel.git
synced 2026-11-07 00:05:14 +00:00
* feat(vm): enable community virtual machines and migrate xpack data * fix(vm): stop ISO download polling on failure or cancellation * fix(vm): respect deleteFile when deleting failed ISO imports
550 lines
18 KiB
Go
550 lines
18 KiB
Go
package helper
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/1Panel-dev/1Panel/agent/app/dto"
|
|
"github.com/1Panel-dev/1Panel/agent/buserr"
|
|
"github.com/1Panel-dev/1Panel/agent/global"
|
|
"github.com/1Panel-dev/1Panel/agent/utils/vm/protocol"
|
|
"github.com/1Panel-dev/1Panel/agent/utils/vm/types"
|
|
)
|
|
|
|
const (
|
|
defaultTimeout = 2 * time.Minute
|
|
longRunningTimeout = 30 * time.Minute
|
|
slowActionThreshold = 5 * time.Second
|
|
)
|
|
|
|
type Provider struct {
|
|
HelperPath string
|
|
URI string
|
|
Timeout time.Duration
|
|
mu sync.Mutex
|
|
versionOnce sync.Once
|
|
versionGate chan struct{}
|
|
checked bool
|
|
}
|
|
|
|
func NewProvider() (*Provider, error) {
|
|
return &Provider{URI: protocol.DefaultURI, Timeout: defaultTimeout}, nil
|
|
}
|
|
|
|
func (p *Provider) CheckEnvironment(ctx context.Context) ([]dto.VMCheckItem, error) {
|
|
var items []protocol.VMCheckItem
|
|
if err := p.call(ctx, protocol.ActionCheck, nil, &items); err != nil {
|
|
return nil, err
|
|
}
|
|
out := make([]dto.VMCheckItem, 0, len(items))
|
|
for _, item := range items {
|
|
out = append(out, dto.VMCheckItem{
|
|
Name: item.Name,
|
|
Passed: item.Passed,
|
|
Message: item.Message,
|
|
IsEnable: item.IsEnable,
|
|
IsExist: item.IsExist,
|
|
IsActive: item.IsActive,
|
|
})
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
func (p *Provider) HealthCheck(ctx context.Context, name string) (dto.VMHealthCheckResult, error) {
|
|
var result protocol.VMHealthCheckResult
|
|
if err := p.callWithTimeout(
|
|
ctx,
|
|
protocol.ActionHealthCheck,
|
|
protocol.VMHealthCheckRequest{Name: name},
|
|
&result,
|
|
longRunningTimeout,
|
|
); err != nil {
|
|
return dto.VMHealthCheckResult{}, err
|
|
}
|
|
out := dto.VMHealthCheckResult{
|
|
Pass: result.Pass,
|
|
Warn: result.Warn,
|
|
Fail: result.Fail,
|
|
Items: make([]dto.VMHealthCheckItem, 0, len(result.Items)),
|
|
}
|
|
for _, item := range result.Items {
|
|
out.Items = append(out.Items, dto.VMHealthCheckItem{
|
|
Level: item.Level, Check: item.Check, Reason: item.Reason, Params: item.Params,
|
|
Detail: item.Detail, FixCommand: item.FixCommand,
|
|
})
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
func (p *Provider) HelperStatus() bool {
|
|
if _, err := p.resolveHelperPath(); err != nil {
|
|
return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
func (p *Provider) List(ctx context.Context) ([]dto.VMInfo, error) {
|
|
var items []protocol.VMInfo
|
|
if err := p.call(ctx, protocol.ActionList, nil, &items); err != nil {
|
|
return nil, err
|
|
}
|
|
out := make([]dto.VMInfo, 0, len(items))
|
|
for _, item := range items {
|
|
out = append(out, toDTO(item))
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
func (p *Provider) ListResources(ctx context.Context, resourceType string) ([]dto.VMResourceInfo, error) {
|
|
var items []protocol.VMResourceInfo
|
|
if err := p.call(ctx, protocol.ActionResources, protocol.VMResourceSearch{Type: resourceType}, &items); err != nil {
|
|
return nil, err
|
|
}
|
|
out := make([]dto.VMResourceInfo, 0, len(items))
|
|
for _, item := range items {
|
|
out = append(out, dto.VMResourceInfo{
|
|
Type: item.Type, Name: item.Name, SubType: item.SubType, BridgeName: item.BridgeName, SourceInterface: item.SourceInterface,
|
|
CIDR: item.CIDR, Gateway: item.Gateway, DHCPStart: item.DHCPStart, DHCPEnd: item.DHCPEnd,
|
|
Path: item.Path, Status: item.Status, AutoStart: item.AutoStart, Capacity: item.Capacity, Available: item.Available,
|
|
})
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
func (p *Provider) CreateResource(ctx context.Context, req types.VMResourceCreate) error {
|
|
return p.call(ctx, protocol.ActionCreateResource, protocol.VMResourceCreate{
|
|
Type: req.Type, Name: req.Name, SubType: req.SubType, BridgeName: req.BridgeName, SourceInterface: req.SourceInterface,
|
|
CIDR: req.CIDR, Gateway: req.Gateway, DHCPStart: req.DHCPStart, DHCPEnd: req.DHCPEnd,
|
|
Path: req.Path, AutoStart: req.AutoStart,
|
|
}, nil)
|
|
}
|
|
|
|
func (p *Provider) UpdateResource(ctx context.Context, req types.VMResourceUpdate) error {
|
|
return p.call(ctx, protocol.ActionUpdateResource, protocol.VMResourceUpdate{
|
|
Type: req.Type, Name: req.Name, AutoStart: req.AutoStart, Status: req.Status,
|
|
}, nil)
|
|
}
|
|
|
|
func (p *Provider) DeleteResource(ctx context.Context, resourceType string, name string) error {
|
|
return p.call(ctx, protocol.ActionDeleteResource, protocol.VMResourceDelete{Type: resourceType, Name: name}, nil)
|
|
}
|
|
|
|
func (p *Provider) ScanOrphans(ctx context.Context, protectedPaths []string) ([]dto.VMOrphanItem, error) {
|
|
var items []protocol.VMOrphanItem
|
|
if err := p.callWithTimeout(ctx, protocol.ActionScanOrphans, protocol.VMOrphanScan{ProtectedPaths: protectedPaths}, &items, longRunningTimeout); err != nil {
|
|
return nil, err
|
|
}
|
|
out := make([]dto.VMOrphanItem, 0, len(items))
|
|
for _, item := range items {
|
|
out = append(out, dto.VMOrphanItem{Type: item.Type, Path: item.Path, Size: item.Size, ModifiedAt: item.ModifiedAt})
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
func (p *Provider) CleanOrphans(ctx context.Context, items []dto.VMOrphanItem, protectedPaths []string) ([]dto.VMOrphanItem, error) {
|
|
payload := protocol.VMOrphanClean{ProtectedPaths: protectedPaths, Items: make([]protocol.VMOrphanItem, 0, len(items))}
|
|
for _, item := range items {
|
|
payload.Items = append(payload.Items, protocol.VMOrphanItem{Type: item.Type, Path: item.Path, Size: item.Size, ModifiedAt: item.ModifiedAt})
|
|
}
|
|
var removed []protocol.VMOrphanItem
|
|
if err := p.callWithTimeout(ctx, protocol.ActionCleanOrphans, payload, &removed, longRunningTimeout); err != nil {
|
|
return nil, err
|
|
}
|
|
out := make([]dto.VMOrphanItem, 0, len(removed))
|
|
for _, item := range removed {
|
|
out = append(out, dto.VMOrphanItem{Type: item.Type, Path: item.Path, Size: item.Size, ModifiedAt: item.ModifiedAt})
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
func (p *Provider) Get(ctx context.Context, name string, checkDisks bool) (dto.VMInfo, error) {
|
|
var item protocol.VMInfo
|
|
if err := p.call(ctx, protocol.ActionGet, protocol.VMInfoReq{Name: name, CheckDisks: checkDisks}, &item); err != nil {
|
|
return dto.VMInfo{}, err
|
|
}
|
|
return toDTO(item), nil
|
|
}
|
|
|
|
func (p *Provider) Stats(ctx context.Context, name string) (dto.VMStats, error) {
|
|
var item protocol.VMStats
|
|
if err := p.call(ctx, protocol.ActionStats, protocol.VMName{Name: name}, &item); err != nil {
|
|
return dto.VMStats{}, err
|
|
}
|
|
return dto.VMStats{
|
|
Name: item.Name, Status: item.Status, Timestamp: item.Timestamp, CPUTime: item.CPUTime, VCPU: item.VCPU,
|
|
Memory: dto.VMMemoryStats(item.Memory), Disk: dto.VMDiskStats(item.Disk), Network: dto.VMNetworkStats(item.Network),
|
|
}, nil
|
|
}
|
|
|
|
func (p *Provider) Create(ctx context.Context, cfg types.VMConfig) (dto.VMInfo, error) {
|
|
var item protocol.VMInfo
|
|
if err := p.call(ctx, protocol.ActionCreate, toProtocolConfig(cfg), &item); err != nil {
|
|
return dto.VMInfo{}, err
|
|
}
|
|
return toDTO(item), nil
|
|
}
|
|
|
|
func (p *Provider) CreateFromTemplate(ctx context.Context, cfg types.VMConfig) (dto.VMInfo, error) {
|
|
var item protocol.VMInfo
|
|
if err := p.callWithTimeout(ctx, protocol.ActionCreateFromTemplate, toProtocolConfig(cfg), &item, longRunningTimeout); err != nil {
|
|
return dto.VMInfo{}, err
|
|
}
|
|
return toDTO(item), nil
|
|
}
|
|
|
|
func (p *Provider) CreateTemplate(ctx context.Context, req types.VMTemplateCreate) (dto.VMInfo, error) {
|
|
var item protocol.VMInfo
|
|
if err := p.callWithTimeout(ctx, protocol.ActionCreateTemplate, protocol.VMTemplateCreate{
|
|
Name: req.Name, SourceName: req.SourceName, DiskPath: req.DiskPath, StoragePool: req.StoragePool,
|
|
}, &item, longRunningTimeout); err != nil {
|
|
return dto.VMInfo{}, err
|
|
}
|
|
return toDTO(item), nil
|
|
}
|
|
|
|
func (p *Provider) Update(ctx context.Context, cfg types.VMConfig) (dto.VMInfo, error) {
|
|
var item protocol.VMInfo
|
|
if err := p.call(ctx, protocol.ActionUpdate, toProtocolConfig(cfg), &item); err != nil {
|
|
return dto.VMInfo{}, err
|
|
}
|
|
return toDTO(item), nil
|
|
}
|
|
|
|
func (p *Provider) ResizeDisk(ctx context.Context, req dto.VMDiskResize) (dto.VMInfo, error) {
|
|
var item protocol.VMInfo
|
|
if err := p.callWithTimeout(ctx, protocol.ActionResizeDisk, protocol.VMDiskResize{Name: req.Name, Target: req.Target, SizeGiB: req.SizeGiB}, &item, longRunningTimeout); err != nil {
|
|
return dto.VMInfo{}, err
|
|
}
|
|
return toDTO(item), nil
|
|
}
|
|
|
|
func (p *Provider) Rename(ctx context.Context, name string, newName string) (dto.VMInfo, error) {
|
|
var item protocol.VMInfo
|
|
if err := p.call(ctx, protocol.ActionRename, protocol.VMRename{Name: name, NewName: newName}, &item); err != nil {
|
|
return dto.VMInfo{}, err
|
|
}
|
|
return toDTO(item), nil
|
|
}
|
|
|
|
func (p *Provider) Delete(ctx context.Context, name string, force bool, deleteDisk bool, protectedPaths []string) error {
|
|
return p.call(ctx, protocol.ActionDelete, protocol.VMDelete{Name: name, Force: force, DeleteDisk: deleteDisk, ProtectedPaths: protectedPaths}, nil)
|
|
}
|
|
|
|
func (p *Provider) ListSnapshots(ctx context.Context, name string) ([]dto.VMSnapshotInfo, error) {
|
|
var items []protocol.VMSnapshotInfo
|
|
if err := p.call(ctx, protocol.ActionListSnapshots, protocol.VMName{Name: name}, &items); err != nil {
|
|
return nil, err
|
|
}
|
|
out := make([]dto.VMSnapshotInfo, 0, len(items))
|
|
for _, item := range items {
|
|
out = append(out, dto.VMSnapshotInfo(item))
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
func (p *Provider) CreateSnapshot(ctx context.Context, name string, snapshotName string, description string) error {
|
|
return p.call(ctx, protocol.ActionCreateSnapshot, protocol.VMSnapshotReq{
|
|
Name: name, SnapshotName: snapshotName, Description: description,
|
|
}, nil)
|
|
}
|
|
|
|
func (p *Provider) RecoverSnapshot(ctx context.Context, name string, snapshotName string) error {
|
|
return p.call(ctx, protocol.ActionRecoverSnapshot, protocol.VMSnapshotReq{Name: name, SnapshotName: snapshotName}, nil)
|
|
}
|
|
|
|
func (p *Provider) DeleteSnapshot(ctx context.Context, name string, snapshotName string) error {
|
|
return p.call(ctx, protocol.ActionDeleteSnapshot, protocol.VMSnapshotReq{Name: name, SnapshotName: snapshotName}, nil)
|
|
}
|
|
|
|
func (p *Provider) Start(ctx context.Context, name string) error {
|
|
return p.lifecycle(ctx, protocol.ActionStart, name)
|
|
}
|
|
|
|
func (p *Provider) Shutdown(ctx context.Context, name string) error {
|
|
return p.lifecycle(ctx, protocol.ActionShutdown, name)
|
|
}
|
|
|
|
func (p *Provider) Reboot(ctx context.Context, name string) error {
|
|
return p.lifecycle(ctx, protocol.ActionReboot, name)
|
|
}
|
|
|
|
func (p *Provider) Destroy(ctx context.Context, name string) error {
|
|
return p.lifecycle(ctx, protocol.ActionDestroy, name)
|
|
}
|
|
|
|
func (p *Provider) Suspend(ctx context.Context, name string) error {
|
|
return p.lifecycle(ctx, protocol.ActionSuspend, name)
|
|
}
|
|
|
|
func (p *Provider) Resume(ctx context.Context, name string) error {
|
|
return p.lifecycle(ctx, protocol.ActionResume, name)
|
|
}
|
|
|
|
func (p *Provider) Console(ctx context.Context, name string) (*dto.VMGraphics, error) {
|
|
var item protocol.VMGraphics
|
|
if err := p.call(ctx, protocol.ActionConsole, protocol.VMName{Name: name}, &item); err != nil {
|
|
return nil, err
|
|
}
|
|
return (*dto.VMGraphics)(&item), nil
|
|
}
|
|
|
|
func (p *Provider) lifecycle(ctx context.Context, action string, name string) error {
|
|
return p.call(ctx, action, protocol.VMName{Name: name}, nil)
|
|
}
|
|
|
|
func (p *Provider) checkVersion(ctx context.Context) error {
|
|
var info protocol.VersionInfo
|
|
if err := p.call(ctx, protocol.ActionVersion, nil, &info); err != nil {
|
|
return err
|
|
}
|
|
if info.API != protocol.MinVersion {
|
|
return buserr.WithErr("ErrVMCommandFailed", fmt.Errorf("unsupported kvm helper api version: %s", info.API))
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (p *Provider) call(ctx context.Context, action string, payload interface{}, out interface{}) error {
|
|
return p.callWithTimeout(ctx, action, payload, out, p.Timeout)
|
|
}
|
|
|
|
func (p *Provider) callWithTimeout(ctx context.Context, action string, payload interface{}, out interface{}, timeout time.Duration) (retErr error) {
|
|
startedAt := time.Now()
|
|
defer func() {
|
|
logAction(action, time.Since(startedAt), retErr)
|
|
}()
|
|
if ctx == nil {
|
|
ctx = context.Background()
|
|
}
|
|
if timeout == 0 {
|
|
timeout = defaultTimeout
|
|
}
|
|
runCtx, cancel := context.WithTimeout(ctx, timeout)
|
|
defer cancel()
|
|
if action != protocol.ActionVersion {
|
|
if err := p.ensureVersion(runCtx); err != nil {
|
|
if errors.Is(err, context.DeadlineExceeded) {
|
|
return buserr.New("ErrCmdTimeout")
|
|
}
|
|
return err
|
|
}
|
|
}
|
|
|
|
req := protocol.Request{Version: protocol.Version, Action: action, URI: p.URI, Payload: payload}
|
|
input, err := json.Marshal(req)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
helperPath, err := p.resolveHelperPath()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
cmd := exec.Command(helperPath)
|
|
cmd.Stdin = bytes.NewReader(input)
|
|
var stdout, stderr bytes.Buffer
|
|
cmd.Stdout = &stdout
|
|
cmd.Stderr = &stderr
|
|
err = runHelperCommand(runCtx, cmd)
|
|
if stderr.Len() > 0 && global.LOG != nil {
|
|
global.LOG.Warnf("kvm helper stderr: %s", stderr.String())
|
|
}
|
|
if errors.Is(runCtx.Err(), context.DeadlineExceeded) {
|
|
return buserr.New("ErrCmdTimeout")
|
|
}
|
|
if err != nil {
|
|
return buserr.WithErr("ErrVMCommandFailed", fmt.Errorf("helper failed: %w, stderr: %s", err, stderr.String()))
|
|
}
|
|
var resp protocol.Response
|
|
if err := json.Unmarshal(stdout.Bytes(), &resp); err != nil {
|
|
return buserr.WithErr("ErrVMCommandFailed", fmt.Errorf("invalid helper response: %w", err))
|
|
}
|
|
if !resp.Success {
|
|
return mapHelperError(resp.Error)
|
|
}
|
|
if out == nil {
|
|
return nil
|
|
}
|
|
data, err := json.Marshal(resp.Data)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return json.Unmarshal(data, out)
|
|
}
|
|
|
|
func logAction(action string, duration time.Duration, err error) {
|
|
if global.LOG == nil {
|
|
return
|
|
}
|
|
durationMs := duration.Milliseconds()
|
|
if err != nil {
|
|
global.LOG.Errorf("[kvm] action failed, action=%s, duration_ms=%d, err=%v", action, durationMs, err)
|
|
return
|
|
}
|
|
if duration >= slowActionThreshold {
|
|
global.LOG.Warnf("[kvm] slow action, action=%s, duration_ms=%d", action, durationMs)
|
|
}
|
|
}
|
|
|
|
func runHelperCommand(ctx context.Context, cmd *exec.Cmd) error {
|
|
if err := ctx.Err(); err != nil {
|
|
return err
|
|
}
|
|
if err := cmd.Start(); err != nil {
|
|
return err
|
|
}
|
|
done := make(chan error, 1)
|
|
go func() {
|
|
done <- cmd.Wait()
|
|
}()
|
|
select {
|
|
case err := <-done:
|
|
return err
|
|
case <-ctx.Done():
|
|
if cmd.Process != nil {
|
|
_ = cmd.Process.Signal(os.Interrupt)
|
|
}
|
|
timer := time.NewTimer(5 * time.Second)
|
|
defer timer.Stop()
|
|
select {
|
|
case err := <-done:
|
|
return err
|
|
case <-timer.C:
|
|
if cmd.Process != nil {
|
|
_ = cmd.Process.Kill()
|
|
}
|
|
return <-done
|
|
}
|
|
}
|
|
}
|
|
|
|
func (p *Provider) ensureVersion(ctx context.Context) error {
|
|
p.versionOnce.Do(func() {
|
|
p.versionGate = make(chan struct{}, 1)
|
|
})
|
|
select {
|
|
case p.versionGate <- struct{}{}:
|
|
defer func() { <-p.versionGate }()
|
|
case <-ctx.Done():
|
|
return ctx.Err()
|
|
}
|
|
if err := ctx.Err(); err != nil {
|
|
return err
|
|
}
|
|
if p.checked {
|
|
return nil
|
|
}
|
|
if err := p.checkVersion(ctx); err != nil {
|
|
return err
|
|
}
|
|
p.checked = true
|
|
return nil
|
|
}
|
|
|
|
func (p *Provider) resolveHelperPath() (string, error) {
|
|
p.mu.Lock()
|
|
defer p.mu.Unlock()
|
|
if p.HelperPath != "" {
|
|
return p.HelperPath, nil
|
|
}
|
|
path, err := findHelperPath()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
p.HelperPath = path
|
|
return path, nil
|
|
}
|
|
|
|
func mapHelperError(item *protocol.Error) error {
|
|
if item == nil {
|
|
return buserr.New("ErrVMCommandFailed")
|
|
}
|
|
switch item.Code {
|
|
case "MAIN_DISK_DELETE_FAILED":
|
|
return item
|
|
case "VM_NOT_FOUND":
|
|
return buserr.New("ErrVMNotFound")
|
|
case "VM_ALREADY_EXISTS":
|
|
return buserr.New("ErrVMAlreadyExists")
|
|
case "VM_IMAGE_ALREADY_EXISTS":
|
|
return buserr.WithDetail("ErrVMImageAlreadyExists", item.Detail, errors.New(item.Detail))
|
|
case "DISK_RESIZE_BLOCKED":
|
|
if item.Detail == "referenceCheck" {
|
|
return buserr.New("ErrVMDiskReferenceCheck")
|
|
}
|
|
if item.Detail == "referenced" {
|
|
return buserr.New("ErrVMDiskReferenced")
|
|
}
|
|
return buserr.New("ErrVMDiskResizeBlocked")
|
|
case "VM_RUNNING":
|
|
return buserr.New("ErrVMRunning")
|
|
case "LIBVIRT_CONNECT":
|
|
return buserr.WithDetail("ErrVMLibvirtConnect", item.Detail, errors.New(item.Detail))
|
|
case "PERMISSION_DENIED":
|
|
return buserr.WithDetail("ErrVMPermission", item.Detail, errors.New(item.Detail))
|
|
case "NETWORK_NOT_FOUND":
|
|
return buserr.WithDetail("ErrVMNetworkNotFound", item.Detail, errors.New(item.Detail))
|
|
case "INVALID_PATH":
|
|
return buserr.WithDetail("ErrVMInvalidPath", item.Detail, errors.New(item.Detail))
|
|
case "INVALID_INPUT":
|
|
return buserr.WithDetail("ErrVMInvalidInput", item.Detail, errors.New(item.Detail))
|
|
default:
|
|
detail := item.Detail
|
|
if detail == "" {
|
|
detail = item.Message
|
|
}
|
|
return buserr.WithErr("ErrVMCommandFailed", errors.New(detail))
|
|
}
|
|
}
|
|
|
|
func findHelperPath() (string, error) {
|
|
if path := os.Getenv("ONEPANEL_KVM"); path != "" {
|
|
return path, nil
|
|
}
|
|
self, err := os.Executable()
|
|
if err == nil {
|
|
candidate := filepath.Join(filepath.Dir(self), protocol.HelperBinary)
|
|
if _, err := os.Stat(candidate); err == nil {
|
|
return candidate, nil
|
|
}
|
|
}
|
|
if path, err := exec.LookPath(protocol.HelperBinary); err == nil {
|
|
return path, nil
|
|
}
|
|
return "", buserr.WithErr("ErrVMCommandFailed", fmt.Errorf("%s not found", protocol.HelperBinary))
|
|
}
|
|
|
|
func toProtocolConfig(cfg types.VMConfig) protocol.VMConfig {
|
|
return protocol.VMConfig{
|
|
Name: cfg.Name, CPU: cfg.CPU, MemoryMiB: cfg.MemoryMiB, DiskSizeGiB: cfg.DiskSizeGiB,
|
|
DiskBus: cfg.DiskBus, DiskPath: cfg.DiskPath, StoragePool: cfg.StoragePool, ISOPaths: cfg.ISOPaths, ImagePath: cfg.ImagePath,
|
|
Network: cfg.Network, BootOrder: cfg.BootOrder, AutoStart: cfg.AutoStart,
|
|
StartAfterCreate: cfg.StartAfterCreate,
|
|
}
|
|
}
|
|
|
|
func toDTO(item protocol.VMInfo) dto.VMInfo {
|
|
out := dto.VMInfo{
|
|
Name: item.Name, UUID: item.UUID, CPU: item.CPU, Memory: item.Memory, DiskSize: item.DiskSize,
|
|
DiskBus: item.DiskBus, DiskPath: item.DiskPath, StoragePool: item.StoragePool, ISOPaths: item.ISOPaths, ImagePath: item.ImagePath,
|
|
Network: item.Network, BootOrder: item.BootOrder, AutoStart: item.AutoStart, Status: item.Status,
|
|
}
|
|
for _, disk := range item.Disks {
|
|
out.Disks = append(out.Disks, dto.VMDisk(disk))
|
|
}
|
|
for _, net := range item.Interfaces {
|
|
out.Interfaces = append(out.Interfaces, dto.VMInterface(net))
|
|
}
|
|
if item.Graphics != nil {
|
|
graphics := dto.VMGraphics(*item.Graphics)
|
|
out.Graphics = &graphics
|
|
}
|
|
return out
|
|
}
|