Skip to content

Commit 4fc95cd

Browse files
committed
vz: implement save/restore
- Only `aarch64` is supported due to lack of support in `Code-Hex/vz/v3`. - Attempt to restore if the `vz-machine-state` file is found in the instance directory. If this fails, proceed with a cold boot. - On stop, always attempt to save to the `vz-machine-state` file; if this fails, perform a shutdown. - Guest OS and Guest Agent behavior are not considered here. Handling may be needed for saving while port forwarding is active. Signed-off-by: Norio Nomura <[email protected]>
1 parent 1f0113c commit 4fc95cd

File tree

5 files changed

+114
-4
lines changed

5 files changed

+114
-4
lines changed

pkg/store/filenames/filenames.go

+1
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ const (
5656
HostAgentStderrLog = "ha.stderr.log"
5757
VzIdentifier = "vz-identifier"
5858
VzEfi = "vz-efi" // efi variable store
59+
VzMachineState = "vz-machine-state" // machine state file
5960
QemuEfiCodeFD = "qemu-efi-code.fd" // efi code; not always created
6061
AnsibleInventoryYAML = "ansible-inventory.yaml"
6162

pkg/vz/save_restore.go

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
//go:build darwin && !arm64 && !no_vz
2+
3+
package vz
4+
5+
import (
6+
"fmt"
7+
"runtime"
8+
9+
"github.com/Code-Hex/vz/v3"
10+
)
11+
12+
func saveVM(vm *vz.VirtualMachine, machineStatePath string) error {
13+
return fmt.Errorf("save is not supported on the vz driver for the architecture %s", runtime.GOARCH)
14+
}
15+
16+
func restoreVM(vm *vz.VirtualMachine, machineStatePath string) error {
17+
return fmt.Errorf("restore is not supported on the vz driver for the architecture %s", runtime.GOARCH)
18+
}

pkg/vz/save_restore_arm64.go

+76
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
//go:build darwin && arm64 && !no_vz
2+
3+
package vz
4+
5+
import (
6+
"errors"
7+
"fmt"
8+
"os"
9+
10+
"github.com/Code-Hex/vz/v3"
11+
"github.com/sirupsen/logrus"
12+
)
13+
14+
func saveVM(vm *vz.VirtualMachine, machineStatePath string) error {
15+
if !vm.CanPause() {
16+
return fmt.Errorf("can't pause the VZ machine")
17+
}
18+
19+
// Remove the old machine state file if it exists,
20+
// because saving the machine state will fail if the file already exists.
21+
if err := os.Remove(machineStatePath); err != nil && !errors.Is(err, os.ErrNotExist) {
22+
logrus.WithError(err).Errorf("Failed to remove the old VZ machine state file %q", machineStatePath)
23+
return err
24+
}
25+
26+
logrus.Info("Pausing VZ")
27+
if err := vm.Pause(); err != nil {
28+
return err
29+
}
30+
31+
// If we can't stop the machine after pausing, saving the machine state will be useless.
32+
// So we should check this before saving the machine state.
33+
if !vm.CanStop() {
34+
return fmt.Errorf("can't stop the VZ machine after pausing")
35+
}
36+
37+
logrus.Info("Saving VZ machine state for resuming later")
38+
if err := vm.SaveMachineStateToPath(machineStatePath); err != nil {
39+
// If we fail to save the machine state, we should resume the machine to call RequestStop() later
40+
logrus.WithError(err).Errorf("Failed to save the machine state to %q", machineStatePath)
41+
if resumeError := vm.Resume(); resumeError != nil {
42+
return resumeError
43+
}
44+
return err
45+
}
46+
47+
logrus.Info("Stopping VZ")
48+
if err := vm.Stop(); err != nil {
49+
// If we fail to stop the machine, we should resume the machine to call RequestStop() later
50+
logrus.WithError(err).Error("Failed to stop the VZ machine")
51+
if resumeError := vm.Resume(); resumeError != nil {
52+
return resumeError
53+
}
54+
return err
55+
}
56+
return nil
57+
}
58+
59+
func restoreVM(vm *vz.VirtualMachine, machineStatePath string) error {
60+
if _, err := os.Stat(machineStatePath); err != nil {
61+
return err
62+
}
63+
logrus.Info("Saved VZ machine state found, resuming VZ")
64+
if err := vm.RestoreMachineStateFromURL(machineStatePath); err != nil {
65+
return err
66+
}
67+
if err := vm.Resume(); err != nil {
68+
return err
69+
}
70+
// Remove the machine state file after resuming the machine
71+
if err := os.Remove(machineStatePath); err != nil {
72+
// We should log the error but continue the process, because the machine state is already restored
73+
logrus.WithError(err).Errorf("Failed to remove the VZ machine state file %q", machineStatePath)
74+
}
75+
return nil
76+
}

pkg/vz/vm_darwin.go

+11-4
Original file line numberDiff line numberDiff line change
@@ -58,10 +58,17 @@ func startVM(ctx context.Context, driver *driver.BaseDriver) (*virtualMachineWra
5858
if err != nil {
5959
return nil, nil, err
6060
}
61-
62-
err = machine.Start()
63-
if err != nil {
64-
return nil, nil, err
61+
machineStatePath := filepath.Join(driver.Instance.Dir, filenames.VzMachineState)
62+
if err := restoreVM(machine, machineStatePath); err != nil {
63+
if errors.Is(err, os.ErrNotExist) {
64+
logrus.Info("Saved VZ machine state not found, starting VZ")
65+
} else {
66+
logrus.WithError(err).Warn("Failed to restore VZ. Falling back to starting")
67+
}
68+
err = machine.Start()
69+
if err != nil {
70+
return nil, nil, err
71+
}
6572
}
6673

6774
wrapper := &virtualMachineWrapper{VirtualMachine: machine, stopped: false}

pkg/vz/vz_driver_darwin.go

+8
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import (
1717
"github.com/lima-vm/lima/pkg/driver"
1818
"github.com/lima-vm/lima/pkg/limayaml"
1919
"github.com/lima-vm/lima/pkg/reflectutil"
20+
"github.com/lima-vm/lima/pkg/store/filenames"
2021
)
2122

2223
var knownYamlProperties = []string{
@@ -192,6 +193,13 @@ func (l *LimaVzDriver) RunGUI() error {
192193
}
193194

194195
func (l *LimaVzDriver) Stop(_ context.Context) error {
196+
machineStatePath := filepath.Join(l.Instance.Dir, filenames.VzMachineState)
197+
if err := saveVM(l.machine.VirtualMachine, machineStatePath); err != nil {
198+
logrus.WithError(err).Warn("Failed to save VZ. Falling back to shutdown")
199+
} else {
200+
return nil
201+
}
202+
195203
logrus.Info("Shutting down VZ")
196204
canStop := l.machine.CanRequestStop()
197205

0 commit comments

Comments
 (0)