Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Snapshot and migrate functionality. #1540

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
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
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
30 changes: 18 additions & 12 deletions cmd/minimega/kvm.go
Original file line number Diff line number Diff line change
Expand Up @@ -593,27 +593,33 @@ 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))
//Appending drive number if there are multiple drives.
}
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
}

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

return err
Expand Down
4 changes: 2 additions & 2 deletions cmd/minimega/namespace.go
Original file line number Diff line number Diff line change
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
81 changes: 28 additions & 53 deletions cmd/minimega/vm_cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -361,29 +361,31 @@ You can also specify the maximum dimension:
Suggest: wrapVMSuggest(VM_ANY_STATE, false),
},
{ // vm snapshot
HelpShort: "write VM state and disk to file",
HelpShort: "create a new snapshot for the VM",
HelpLong: `
Write VM state (migrate) and disk to file, which can later be booted with 'vm config
migrate ...' and 'vm config disk ...', respectively.
Creates a point-in-time snapshot for the VM. This snapshot is backed by the original image and contains any changes
since boot. The snapshot can later be used 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
check the status of in-flight snapshots by invoking vm snapshot with no arguments.`,
Saved disk files are written to the files directory as specified with
<filename>. Multiple disks will be handled automatically by appending a unique value.
On success, a call to snapshot a VM will return immediately.`,
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",
HelpShort: "write VM state and disk to file",
HelpLong: `
Migrate runtime state of a VM to disk, which can later be booted with vm config
migrate.
Migrate runtime state and disk of a VM to files, which can later be booted with 'vm config
migrate ...' and 'vm config disk ...', respectively. The migrate file may have a
dependency with the corresponding disk snapshot image.

Migration files are written to the files directory as specified with -filepath.
On success, a call to migrate a VM will return immediately. You can check the
Migration and disk files are written to the files directory as specified with <filename>.
Migrate file will have .migrate appended to filename, while drive(s) 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{
"vm migrate",
Expand Down Expand Up @@ -792,35 +794,13 @@ func cliVMScreenshot(ns *Namespace, c *minicli.Command, resp *minicli.Response)
}

func cliVMSnapshot(ns *Namespace, c *minicli.Command, resp *minicli.Response) error {
if _, ok := c.StringArgs["vm"]; !ok { // report current status
resp.Header = []string{"id", "name", "status", "complete (%)"}

for _, vm := range ns.FindKvmVMs() {
status, complete, err := vm.QueryMigrate()
if err != nil {
return err
}
if status == "" {
continue
}

resp.Tabular = append(resp.Tabular, []string{
fmt.Sprintf("%v", vm.GetID()),
vm.GetName(),
status,
fmt.Sprintf("%.2f", complete)})
}

return nil
}

vm, err := ns.FindKvmVM(c.StringArgs["vm"])
if err != nil {
return err
}

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

if !filepath.IsAbs(filename) {
filename = filepath.Join(*f_iomBase, filename)
Expand All @@ -838,22 +818,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,10 +849,10 @@ 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)
}

Expand All @@ -899,6 +864,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
2 changes: 1 addition & 1 deletion cmd/minirouter/ip.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ func ipDel(idx int, ip string) error {
return fmt.Errorf("%v: %v", err, string(out))
}

IPs[idx] = append(IPs[idx][:len(IPs[idx])-1])
IPs[idx] = IPs[idx][:len(IPs[idx])-1]
if len(IPs[idx]) == 0 {
delete(IPs, idx)
}
Expand Down