Skip to content

Commit

Permalink
VM: Send two ACPI events on shutdown, fix timeout errors and add prog…
Browse files Browse the repository at this point in the history
…ress tracking to export (from Incus) (#14850)

Cherry-picks from lxc/incus#1043.

Closes #14816.
  • Loading branch information
tomponline authored Jan 24, 2025
2 parents 84be4ee + bec5351 commit a0a3c77
Show file tree
Hide file tree
Showing 4 changed files with 35 additions and 9 deletions.
10 changes: 9 additions & 1 deletion lxd/images.go
Original file line number Diff line number Diff line change
Expand Up @@ -440,11 +440,19 @@ func imgPostInstanceInfo(s *state.State, r *http.Request, req api.ImagesPost, op
writer = io.MultiWriter(imageProgressWriter, sha256)
}

// Tracker instance for the export phase.
tracker := &ioprogress.ProgressTracker{
Handler: func(value, speed int64) {
shared.SetProgressMetadata(metadata, "create_image_from_container_pack", "Exporting", value, 0, 0)
_ = op.UpdateMetadata(metadata)
},
}

// Export instance to writer.
var meta api.ImageMetadata

writer = shared.NewQuotaWriter(writer, budget)
meta, err = c.Export(writer, req.Properties, req.ExpiresAt)
meta, err = c.Export(writer, req.Properties, req.ExpiresAt, tracker)

// Get ExpiresAt
if meta.ExpiryDate != 0 {
Expand Down
3 changes: 2 additions & 1 deletion lxd/instance/drivers/driver_lxc.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ import (
"github.com/canonical/lxd/lxd/util"
"github.com/canonical/lxd/shared"
"github.com/canonical/lxd/shared/api"
"github.com/canonical/lxd/shared/ioprogress"
"github.com/canonical/lxd/shared/logger"
"github.com/canonical/lxd/shared/netutils"
"github.com/canonical/lxd/shared/osarch"
Expand Down Expand Up @@ -4905,7 +4906,7 @@ func (d *lxc) Update(args db.InstanceArgs, userRequested bool) error {
}

// Export backs up the instance.
func (d *lxc) Export(w io.Writer, properties map[string]string, expiration time.Time) (api.ImageMetadata, error) {
func (d *lxc) Export(w io.Writer, properties map[string]string, expiration time.Time, tracker *ioprogress.ProgressTracker) (api.ImageMetadata, error) {
ctxMap := logger.Ctx{
"created": d.creationDate,
"ephemeral": d.ephemeral,
Expand Down
28 changes: 22 additions & 6 deletions lxd/instance/drivers/driver_qemu.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ import (
"github.com/canonical/lxd/shared"
"github.com/canonical/lxd/shared/api"
"github.com/canonical/lxd/shared/entity"
"github.com/canonical/lxd/shared/ioprogress"
"github.com/canonical/lxd/shared/logger"
"github.com/canonical/lxd/shared/osarch"
"github.com/canonical/lxd/shared/revert"
Expand Down Expand Up @@ -723,6 +724,21 @@ func (d *qemu) Shutdown(timeout time.Duration) error {
return err
}

// Wait 500ms for the first event to be received by the guest.
time.Sleep(500 * time.Millisecond)

// Send a second system_powerdown command (required to get Windows to shutdown).
err = monitor.Powerdown()
if err != nil {
if err == qmp.ErrMonitorDisconnect {
op.Done(nil)
return nil
}

op.Done(err)
return err
}

d.logger.Debug("Shutdown request sent to instance")

ctx, cancel := context.WithTimeout(context.Background(), timeout)
Expand Down Expand Up @@ -2321,7 +2337,7 @@ func (d *qemu) deviceDetachPath(deviceName string) error {
}

if time.Now().After(waitUntil) {
return fmt.Errorf("Failed to detach path device after %v: %w", waitDuration, err)
return fmt.Errorf("Failed to detach path device after %v", waitDuration)
}
}

Expand Down Expand Up @@ -2362,7 +2378,7 @@ func (d *qemu) deviceDetachBlockDevice(deviceName string) error {
}

if time.Now().After(waitUntil) {
return fmt.Errorf("Failed to detach block device after %v: %w", waitDuration, err)
return fmt.Errorf("Failed to detach block device after %v", waitDuration)
}
}

Expand Down Expand Up @@ -2559,7 +2575,7 @@ func (d *qemu) deviceDetachNIC(deviceName string) error {
}

if time.Now().After(waitUntil) {
return fmt.Errorf("Failed to detach NIC after %v: %w", waitDuration, err)
return fmt.Errorf("Failed to detach NIC after %v", waitDuration)
}

d.logger.Debug("Waiting for NIC device to be detached", logger.Ctx{"device": deviceName})
Expand Down Expand Up @@ -6201,7 +6217,7 @@ func (d *qemu) delete(force bool) error {
}

// Export publishes the instance.
func (d *qemu) Export(w io.Writer, properties map[string]string, expiration time.Time) (api.ImageMetadata, error) {
func (d *qemu) Export(w io.Writer, properties map[string]string, expiration time.Time, tracker *ioprogress.ProgressTracker) (api.ImageMetadata, error) {
ctxMap := logger.Ctx{
"created": d.creationDate,
"ephemeral": d.ephemeral,
Expand Down Expand Up @@ -6413,7 +6429,7 @@ func (d *qemu) Export(w io.Writer, properties map[string]string, expiration time
// Convert to qcow2 image.
cmd := []string{
"nice", "-n19", // Run with low priority to reduce CPU impact on other processes.
"qemu-img", "convert", "-f", "raw", "-O", "qcow2", "-c",
"qemu-img", "convert", "-p", "-f", "raw", "-O", "qcow2", "-c",
}

revert := revert.New()
Expand All @@ -6436,7 +6452,7 @@ func (d *qemu) Export(w io.Writer, properties map[string]string, expiration time

cmd = append(cmd, mountInfo.DiskPath, fPath)

_, err = apparmor.QemuImg(d.state.OS, cmd, mountInfo.DiskPath, fPath, nil)
_, err = apparmor.QemuImg(d.state.OS, cmd, mountInfo.DiskPath, fPath, tracker)
if err != nil {
return meta, fmt.Errorf("Failed converting instance to qcow2: %w", err)
}
Expand Down
3 changes: 2 additions & 1 deletion lxd/instance/instance_interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"github.com/canonical/lxd/lxd/metrics"
"github.com/canonical/lxd/lxd/operations"
"github.com/canonical/lxd/shared/api"
"github.com/canonical/lxd/shared/ioprogress"
)

// HookStart hook used when instance has started.
Expand Down Expand Up @@ -99,7 +100,7 @@ type Instance interface {
Update(newConfig db.InstanceArgs, userRequested bool) error

Delete(force bool) error
Export(w io.Writer, properties map[string]string, expiration time.Time) (api.ImageMetadata, error)
Export(w io.Writer, properties map[string]string, expiration time.Time, tracker *ioprogress.ProgressTracker) (api.ImageMetadata, error)

// Live configuration.
CGroup() (*cgroup.CGroup, error)
Expand Down

0 comments on commit a0a3c77

Please sign in to comment.