From 0f20b6b81bde625adf4d19d484453351cd62a35a Mon Sep 17 00:00:00 2001 From: Peng Tao Date: Thu, 5 Jul 2018 15:25:19 +0800 Subject: [PATCH] vendor: update govmm changes To include vm factory related commits. Full list: 54caf78 (mine/templating, templating) qmp: add hotplug memory e66a9b4 qemu: add appendMemoryKnobs helper 8aeca15 qmp: add migrate set arguments a03d496 qmp: add set migration capabilities 0ace417 qemu: allow to set migration incoming 723bc5f qemu: allow to create a stopped guest 283d7df qemu: add file backed memory device support Signed-off-by: Peng Tao --- Gopkg.lock | 4 +- Gopkg.toml | 2 +- vendor/github.com/docker/go-units/duration.go | 35 ++++++ vendor/github.com/docker/go-units/ulimit.go | 118 ++++++++++++++++++ vendor/github.com/intel/govmm/qemu/qemu.go | 107 +++++++++++++--- vendor/github.com/intel/govmm/qemu/qmp.go | 53 ++++++++ 6 files changed, 299 insertions(+), 20 deletions(-) create mode 100644 vendor/github.com/docker/go-units/duration.go create mode 100644 vendor/github.com/docker/go-units/ulimit.go diff --git a/Gopkg.lock b/Gopkg.lock index cdcd3bef1a..3f024ffebf 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -91,7 +91,7 @@ [[projects]] name = "github.com/intel/govmm" packages = ["qemu"] - revision = "9cf8ce6c6dda19d4a6d529e73714e231f6156820" + revision = "ff2401825e0930811919c86c36d64b113aa00083" [[projects]] name = "github.com/kata-containers/agent" @@ -263,6 +263,6 @@ [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "a8e90901b945488c3b660e20c076fce3345dba96b4ec15e7ca00b8a06baa16a3" + inputs-digest = "4d57a771261fe6b0e1f86bf2de82f8c39cc0047170f4277754972e2feab4796f" solver-name = "gps-cdcl" solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml index 2959e7013d..1003cdbf29 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -56,7 +56,7 @@ [[constraint]] name = "github.com/intel/govmm" - revision = "9cf8ce6c6dda19d4a6d529e73714e231f6156820" + revision = "ff2401825e0930811919c86c36d64b113aa00083" [[constraint]] name = "github.com/kata-containers/agent" diff --git a/vendor/github.com/docker/go-units/duration.go b/vendor/github.com/docker/go-units/duration.go new file mode 100644 index 0000000000..ba02af26dc --- /dev/null +++ b/vendor/github.com/docker/go-units/duration.go @@ -0,0 +1,35 @@ +// Package units provides helper function to parse and print size and time units +// in human-readable format. +package units + +import ( + "fmt" + "time" +) + +// HumanDuration returns a human-readable approximation of a duration +// (eg. "About a minute", "4 hours ago", etc.). +func HumanDuration(d time.Duration) string { + if seconds := int(d.Seconds()); seconds < 1 { + return "Less than a second" + } else if seconds == 1 { + return "1 second" + } else if seconds < 60 { + return fmt.Sprintf("%d seconds", seconds) + } else if minutes := int(d.Minutes()); minutes == 1 { + return "About a minute" + } else if minutes < 46 { + return fmt.Sprintf("%d minutes", minutes) + } else if hours := int(d.Hours() + 0.5); hours == 1 { + return "About an hour" + } else if hours < 48 { + return fmt.Sprintf("%d hours", hours) + } else if hours < 24*7*2 { + return fmt.Sprintf("%d days", hours/24) + } else if hours < 24*30*2 { + return fmt.Sprintf("%d weeks", hours/24/7) + } else if hours < 24*365*2 { + return fmt.Sprintf("%d months", hours/24/30) + } + return fmt.Sprintf("%d years", int(d.Hours())/24/365) +} diff --git a/vendor/github.com/docker/go-units/ulimit.go b/vendor/github.com/docker/go-units/ulimit.go new file mode 100644 index 0000000000..5ac7fd825f --- /dev/null +++ b/vendor/github.com/docker/go-units/ulimit.go @@ -0,0 +1,118 @@ +package units + +import ( + "fmt" + "strconv" + "strings" +) + +// Ulimit is a human friendly version of Rlimit. +type Ulimit struct { + Name string + Hard int64 + Soft int64 +} + +// Rlimit specifies the resource limits, such as max open files. +type Rlimit struct { + Type int `json:"type,omitempty"` + Hard uint64 `json:"hard,omitempty"` + Soft uint64 `json:"soft,omitempty"` +} + +const ( + // magic numbers for making the syscall + // some of these are defined in the syscall package, but not all. + // Also since Windows client doesn't get access to the syscall package, need to + // define these here + rlimitAs = 9 + rlimitCore = 4 + rlimitCPU = 0 + rlimitData = 2 + rlimitFsize = 1 + rlimitLocks = 10 + rlimitMemlock = 8 + rlimitMsgqueue = 12 + rlimitNice = 13 + rlimitNofile = 7 + rlimitNproc = 6 + rlimitRss = 5 + rlimitRtprio = 14 + rlimitRttime = 15 + rlimitSigpending = 11 + rlimitStack = 3 +) + +var ulimitNameMapping = map[string]int{ + //"as": rlimitAs, // Disabled since this doesn't seem usable with the way Docker inits a container. + "core": rlimitCore, + "cpu": rlimitCPU, + "data": rlimitData, + "fsize": rlimitFsize, + "locks": rlimitLocks, + "memlock": rlimitMemlock, + "msgqueue": rlimitMsgqueue, + "nice": rlimitNice, + "nofile": rlimitNofile, + "nproc": rlimitNproc, + "rss": rlimitRss, + "rtprio": rlimitRtprio, + "rttime": rlimitRttime, + "sigpending": rlimitSigpending, + "stack": rlimitStack, +} + +// ParseUlimit parses and returns a Ulimit from the specified string. +func ParseUlimit(val string) (*Ulimit, error) { + parts := strings.SplitN(val, "=", 2) + if len(parts) != 2 { + return nil, fmt.Errorf("invalid ulimit argument: %s", val) + } + + if _, exists := ulimitNameMapping[parts[0]]; !exists { + return nil, fmt.Errorf("invalid ulimit type: %s", parts[0]) + } + + var ( + soft int64 + hard = &soft // default to soft in case no hard was set + temp int64 + err error + ) + switch limitVals := strings.Split(parts[1], ":"); len(limitVals) { + case 2: + temp, err = strconv.ParseInt(limitVals[1], 10, 64) + if err != nil { + return nil, err + } + hard = &temp + fallthrough + case 1: + soft, err = strconv.ParseInt(limitVals[0], 10, 64) + if err != nil { + return nil, err + } + default: + return nil, fmt.Errorf("too many limit value arguments - %s, can only have up to two, `soft[:hard]`", parts[1]) + } + + if soft > *hard { + return nil, fmt.Errorf("ulimit soft limit must be less than or equal to hard limit: %d > %d", soft, *hard) + } + + return &Ulimit{Name: parts[0], Soft: soft, Hard: *hard}, nil +} + +// GetRlimit returns the RLimit corresponding to Ulimit. +func (u *Ulimit) GetRlimit() (*Rlimit, error) { + t, exists := ulimitNameMapping[u.Name] + if !exists { + return nil, fmt.Errorf("invalid ulimit name %s", u.Name) + } + + return &Rlimit{Type: t, Soft: uint64(u.Soft), Hard: uint64(u.Hard)}, nil +} + +func (u *Ulimit) String() string { + return fmt.Sprintf("%s=%d:%d", u.Name, u.Soft, u.Hard) +} diff --git a/vendor/github.com/intel/govmm/qemu/qemu.go b/vendor/github.com/intel/govmm/qemu/qemu.go index f2f4f5b8b2..41381d80ee 100644 --- a/vendor/github.com/intel/govmm/qemu/qemu.go +++ b/vendor/github.com/intel/govmm/qemu/qemu.go @@ -1126,6 +1126,10 @@ type Memory struct { // MaxMem is the maximum amount of memory that can be made available // to the guest through e.g. hot pluggable memory. MaxMem string + + // Path is the file path of the memory device. It points to a local + // file path used by FileBackedMem. + Path string } // Kernel is the guest kernel configuration structure. @@ -1167,10 +1171,20 @@ type Knobs struct { // MemPrealloc will allocate all the RAM upfront MemPrealloc bool + // FileBackedMem requires Memory.Size and Memory.Path of the VM to + // be set. + FileBackedMem bool + + // FileBackedMemShared will set the FileBackedMem device as shared. + FileBackedMemShared bool + // Mlock will control locking of memory // Only active when Realtime is set to true Mlock bool + // Stopped will not start guest CPU at startup + Stopped bool + // Realtime will enable realtime QEMU Realtime bool } @@ -1180,6 +1194,24 @@ type IOThread struct { ID string } +const ( + // MigrationFD is the migration incoming type based on open file descriptor. + // Skip default 0 so that it must be set on purpose. + MigrationFD = 1 + // MigrationExec is the migration incoming type based on commands. + MigrationExec = 2 +) + +// Incoming controls migration source preparation +type Incoming struct { + // Possible values are MigrationFD, MigrationExec + MigrationType int + // Only valid if MigrationType == MigrationFD + FD *os.File + // Only valid if MigrationType == MigrationExec + Exec string +} + // Config is the qemu configuration structure. // It allows for passing custom settings and parameters to the qemu API. type Config struct { @@ -1231,6 +1263,9 @@ type Config struct { // Bios is the -bios parameter Bios string + // Incoming controls migration source preparation + Incoming Incoming + // fds is a list of open file descriptors to be passed to the spawned qemu process fds []*os.File @@ -1433,23 +1468,7 @@ func (config *Config) appendKernel() { } } -func (config *Config) appendKnobs() { - if config.Knobs.NoUserConfig == true { - config.qemuParams = append(config.qemuParams, "-no-user-config") - } - - if config.Knobs.NoDefaults == true { - config.qemuParams = append(config.qemuParams, "-nodefaults") - } - - if config.Knobs.NoGraphic == true { - config.qemuParams = append(config.qemuParams, "-nographic") - } - - if config.Knobs.Daemonize == true { - config.qemuParams = append(config.qemuParams, "-daemonize") - } - +func (config *Config) appendMemoryKnobs() { if config.Knobs.HugePages == true { if config.Memory.Size != "" { dimmName := "dimm1" @@ -1474,7 +1493,42 @@ func (config *Config) appendKnobs() { config.qemuParams = append(config.qemuParams, "-device") config.qemuParams = append(config.qemuParams, deviceMemParam) } + } else if config.Knobs.FileBackedMem == true { + if config.Memory.Size != "" && config.Memory.Path != "" { + dimmName := "dimm1" + objMemParam := "memory-backend-file,id=" + dimmName + ",size=" + config.Memory.Size + ",mem-path=" + config.Memory.Path + if config.Knobs.FileBackedMemShared == true { + objMemParam += ",share=on" + } + numaMemParam := "node,memdev=" + dimmName + + config.qemuParams = append(config.qemuParams, "-object") + config.qemuParams = append(config.qemuParams, objMemParam) + + config.qemuParams = append(config.qemuParams, "-numa") + config.qemuParams = append(config.qemuParams, numaMemParam) + } } +} + +func (config *Config) appendKnobs() { + if config.Knobs.NoUserConfig == true { + config.qemuParams = append(config.qemuParams, "-no-user-config") + } + + if config.Knobs.NoDefaults == true { + config.qemuParams = append(config.qemuParams, "-nodefaults") + } + + if config.Knobs.NoGraphic == true { + config.qemuParams = append(config.qemuParams, "-nographic") + } + + if config.Knobs.Daemonize == true { + config.qemuParams = append(config.qemuParams, "-daemonize") + } + + config.appendMemoryKnobs() if config.Knobs.Realtime == true { config.qemuParams = append(config.qemuParams, "-realtime") @@ -1495,6 +1549,10 @@ func (config *Config) appendKnobs() { config.qemuParams = append(config.qemuParams, "mlock=off") } } + + if config.Knobs.Stopped == true { + config.qemuParams = append(config.qemuParams, "-S") + } } func (config *Config) appendBios() { @@ -1513,6 +1571,20 @@ func (config *Config) appendIOThreads() { } } +func (config *Config) appendIncoming() { + var uri string + switch config.Incoming.MigrationType { + case MigrationExec: + uri = fmt.Sprintf("exec:%s", config.Incoming.Exec) + case MigrationFD: + chFDs := config.appendFDs([]*os.File{config.Incoming.FD}) + uri = fmt.Sprintf("fd:%d", chFDs[0]) + default: + return + } + config.qemuParams = append(config.qemuParams, "-S", "-incoming", uri) +} + // LaunchQemu can be used to launch a new qemu instance. // // The Config parameter contains a set of qemu parameters and settings. @@ -1537,6 +1609,7 @@ func LaunchQemu(config Config, logger QMPLog) (string, error) { config.appendKernel() config.appendBios() config.appendIOThreads() + config.appendIncoming() if err := config.appendCPUs(); err != nil { return "", err diff --git a/vendor/github.com/intel/govmm/qemu/qmp.go b/vendor/github.com/intel/govmm/qemu/qmp.go index 8888ad4d4a..37334e99e2 100644 --- a/vendor/github.com/intel/govmm/qemu/qmp.go +++ b/vendor/github.com/intel/govmm/qemu/qmp.go @@ -828,3 +828,56 @@ func (q *QMP) ExecuteQueryHotpluggableCPUs(ctx context.Context) ([]HotpluggableC return cpus, nil } + +// ExecSetMigrationCaps sets migration capabilities +func (q *QMP) ExecSetMigrationCaps(ctx context.Context, caps []map[string]interface{}) error { + args := map[string]interface{}{ + "capabilities": caps, + } + + return q.executeCommand(ctx, "migrate-set-capabilities", args, nil) +} + +// ExecSetMigrateArguments sets the command line used for migration +func (q *QMP) ExecSetMigrateArguments(ctx context.Context, url string) error { + args := map[string]interface{}{ + "uri": url, + } + + return q.executeCommand(ctx, "migrate", args, nil) +} + +// ExecHotplugMemory adds size of MiB memory to the guest +func (q *QMP) ExecHotplugMemory(ctx context.Context, qomtype, id, mempath string, size int) error { + args := map[string]interface{}{ + "qom-type": qomtype, + "id": id, + "props": map[string]interface{}{"size": uint64(size) << 20}, + } + if mempath != "" { + args["mem-path"] = mempath + } + err := q.executeCommand(ctx, "object-add", args, nil) + if err != nil { + return err + } + + defer func() { + if err != nil { + q.cfg.Logger.Errorf("Unable to hotplug memory device: %v", err) + err = q.executeCommand(ctx, "object-del", map[string]interface{}{"id": id}, nil) + if err != nil { + q.cfg.Logger.Warningf("Unable to clean up memory object: %v", err) + } + } + }() + + args = map[string]interface{}{ + "driver": "pc-dimm", + "id": "dimm" + id, + "memdev": id, + } + err = q.executeCommand(ctx, "device_add", args, nil) + + return err +}