Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 20 additions & 2 deletions cmd/vm/lifecycle.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,8 +65,12 @@ func (h Handler) Inspect(cmd *cobra.Command, args []string) error {

out := vmOutput{VM: info, Stale: stale}
if info.State == types.VMStateRunning {
out.AttachedDevices = collectAttachedDevices(ctx, hyper, args[0])
out.Balloon = collectBalloon(ctx, hyper, args[0])
if v, ok := hyper.(hypervisor.RunningViewer); ok {
out.AttachedDevices, out.Balloon = collectRunningView(ctx, v, args[0])
} else {
out.AttachedDevices = collectAttachedDevices(ctx, hyper, args[0])
out.Balloon = collectBalloon(ctx, hyper, args[0])
}
}
return cliutil.OutputJSON(out)
}
Expand Down Expand Up @@ -214,6 +218,20 @@ func collectBalloon(ctx context.Context, hyper hypervisor.Hypervisor, ref string
return st
}

func collectRunningView(ctx context.Context, v hypervisor.RunningViewer, ref string) (*attachedDevices, *balloon.Status) {
view, err := v.RunningView(ctx, ref)
if err != nil {
log.WithFunc("cmd.vm.inspect").Warnf(ctx, "read running state for %s: %v", ref, err)
}
if view == nil {
return nil, nil
}
if len(view.Fs) == 0 && len(view.Devices) == 0 && len(view.Disks) == 0 {
return nil, view.Balloon
}
return &attachedDevices{Fs: view.Fs, Devices: view.Devices, Disks: view.Disks}, view.Balloon
}

// collectAttachedDevices logs and drops errors so inspect tolerates a flaky vm.info.
func collectAttachedDevices(ctx context.Context, hyper hypervisor.Hypervisor, ref string) *attachedDevices {
logger := log.WithFunc("cmd.vm.inspect")
Expand Down
69 changes: 47 additions & 22 deletions hypervisor/cloudhypervisor/extend.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,15 +116,7 @@ func (ch *CloudHypervisor) DiskDetach(ctx context.Context, vmRef, name string) e
}

func (ch *CloudHypervisor) DiskList(ctx context.Context, vmRef string) ([]disk.Attached, error) {
return ch.listWith(ctx, vmRef, func(info *chVMInfoResponse) []disk.Attached {
var out []disk.Attached
for _, d := range info.Config.Disks {
if name := disk.NameFromID(d.ID); name != "" {
out = append(out, disk.Attached{ID: d.ID, Name: name, Path: d.Path, ReadOnly: d.ReadOnly})
}
}
return out
})
return ch.listWith(ctx, vmRef, disksAttached)
}

func (ch *CloudHypervisor) FsAttach(ctx context.Context, vmRef string, spec fs.Spec) (string, error) {
Expand Down Expand Up @@ -164,13 +156,7 @@ func (ch *CloudHypervisor) FsDetach(ctx context.Context, vmRef, tag string) erro
}

func (ch *CloudHypervisor) FsList(ctx context.Context, vmRef string) ([]fs.Attached, error) {
return ch.listWith(ctx, vmRef, func(info *chVMInfoResponse) []fs.Attached {
out := make([]fs.Attached, 0, len(info.Config.Fs))
for _, f := range info.Config.Fs {
out = append(out, fs.Attached{ID: f.ID, Tag: f.Tag, Socket: f.Socket})
}
return out
})
return ch.listWith(ctx, vmRef, fsAttached)
}

func (ch *CloudHypervisor) DeviceAttach(ctx context.Context, vmRef string, spec vfio.Spec) (string, error) {
Expand Down Expand Up @@ -213,13 +199,26 @@ func (ch *CloudHypervisor) DeviceDetach(ctx context.Context, vmRef, id string) e
}

func (ch *CloudHypervisor) DeviceList(ctx context.Context, vmRef string) ([]vfio.Attached, error) {
return ch.listWith(ctx, vmRef, func(info *chVMInfoResponse) []vfio.Attached {
out := make([]vfio.Attached, 0, len(info.Config.Devices))
for _, d := range info.Config.Devices {
out = append(out, vfio.Attached{ID: d.ID, BDF: strings.TrimPrefix(d.Path, vfio.SysfsPCIPrefix)})
return ch.listWith(ctx, vmRef, devicesAttached)
}

func (ch *CloudHypervisor) RunningView(ctx context.Context, vmRef string) (*hypervisor.RunningView, error) {
hc, rec, err := ch.RunningVMClient(ctx, vmRef)
if err != nil {
if errors.Is(err, hypervisor.ErrNotRunning) {
return nil, nil
}
return out
})
return nil, err
}
info, err := getVMInfo(ctx, hc)
if err != nil {
return nil, err
}
view := &hypervisor.RunningView{Fs: fsAttached(info), Devices: devicesAttached(info), Disks: disksAttached(info)}
if info.Config.Balloon != nil {
view.Balloon = new(balloonStatus(&rec, info))
}
return view, nil
}

// lockedDeviceOp locks device-set mutations per VM and returns the record plus vm.info, resuming an ownerless pause.
Expand Down Expand Up @@ -291,6 +290,32 @@ func (ch *CloudHypervisor) listWith[A any](ctx context.Context, vmRef string, ex
return extract(info), nil
}

func disksAttached(info *chVMInfoResponse) []disk.Attached {
var out []disk.Attached
for _, d := range info.Config.Disks {
if name := disk.NameFromID(d.ID); name != "" {
out = append(out, disk.Attached{ID: d.ID, Name: name, Path: d.Path, ReadOnly: d.ReadOnly})
}
}
return out
}

func fsAttached(info *chVMInfoResponse) []fs.Attached {
out := make([]fs.Attached, 0, len(info.Config.Fs))
for _, f := range info.Config.Fs {
out = append(out, fs.Attached{ID: f.ID, Tag: f.Tag, Socket: f.Socket})
}
return out
}

func devicesAttached(info *chVMInfoResponse) []vfio.Attached {
out := make([]vfio.Attached, 0, len(info.Config.Devices))
for _, d := range info.Config.Devices {
out = append(out, vfio.Attached{ID: d.ID, BDF: strings.TrimPrefix(d.Path, vfio.SysfsPCIPrefix)})
}
return out
}

func addDevice(ctx context.Context, hc *http.Client, endpoint string, body any) (string, error) {
resp, err := utils.DoJSONOnce(ctx, hc, http.MethodPut, chAPIBase+endpoint, endpoint, body, http.StatusOK)
if err != nil {
Expand Down
14 changes: 14 additions & 0 deletions hypervisor/hypervisor.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,11 @@ import (
"errors"
"io"

"github.com/cocoonstack/cocoon/extend/balloon"
"github.com/cocoonstack/cocoon/extend/disk"
"github.com/cocoonstack/cocoon/extend/fs"
"github.com/cocoonstack/cocoon/extend/netresize"
"github.com/cocoonstack/cocoon/extend/vfio"
"github.com/cocoonstack/cocoon/gc"
"github.com/cocoonstack/cocoon/types"
)
Expand All @@ -24,6 +27,17 @@ var (

type PersistFunc func(cfg *types.SnapshotConfig, srcDir string) error

type RunningViewer interface {
RunningView(ctx context.Context, ref string) (*RunningView, error)
}

type RunningView struct {
Fs []fs.Attached
Devices []vfio.Attached
Disks []disk.Attached
Balloon *balloon.Status
}

// Hypervisor manages VM lifecycle.
type Hypervisor interface {
Supervisable
Expand Down
Loading