Skip to content

Commit

Permalink
reconfiguring vm snapshot/migrate functionality, recategorizing some …
Browse files Browse the repository at this point in the history
…logging
  • Loading branch information
Wright committed Apr 12, 2024
1 parent f0b7fcc commit d2606cf
Show file tree
Hide file tree
Showing 4 changed files with 57 additions and 93 deletions.
4 changes: 2 additions & 2 deletions cmd/minimega/command_socket.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ func commandSocketStart() {
log.Error("commandSocketStart: accept: %v", err)
continue
}
log.Infoln("client connected")
log.Debugln("client connected")

go commandSocketHandle(conn)
}
Expand Down Expand Up @@ -185,7 +185,7 @@ func commandSocketHandle(c net.Conn) {

// finally, log the error, if there was one
if err == nil || err == io.EOF {
log.Infoln("command client disconnected")
log.Debugln("command client disconnected")
} else if err != nil && strings.Contains(err.Error(), "write: broken pipe") {
log.Infoln("command client disconnected without waiting for responses")
} else if err != nil {
Expand Down
78 changes: 23 additions & 55 deletions cmd/minimega/kvm.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ type KVMConfig struct {

// Assign a migration image, generated by a previously saved VM to boot
// with. By default, images are read from the files directory as specified
// with -filepath. This can be overridden by using an absolute path.
// with -filepath. This can be overriden by using an absolute path.
// Migration images should be booted with a kernel/initrd, disk, or cdrom.
// Use 'vm migrate' to generate migration images from running VMs.
//
Expand Down Expand Up @@ -180,7 +180,7 @@ type KVMConfig struct {
Disks DiskConfigs

// If true will use xHCI USB controller. Otherwise will use EHCI.
// EHCI does not support USB 3.0, but may be used for backwards compatibility.
// EHCI does not support USB 3.0, but may be used for backwards compatability.
//
// Default: true
UsbUseXHCI bool
Expand All @@ -189,12 +189,6 @@ type KVMConfig struct {
// socket at the path provided
TpmSocketPath string

// Enables bidirectional copy paste instead of basic pasting into VM.
// Requires QEMU 6.1+ compiled with qemu-vdagent chardev and for spice-vdagent to be installed on VM.
//
// Default: false
BidirectionalCopyPaste bool

// Add additional arguments to be passed to the QEMU instance. For example:
//
// vm config qemu-append -serial tcp:localhost:4001
Expand Down Expand Up @@ -552,7 +546,6 @@ func (vm *KVMConfig) String() string {
fmt.Fprintf(w, "Sockets:\t%v\n", vm.Sockets)
fmt.Fprintf(w, "VGA:\t%v\n", vm.Vga)
fmt.Fprintf(w, "Usb Use XHCI:\t%v\n", vm.UsbUseXHCI)
fmt.Fprintf(w, "Bidirectional Copy Paste:\t%v\n", vm.BidirectionalCopyPaste)
fmt.Fprintf(w, "TPM Socket: \t%v\n", vm.TpmSocketPath)
w.Flush()
fmt.Fprintln(&o)
Expand Down Expand Up @@ -581,7 +574,7 @@ func (vm *KvmVM) Save(filename string) error {
vm.lock.Lock()
defer vm.lock.Unlock()

fp := fmt.Sprintf("%s/%s", *f_base, strconv.Itoa(vm.ID))
fp := fmt.Sprintf("%s/%s", *f_base, strconv.Itoa(vm.ID))

r, err := vm.q.QueryBlock()
if err != nil {
Expand All @@ -593,27 +586,32 @@ func (vm *KvmVM) Save(filename string) error {
json.Unmarshal(rString, &v)

// find the device name
var device string
var device []string
for _, dev := range v {
if dev.Inserted != nil {
if strings.HasPrefix(dev.Inserted.File, fp) {
device = dev.Device
break
device = append(device, dev.Device)
}
}
}

err = vm.q.SaveDisk(filename, device)
if err != nil {
return err
}
for d, devi := range device {
if d > 0 {
filename = fmt.Sprintf("%s.%s",filename,strconv.Itoa(d))
}
log.Info("Saving disk %s to %s", devi, filename)

// wait for drive-backup to finish
for {
if r, _ := vm.q.QueryBlockJobs(); len(r) == 0 {
break
if err := vm.q.SaveDisk(filename, devi); err != nil {
return err
}
time.Sleep(time.Second * 1)

// wait for drive-backup to finish
for {
if r, _ := vm.q.QueryBlockJobs(); len(r) == 0 {
break
}
time.Sleep(time.Second * 1)
}
}

return err
Expand Down Expand Up @@ -779,16 +777,6 @@ func (vm *KvmVM) connectVNC() error {
for {
msg, err := vnc.ReadClientMessage(tee)
if err == nil {
// for cut text, send text immediately as string if not bi-directional
if cut, ok := msg.(*vnc.ClientCutText); ok && !vm.BidirectionalCopyPaste {
if cut.Length > 0 {
log.Info("sending text for ClientCutText: %s", cut.Text)
err = ns.Player.PlaybackString(vm.Name, vm.vncShim.Addr().String(), string(cut.Text))
if err != nil {
log.Warnln(err)
}
}
}
ns.Recorder.Route(vm.GetName(), msg)
continue
}
Expand Down Expand Up @@ -943,17 +931,6 @@ func (vm *KvmVM) launch() error {
var sErr bytes.Buffer

vmConfig := VMConfig{BaseConfig: vm.BaseConfig, KVMConfig: vm.KVMConfig}

// if using bidirectionalCopyPaste, error out if dependencies aren't met
if vmConfig.BidirectionalCopyPaste {
if err := checkVersion("qemu", MIN_QEMU_COPY_PASTE, qemuVersion); err != nil {
return fmt.Errorf("bidirectional-copy-paste not supported. Please disable: %v", err)
}
if err := checkQemuChardev("qemu-vdagent"); err != nil {
return fmt.Errorf("bidirectional-copy-paste not supported. Please disable: %v", err)
}
}

args := vmConfig.qemuArgs(vm.ID, vm.instancePath)
args = vmConfig.applyQemuOverrides(args)
log.Debug("final qemu args: %#v", args)
Expand Down Expand Up @@ -990,8 +967,8 @@ func (vm *KvmVM) launch() error {
if err := vm.connectQMP(); err != nil {
// Failed to connect to qmp so clean up the process
cmd.Process.Kill()
// Qemu stderr likely contains reason
return vm.setErrorf("unable to connect to qmp socket: %v. qemu output: %v", err, sErr.String())

return vm.setErrorf("unable to connect to qmp socket: %v", err)
}

go vm.qmpLogger()
Expand Down Expand Up @@ -1351,7 +1328,7 @@ func (vm VMConfig) qemuArgs(id int, vmPath string) []string {

if vm.TpmSocketPath != "" {
args = append(args, "-chardev")
args = append(args, fmt.Sprintf("socket,id=chrtpm,path=%v", vm.TpmSocketPath))
args = append(args, fmt.Sprintf("socket,id=chrtpm,path=%v,nowait", vm.TpmSocketPath))
args = append(args, "-tpmdev")
args = append(args, "emulator,id=tpm0,chardev=chrtpm")
args = append(args, "-device")
Expand Down Expand Up @@ -1503,15 +1480,6 @@ func (vm VMConfig) qemuArgs(id int, vmPath string) []string {
args = append(args, fmt.Sprintf("virtserialport,bus=virtio-serial%v.0,chardev=charvserialCC,id=charvserialCC,name=cc", virtioPort))
}

if vm.BidirectionalCopyPaste {
addVirtioDevice()

args = append(args, "-chardev")
args = append(args, "qemu-vdagent,id=vdagent,clipboard=on")
args = append(args, "-device")
args = append(args, fmt.Sprintf("virtserialport,bus=virtio-serial%v.0,chardev=vdagent,name=com.redhat.spice.0", virtioPort))
}

if vm.VirtioPorts != "" {
names := []string{}

Expand Down
6 changes: 3 additions & 3 deletions cmd/minimega/namespace.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2015-2023 National Technology & Engineering Solutions of Sandia, LLC (NTESS).
// Copyright 2015-2021 National Technology & Engineering Solutions of Sandia, LLC (NTESS).
// Under the terms of Contract DE-NA0003525 with NTESS, the U.S. Government retains certain
// rights in this software.

Expand Down Expand Up @@ -913,7 +913,7 @@ func SetNamespace(name string) error {
return errors.New("namespace name cannot be the empty string")
}

log.Info("setting active namespace: %v", name)
log.Debug("setting active namespace: %v", name)

if name == namespace {
return fmt.Errorf("already in namespace: %v", name)
Expand All @@ -929,7 +929,7 @@ func RevertNamespace(old, curr *Namespace) {
namespaceLock.Lock()
defer namespaceLock.Unlock()

log.Info("reverting to namespace: %v", old)
log.Debug("reverting to namespace: %v", old)

// This is very odd and should *never* happen unless something has gone
// horribly wrong.
Expand Down
62 changes: 29 additions & 33 deletions cmd/minimega/vm_cli.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2015-2023 National Technology & Engineering Solutions of Sandia, LLC (NTESS).
// Copyright 2015-2021 National Technology & Engineering Solutions of Sandia, LLC (NTESS).
// Under the terms of Contract DE-NA0003525 with NTESS, the U.S. Government retains certain
// rights in this software.

Expand Down Expand Up @@ -47,7 +47,7 @@ info include:
- ip : IPv4 address
- ip6 : IPv6 address
- bandwidth : stats regarding bandwidth usage
- qos : quality-of-service constraints on network interfaces
- qos : quality-of-service contraints on network interfaces
- tags : any additional information attached to the VM
Additional fields are available for KVM-based VMs:
Expand Down Expand Up @@ -110,7 +110,7 @@ naming scheme:
Note: VM names cannot be integers or reserved words (e.g. "all").
Users may specify a saved config explicitly rather than use the current one, for
Users may specify a saved config explicity rather than use the current one, for
example:
vm config save endpoint
Expand Down Expand Up @@ -250,7 +250,7 @@ To add a network connection, you can specify the same options as you do when you
connections via vm config when launching VMs. See "vm config net" for more details.
You will need to specify the VLAN of which the interface is a member. Optionally, you may
specify the bridge the interface will be connected on. You may also specify a MAC address for
specify the brige the interface will be connected on. You may also specify a MAC address for
the interface. Finally, you may also specify the network device for qemu to use. By default,
"e1000" is used. The order is:
Expand Down Expand Up @@ -363,26 +363,29 @@ You can also specify the maximum dimension:
{ // vm snapshot
HelpShort: "write VM state and disk to file",
HelpLong: `
Write VM state (migrate) and disk to file, which can later be booted with 'vm config
migrate ...' and 'vm config disk ...', respectively.
Write VM disk to file, which can later be booted with
'vm config disk ...'.
Saved migrate and disk files are written to the files directory as specified with
-filepath. On success, a call to snapshot a VM will return immediately. You can
Saved disk files are written to the files directory as specified with
filepath. Multiple disks will be handled automatically by appending a unique value.
On success, a call to snapshot a VM will return immediately. You can
check the status of in-flight snapshots by invoking vm snapshot with no arguments.`,
Patterns: []string{
"vm snapshot",
"vm snapshot <vm name> <state filename> <disk filename>",
"vm snapshot <vm name> <filename>",
},
Call: wrapVMTargetCLI(cliVMSnapshot),
Suggest: wrapVMSuggest(VM_ANY_STATE, false),
},
{ // vm migrate
HelpShort: "write VM state to disk",
HelpLong: `
Migrate runtime state of a VM to disk, which can later be booted with vm config
migrate.
Migrate runtime state of a VM to disk, which can later be booted with 'vm config
migrate ...' and 'vm config disk ...', respectively. The migrate file may have a
depencendy with the corresponding disk snapshot image.
Migration files are written to the files directory as specified with -filepath.
Migration and disk files are written to the files directory as specified with filename.
Migrate file will have .migrate appended to filename, while drive will hold filename provided.
On success, a call to migrate a VM will return immediately. You can check the
status of in-flight migrations by invoking vm migrate with no arguments.`,
Patterns: []string{
Expand Down Expand Up @@ -646,7 +649,7 @@ func cliVMTag(ns *Namespace, c *minicli.Command, resp *minicli.Response) error {

func cliClearVMTag(ns *Namespace, c *minicli.Command, resp *minicli.Response) error {
// Get the specified tag name or use Wildcard if not provided
key, ok := c.StringArgs["tag"]
key, ok := c.StringArgs["key"]
if !ok {
key = Wildcard
}
Expand Down Expand Up @@ -820,8 +823,7 @@ func cliVMSnapshot(ns *Namespace, c *minicli.Command, resp *minicli.Response) er
}

// save disk
filename := c.StringArgs["disk"]

filename := c.StringArgs["filename"]
if !filepath.IsAbs(filename) {
filename = filepath.Join(*f_iomBase, filename)
}
Expand All @@ -838,22 +840,7 @@ func cliVMSnapshot(ns *Namespace, c *minicli.Command, resp *minicli.Response) er
return err
}

// save state
filename = c.StringArgs["state"]

if !filepath.IsAbs(filename) {
filename = filepath.Join(*f_iomBase, filename)
}

if _, err := os.Stat(filepath.Dir(filename)); os.IsNotExist(err) {
if err := os.MkdirAll(filepath.Dir(filename), 0755); err != nil {
return err
}
} else if err != nil {
return err
}

return vm.Migrate(filename)
return nil
}

func cliVMMigrate(ns *Namespace, c *minicli.Command, resp *minicli.Response) error {
Expand Down Expand Up @@ -884,13 +871,12 @@ func cliVMMigrate(ns *Namespace, c *minicli.Command, resp *minicli.Response) err
return err
}

//save disk (a migrate file is often useless without the state of the drive)
fname := c.StringArgs["filename"]

if !filepath.IsAbs(fname) {
// TODO: should we write to the VM directory instead?
fname = filepath.Join(*f_iomBase, fname)
}

if _, err := os.Stat(filepath.Dir(fname)); os.IsNotExist(err) {
if err := os.MkdirAll(filepath.Dir(fname), 0755); err != nil {
return err
Expand All @@ -899,6 +885,16 @@ func cliVMMigrate(ns *Namespace, c *minicli.Command, resp *minicli.Response) err
return err
}

//Saving disk
if err := vm.Save(fname); err != nil {
return err
}

//Saving memory/migrate
fname = fmt.Sprintf("%s.migrate",fname)

log.Info("Migrating to file %s", fname)

return vm.Migrate(fname)
}

Expand Down

0 comments on commit d2606cf

Please sign in to comment.