[lxc-devel] [lxd/master] Improve progress tracking
stgraber on Github
lxc-bot at linuxcontainers.org
Wed Nov 16 17:13:55 UTC 2016
A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 301 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20161116/158be804/attachment.bin>
-------------- next part --------------
From b0777b2f8944da913837c5f08b4b49165f56190d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at ubuntu.com>
Date: Wed, 16 Nov 2016 11:57:30 -0500
Subject: [PATCH 1/2] client: Rework progress handling
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Use one common approach to progress tracking and reporting.
Signed-off-by: Stéphane Graber <stgraber at ubuntu.com>
---
lxc/image.go | 34 ++++++++++-----------
lxc/init.go | 13 ++++----
lxc/launch.go | 4 ++-
lxc/main.go | 38 +++++++++++++++++++++++
po/lxd.pot | 96 +++++++++++++++++++++++++++++------------------------------
5 files changed, 111 insertions(+), 74 deletions(-)
diff --git a/lxc/image.go b/lxc/image.go
index c032d5c..54a4be0 100644
--- a/lxc/image.go
+++ b/lxc/image.go
@@ -276,14 +276,12 @@ func (c *imageCmd) run(config *lxd.Config, args []string) error {
return err
}
- progressHandler := func(progress string) {
- fmt.Printf(i18n.G("Copying the image: %s")+"\r", progress)
- }
-
- err = d.CopyImage(inName, dest, c.copyAliases, c.addAliases, c.publicImage, c.autoUpdate, progressHandler)
+ progress := ProgressRenderer{Format: i18n.G("Copying the image: %s")}
+ err = d.CopyImage(inName, dest, c.copyAliases, c.addAliases, c.publicImage, c.autoUpdate, progress.Update)
if err == nil {
- fmt.Println(i18n.G("Image copied successfully!"))
+ progress.Done(i18n.G("Image copied successfully!"))
}
+
return err
case "delete":
@@ -417,29 +415,29 @@ func (c *imageCmd) run(config *lxd.Config, args []string) error {
return err
}
- handler := func(percent int) {
- fmt.Printf(i18n.G("Transferring image: %d%%")+"\r", percent)
- if percent == 100 {
- fmt.Printf("\n")
- }
- }
-
if strings.HasPrefix(imageFile, "https://") {
- progressHandler := func(progress string) {
- fmt.Printf(i18n.G("Importing the image: %s")+"\r", progress)
+ progress := ProgressRenderer{Format: i18n.G("Importing the image: %s")}
+ fingerprint, err = d.PostImageURL(imageFile, properties, c.publicImage, c.addAliases, progress.Update)
+ if err == nil {
+ progress.Done(fmt.Sprintf(i18n.G("Image imported with fingerprint: %s"), fingerprint))
}
-
- fingerprint, err = d.PostImageURL(imageFile, properties, c.publicImage, c.addAliases, progressHandler)
} else if strings.HasPrefix(imageFile, "http://") {
return fmt.Errorf(i18n.G("Only https:// is supported for remote image import."))
} else {
+ progress := ProgressRenderer{Format: i18n.G("Transferring image: %s")}
+ handler := func(percent int) {
+ progress.Update(fmt.Sprintf("%d%%", percent))
+ }
+
fingerprint, err = d.PostImage(imageFile, rootfsFile, properties, c.publicImage, c.addAliases, handler)
+ if err == nil {
+ progress.Done(fmt.Sprintf(i18n.G("Image imported with fingerprint: %s"), fingerprint))
+ }
}
if err != nil {
return err
}
- fmt.Printf(i18n.G("Image imported with fingerprint: %s")+"\n", fingerprint)
return nil
diff --git a/lxc/init.go b/lxc/init.go
index 454d301..306c932 100644
--- a/lxc/init.go
+++ b/lxc/init.go
@@ -205,9 +205,11 @@ func (c *initCmd) run(config *lxd.Config, args []string) error {
return err
}
- c.initProgressTracker(d, resp.Operation)
+ progress := ProgressRenderer{}
+ c.initProgressTracker(d, &progress, resp.Operation)
err = d.WaitForSuccess(resp.Operation)
+ progress.Done("")
if err != nil {
return err
@@ -233,7 +235,8 @@ func (c *initCmd) run(config *lxd.Config, args []string) error {
return nil
}
-func (c *initCmd) initProgressTracker(d *lxd.Client, operation string) {
+func (c *initCmd) initProgressTracker(d *lxd.Client, progress *ProgressRenderer, operation string) {
+ progress.Format = i18n.G("Retrieving image: %s")
handler := func(msg interface{}) {
if msg == nil {
return
@@ -264,11 +267,7 @@ func (c *initCmd) initProgressTracker(d *lxd.Client, operation string) {
opMd := md["metadata"].(map[string]interface{})
_, ok := opMd["download_progress"]
if ok {
- fmt.Printf(i18n.G("Retrieving image: %s")+"\r", opMd["download_progress"].(string))
- }
-
- if opMd["download_progress"].(string) == "100%" {
- fmt.Printf("\n")
+ progress.Update(opMd["download_progress"].(string))
}
}
go d.Monitor([]string{"operation"}, handler)
diff --git a/lxc/launch.go b/lxc/launch.go
index 439302c..7c7cf7b 100644
--- a/lxc/launch.go
+++ b/lxc/launch.go
@@ -103,7 +103,8 @@ func (c *launchCmd) run(config *lxd.Config, args []string) error {
return err
}
- c.init.initProgressTracker(d, resp.Operation)
+ progress := ProgressRenderer{}
+ c.init.initProgressTracker(d, &progress, resp.Operation)
if name == "" {
op, err := resp.MetadataAsOperation()
@@ -136,6 +137,7 @@ func (c *launchCmd) run(config *lxd.Config, args []string) error {
if err = d.WaitForSuccess(resp.Operation); err != nil {
return err
}
+ progress.Done("")
c.init.checkNetwork(d, name)
diff --git a/lxc/main.go b/lxc/main.go
index 3dc7378..b50904e 100644
--- a/lxc/main.go
+++ b/lxc/main.go
@@ -308,3 +308,41 @@ func execIfAliases(config *lxd.Config, origArgs []string) {
fmt.Fprintf(os.Stderr, i18n.G("processing aliases failed %s\n"), ret)
os.Exit(5)
}
+
+type ProgressRenderer struct {
+ Format string
+
+ maxLength int
+}
+
+func (p *ProgressRenderer) Done(msg string) {
+ if msg != "" {
+ msg += "\n"
+ }
+
+ if len(msg) > p.maxLength {
+ p.maxLength = len(msg)
+ } else {
+ fmt.Printf("%s\r", strings.Repeat(" ", p.maxLength))
+ }
+
+ fmt.Print(msg)
+}
+
+func (p *ProgressRenderer) Update(status string) {
+ msg := "%s"
+ if p.Format != "" {
+ msg = p.Format
+ }
+
+ msg = fmt.Sprintf(msg, status)
+ msg += "\r"
+
+ if len(msg) > p.maxLength {
+ p.maxLength = len(msg)
+ } else {
+ fmt.Printf("%s\r", strings.Repeat(" ", p.maxLength))
+ }
+
+ fmt.Print(msg)
+}
diff --git a/po/lxd.pot b/po/lxd.pot
index 61280f6..581dce7 100644
--- a/po/lxd.pot
+++ b/po/lxd.pot
@@ -7,7 +7,7 @@
msgid ""
msgstr "Project-Id-Version: lxd\n"
"Report-Msgid-Bugs-To: lxc-devel at lists.linuxcontainers.org\n"
- "POT-Creation-Date: 2016-11-04 21:31-0400\n"
+ "POT-Creation-Date: 2016-11-16 12:13-0500\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL at ADDRESS>\n"
"Language-Team: LANGUAGE <LL at li.org>\n"
@@ -100,7 +100,7 @@ msgid "### This is a yaml representation of the profile.\n"
"### Note that the name is shown but cannot be changed"
msgstr ""
-#: lxc/image.go:617
+#: lxc/image.go:615
#, c-format
msgid "%s (%d more)"
msgstr ""
@@ -113,11 +113,11 @@ msgstr ""
msgid "(none)"
msgstr ""
-#: lxc/image.go:638 lxc/image.go:680
+#: lxc/image.go:636 lxc/image.go:678
msgid "ALIAS"
msgstr ""
-#: lxc/image.go:642
+#: lxc/image.go:640
msgid "ARCH"
msgstr ""
@@ -134,7 +134,7 @@ msgstr ""
msgid "Admin password for %s: "
msgstr ""
-#: lxc/image.go:365
+#: lxc/image.go:363
msgid "Aliases:"
msgstr ""
@@ -142,12 +142,12 @@ msgstr ""
msgid "An environment variable of the form HOME=/home/foo"
msgstr ""
-#: lxc/image.go:348 lxc/info.go:93
+#: lxc/image.go:346 lxc/info.go:93
#, c-format
msgid "Architecture: %s"
msgstr ""
-#: lxc/image.go:369
+#: lxc/image.go:367
#, c-format
msgid "Auto update: %s"
msgstr ""
@@ -214,7 +214,7 @@ msgstr ""
msgid "Config key/value to apply to the new container"
msgstr ""
-#: lxc/config.go:530 lxc/config.go:595 lxc/image.go:734 lxc/network.go:342 lxc/profile.go:218
+#: lxc/config.go:530 lxc/config.go:595 lxc/image.go:732 lxc/network.go:342 lxc/profile.go:218
#, c-format
msgid "Config parsing error: %s"
msgstr ""
@@ -227,7 +227,7 @@ msgstr ""
msgid "Container name is mandatory"
msgstr ""
-#: lxc/copy.go:140 lxc/copy.go:241 lxc/init.go:227
+#: lxc/copy.go:140 lxc/copy.go:241 lxc/init.go:229
#, c-format
msgid "Container name is: %s"
msgstr ""
@@ -247,7 +247,7 @@ msgid "Copy containers within or in between lxd instances.\n"
"lxc copy [remote:]<source container> [[remote:]<destination container>] [--ephemeral|e] [--profile|-p <profile>...] [--config|-c <key=value>...]"
msgstr ""
-#: lxc/image.go:280
+#: lxc/image.go:279
#, c-format
msgid "Copying the image: %s"
msgstr ""
@@ -276,12 +276,12 @@ msgstr ""
msgid "Create any directories necessary"
msgstr ""
-#: lxc/image.go:353 lxc/info.go:95
+#: lxc/image.go:351 lxc/info.go:95
#, c-format
msgid "Created: %s"
msgstr ""
-#: lxc/init.go:180 lxc/launch.go:134
+#: lxc/init.go:180 lxc/launch.go:135
#, c-format
msgid "Creating %s"
msgstr ""
@@ -290,7 +290,7 @@ msgstr ""
msgid "Creating the container"
msgstr ""
-#: lxc/image.go:641 lxc/image.go:682
+#: lxc/image.go:639 lxc/image.go:680
msgid "DESCRIPTION"
msgstr ""
@@ -352,16 +352,16 @@ msgid "Execute the specified command in a container.\n"
"Mode defaults to non-interactive, interactive mode is selected if both stdin AND stdout are terminals (stderr is ignored)."
msgstr ""
-#: lxc/image.go:357
+#: lxc/image.go:355
#, c-format
msgid "Expires: %s"
msgstr ""
-#: lxc/image.go:359
+#: lxc/image.go:357
msgid "Expires: never"
msgstr ""
-#: lxc/config.go:272 lxc/image.go:639 lxc/image.go:681
+#: lxc/config.go:272 lxc/image.go:637 lxc/image.go:679
msgid "FINGERPRINT"
msgstr ""
@@ -369,7 +369,7 @@ msgstr ""
msgid "Fast mode (same as --columns=nsacPt"
msgstr ""
-#: lxc/image.go:346
+#: lxc/image.go:344
#, c-format
msgid "Fingerprint: %s"
msgstr ""
@@ -424,16 +424,16 @@ msgstr ""
msgid "Ignore the container state (only for start)."
msgstr ""
-#: lxc/image.go:285
+#: lxc/image.go:282
msgid "Image copied successfully!"
msgstr ""
-#: lxc/image.go:442
+#: lxc/image.go:422 lxc/image.go:434
#, c-format
msgid "Image imported with fingerprint: %s"
msgstr ""
-#: lxc/image.go:429
+#: lxc/image.go:419
#, c-format
msgid "Importing the image: %s"
msgstr ""
@@ -877,7 +877,7 @@ msgstr ""
msgid "Only https URLs are supported for simplestreams"
msgstr ""
-#: lxc/image.go:434
+#: lxc/image.go:425
msgid "Only https:// is supported for remote image import."
msgstr ""
@@ -885,7 +885,7 @@ msgstr ""
msgid "Options:"
msgstr ""
-#: lxc/image.go:538
+#: lxc/image.go:536
#, c-format
msgid "Output is in %s"
msgstr ""
@@ -910,7 +910,7 @@ msgstr ""
msgid "PROTOCOL"
msgstr ""
-#: lxc/image.go:640 lxc/remote.go:384
+#: lxc/image.go:638 lxc/remote.go:384
msgid "PUBLIC"
msgstr ""
@@ -949,7 +949,7 @@ msgstr ""
msgid "Press enter to open the editor again"
msgstr ""
-#: lxc/config.go:531 lxc/config.go:596 lxc/image.go:735
+#: lxc/config.go:531 lxc/config.go:596 lxc/image.go:733
msgid "Press enter to start the editor again"
msgstr ""
@@ -1014,7 +1014,7 @@ msgstr ""
msgid "Profiles: %s"
msgstr ""
-#: lxc/image.go:361
+#: lxc/image.go:359
msgid "Properties:"
msgstr ""
@@ -1022,7 +1022,7 @@ msgstr ""
msgid "Public image server"
msgstr ""
-#: lxc/image.go:349
+#: lxc/image.go:347
#, c-format
msgid "Public: %s"
msgstr ""
@@ -1059,12 +1059,12 @@ msgstr ""
msgid "Resources:"
msgstr ""
-#: lxc/init.go:267
+#: lxc/init.go:239
#, c-format
msgid "Retrieving image: %s"
msgstr ""
-#: lxc/image.go:643
+#: lxc/image.go:641
msgid "SIZE"
msgstr ""
@@ -1129,7 +1129,7 @@ msgstr ""
msgid "Show the container's last 100 log lines?"
msgstr ""
-#: lxc/image.go:347
+#: lxc/image.go:345
#, c-format
msgid "Size: %.2fMB"
msgstr ""
@@ -1138,11 +1138,11 @@ msgstr ""
msgid "Snapshots:"
msgstr ""
-#: lxc/image.go:371
+#: lxc/image.go:369
msgid "Source:"
msgstr ""
-#: lxc/launch.go:142
+#: lxc/launch.go:144
#, c-format
msgid "Starting %s"
msgstr ""
@@ -1184,7 +1184,7 @@ msgstr ""
msgid "The container is currently running. Use --force to have it stopped and restarted."
msgstr ""
-#: lxc/init.go:313
+#: lxc/init.go:312
msgid "The container you are starting doesn’t have any network attached to it."
msgstr ""
@@ -1192,7 +1192,7 @@ msgstr ""
msgid "The device doesn't exist"
msgstr ""
-#: lxc/init.go:297
+#: lxc/init.go:296
#, c-format
msgid "The local image '%s' couldn't be found, trying '%s:' instead."
msgstr ""
@@ -1217,15 +1217,15 @@ msgstr ""
msgid "Time to wait for the container before killing it."
msgstr ""
-#: lxc/image.go:350
+#: lxc/image.go:348
msgid "Timestamps:"
msgstr ""
-#: lxc/init.go:315
+#: lxc/init.go:314
msgid "To attach a network to a container, use: lxc network attach"
msgstr ""
-#: lxc/init.go:314
+#: lxc/init.go:313
msgid "To create a new network, use: lxc network create"
msgstr ""
@@ -1233,12 +1233,12 @@ msgstr ""
msgid "To start your first container, try: lxc launch ubuntu:16.04"
msgstr ""
-#: lxc/image.go:421
+#: lxc/image.go:427
#, c-format
-msgid "Transferring image: %d%%"
+msgid "Transferring image: %s"
msgstr ""
-#: lxc/action.go:99 lxc/launch.go:155
+#: lxc/action.go:99 lxc/launch.go:157
#, c-format
msgid "Try `lxc info --show-log %s` for more info"
msgstr ""
@@ -1251,7 +1251,7 @@ msgstr ""
msgid "Type: persistent"
msgstr ""
-#: lxc/image.go:644
+#: lxc/image.go:642
msgid "UPLOAD DATE"
msgstr ""
@@ -1267,7 +1267,7 @@ msgstr ""
msgid "Unable to read remote TLS certificate"
msgstr ""
-#: lxc/image.go:355
+#: lxc/image.go:353
#, c-format
msgid "Uploaded: %s"
msgstr ""
@@ -1305,7 +1305,7 @@ msgstr ""
msgid "`lxc config profile` is deprecated, please use `lxc profile`"
msgstr ""
-#: lxc/launch.go:127
+#: lxc/launch.go:128
msgid "bad number of things scanned from image, container or snapshot"
msgstr ""
@@ -1333,15 +1333,15 @@ msgstr ""
msgid "default"
msgstr ""
-#: lxc/copy.go:131 lxc/copy.go:136 lxc/copy.go:232 lxc/copy.go:237 lxc/init.go:217 lxc/init.go:222 lxc/launch.go:111 lxc/launch.go:116
+#: lxc/copy.go:131 lxc/copy.go:136 lxc/copy.go:232 lxc/copy.go:237 lxc/init.go:219 lxc/init.go:224 lxc/launch.go:112 lxc/launch.go:117
msgid "didn't get any affected image, container or snapshot from server"
msgstr ""
-#: lxc/image.go:341
+#: lxc/image.go:339
msgid "disabled"
msgstr ""
-#: lxc/image.go:343
+#: lxc/image.go:341
msgid "enabled"
msgstr ""
@@ -1355,11 +1355,11 @@ msgstr ""
msgid "error: unknown command: %s"
msgstr ""
-#: lxc/launch.go:131
+#: lxc/launch.go:132
msgid "got bad version"
msgstr ""
-#: lxc/image.go:336 lxc/image.go:620
+#: lxc/image.go:334 lxc/image.go:618
msgid "no"
msgstr ""
@@ -1421,7 +1421,7 @@ msgstr ""
msgid "wrong number of subcommand arguments"
msgstr ""
-#: lxc/delete.go:45 lxc/image.go:338 lxc/image.go:624
+#: lxc/delete.go:45 lxc/image.go:336 lxc/image.go:622
msgid "yes"
msgstr ""
From ee10788dc3c79065aa1d276f7767c72b8e410eb2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at ubuntu.com>
Date: Tue, 15 Nov 2016 20:39:41 -0500
Subject: [PATCH 2/2] Track speed during network transfers
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Stéphane Graber <stgraber at ubuntu.com>
---
client.go | 2 +-
lxc/image.go | 4 ++--
lxd/daemon_images.go | 4 ++--
shared/simplestreams.go | 4 ++--
shared/util.go | 22 +++++++++++++++++++---
5 files changed, 26 insertions(+), 10 deletions(-)
diff --git a/client.go b/client.go
index 49fa8e3..a32709b 100644
--- a/client.go
+++ b/client.go
@@ -988,7 +988,7 @@ func (c *Client) PostImageURL(imageFile string, properties []string, public bool
return fingerprint, nil
}
-func (c *Client) PostImage(imageFile string, rootfsFile string, properties []string, public bool, aliases []string, progressHandler func(percent int)) (string, error) {
+func (c *Client) PostImage(imageFile string, rootfsFile string, properties []string, public bool, aliases []string, progressHandler func(int, int)) (string, error) {
if c.Remote.Public {
return "", fmt.Errorf("This function isn't supported by public remotes.")
}
diff --git a/lxc/image.go b/lxc/image.go
index 54a4be0..2241d7f 100644
--- a/lxc/image.go
+++ b/lxc/image.go
@@ -425,8 +425,8 @@ func (c *imageCmd) run(config *lxd.Config, args []string) error {
return fmt.Errorf(i18n.G("Only https:// is supported for remote image import."))
} else {
progress := ProgressRenderer{Format: i18n.G("Transferring image: %s")}
- handler := func(percent int) {
- progress.Update(fmt.Sprintf("%d%%", percent))
+ handler := func(percent int, speed int) {
+ progress.Update(fmt.Sprintf("%d%% (%s/s)", percent, shared.GetByteSizeString(int64(speed))))
}
fingerprint, err = d.PostImage(imageFile, rootfsFile, properties, c.publicImage, c.addAliases, handler)
diff --git a/lxd/daemon_images.go b/lxd/daemon_images.go
index 876bd7a..5007d13 100644
--- a/lxd/daemon_images.go
+++ b/lxd/daemon_images.go
@@ -247,7 +247,7 @@ func (d *Daemon) ImageDownload(op *operation, server string, protocol string, ce
d.Storage.ImageDelete(fp)
}
- progress := func(progressInt int) {
+ progress := func(progressInt int, speedInt int) {
if op == nil {
return
}
@@ -257,7 +257,7 @@ func (d *Daemon) ImageDownload(op *operation, server string, protocol string, ce
meta = make(map[string]interface{})
}
- progress := fmt.Sprintf("%d%%", progressInt)
+ progress := fmt.Sprintf("%d%% (%s/s)", progressInt, shared.GetByteSizeString(int64(speedInt)))
if meta["download_progress"] != progress {
meta["download_progress"] = progress
diff --git a/shared/simplestreams.go b/shared/simplestreams.go
index e0e358c..8fced30 100644
--- a/shared/simplestreams.go
+++ b/shared/simplestreams.go
@@ -494,7 +494,7 @@ func (s *SimpleStreams) getPaths(fingerprint string) ([][]string, error) {
return nil, fmt.Errorf("Couldn't find the requested image")
}
-func (s *SimpleStreams) downloadFile(path string, hash string, target string, progress func(int)) error {
+func (s *SimpleStreams) downloadFile(path string, hash string, target string, progress func(int, int)) error {
download := func(url string, hash string, target string) error {
out, err := os.Create(target)
if err != nil {
@@ -623,7 +623,7 @@ func (s *SimpleStreams) ExportImage(image string, target string) (string, error)
return target, nil
}
-func (s *SimpleStreams) Download(image string, file string, target string, progress func(int)) error {
+func (s *SimpleStreams) Download(image string, file string, target string, progress func(int, int)) error {
paths, err := s.getPaths(image)
if err != nil {
return err
diff --git a/shared/util.go b/shared/util.go
index 2cf0371..2b5b70e 100644
--- a/shared/util.go
+++ b/shared/util.go
@@ -19,6 +19,7 @@ import (
"regexp"
"strconv"
"strings"
+ "time"
)
const SnapshotDelimiter = "/"
@@ -742,8 +743,10 @@ type TransferProgress struct {
percentage float64
total int64
+ start *time.Time
+
Length int64
- Handler func(int)
+ Handler func(int, int)
}
func (pt *TransferProgress) Read(p []byte) (int, error) {
@@ -753,19 +756,32 @@ func (pt *TransferProgress) Read(p []byte) (int, error) {
return n, err
}
+ if pt.start == nil {
+ cur := time.Now()
+ pt.start = &cur
+ }
+
if n > 0 {
pt.total += int64(n)
percentage := float64(pt.total) / float64(pt.Length) * float64(100)
if percentage-pt.percentage > 0.9 {
+ // Determine percentage
pt.percentage = percentage
-
progressInt := 1 - (int(percentage) % 1) + int(percentage)
if progressInt > 100 {
progressInt = 100
}
- pt.Handler(progressInt)
+ // Determine speed
+ speedInt := int(0)
+ duration := time.Since(*pt.start).Seconds()
+ if duration > 0 {
+ speed := float64(pt.total) / duration
+ speedInt = int(speed)
+ }
+
+ pt.Handler(progressInt, speedInt)
}
}
More information about the lxc-devel
mailing list