The following pull request was submitted through Github.
It can be accessed and reviewed at: https://github.com/lxc/lxd/pull/3768

This e-mail was sent by the LXC bot, direct replies will not reach the author
unless they happen to be subscribed to this list.

=== Description (from pull-request) ===
This moves the `lxd-benchmark` logic to a separate package, and refactors a bit the code to make it reusable.
The intent is to use be able to reuse the logic in a separate tool for performance testing.
From 2dfef81b8a04a2a8aa61ac4565757c04d77d5453 Mon Sep 17 00:00:00 2001
From: Alberto Donato <alberto.don...@canonical.com>
Date: Tue, 5 Sep 2017 12:02:45 +0200
Subject: [PATCH 1/7] benchmark: extract logic to separate package

Signed-off-by: Alberto Donato <alberto.don...@canonical.com>
---
 benchmark/benchmark.go     | 327 +++++++++++++++++++++++++++++++++++++++++++++
 test/lxd-benchmark/main.go | 324 +-------------------------------------------
 2 files changed, 330 insertions(+), 321 deletions(-)
 create mode 100644 benchmark/benchmark.go

diff --git a/benchmark/benchmark.go b/benchmark/benchmark.go
new file mode 100644
index 000000000..e8fe5e8f7
--- /dev/null
+++ b/benchmark/benchmark.go
@@ -0,0 +1,327 @@
+package benchmark
+
+import (
+       "fmt"
+       "io/ioutil"
+       "strings"
+       "sync"
+       "time"
+
+       "github.com/lxc/lxd/client"
+       "github.com/lxc/lxd/lxc/config"
+       "github.com/lxc/lxd/shared/api"
+       "github.com/lxc/lxd/shared/version"
+)
+
+func SpawnContainers(c lxd.ContainerServer, count int, parallel int, image 
string, privileged bool, freeze bool) error {
+       batch := parallel
+       if batch < 1 {
+               // Detect the number of parallel actions
+               cpus, err := ioutil.ReadDir("/sys/bus/cpu/devices")
+               if err != nil {
+                       return err
+               }
+
+               batch = len(cpus)
+       }
+
+       batches := count / batch
+       remainder := count % batch
+
+       // Print the test header
+       st, _, err := c.GetServer()
+       if err != nil {
+               return err
+       }
+
+       privilegedStr := "unprivileged"
+       if privileged {
+               privilegedStr = "privileged"
+       }
+
+       mode := "normal startup"
+       if freeze {
+               mode = "start and freeze"
+       }
+
+       fmt.Printf("Test environment:\n")
+       fmt.Printf("  Server backend: %s\n", st.Environment.Server)
+       fmt.Printf("  Server version: %s\n", st.Environment.ServerVersion)
+       fmt.Printf("  Kernel: %s\n", st.Environment.Kernel)
+       fmt.Printf("  Kernel architecture: %s\n", 
st.Environment.KernelArchitecture)
+       fmt.Printf("  Kernel version: %s\n", st.Environment.KernelVersion)
+       fmt.Printf("  Storage backend: %s\n", st.Environment.Storage)
+       fmt.Printf("  Storage version: %s\n", st.Environment.StorageVersion)
+       fmt.Printf("  Container backend: %s\n", st.Environment.Driver)
+       fmt.Printf("  Container version: %s\n", st.Environment.DriverVersion)
+       fmt.Printf("\n")
+       fmt.Printf("Test variables:\n")
+       fmt.Printf("  Container count: %d\n", count)
+       fmt.Printf("  Container mode: %s\n", privilegedStr)
+       fmt.Printf("  Startup mode: %s\n", mode)
+       fmt.Printf("  Image: %s\n", image)
+       fmt.Printf("  Batches: %d\n", batches)
+       fmt.Printf("  Batch size: %d\n", batch)
+       fmt.Printf("  Remainder: %d\n", remainder)
+       fmt.Printf("\n")
+
+       // Pre-load the image
+       var fingerprint string
+       if strings.Contains(image, ":") {
+               var remote string
+
+               defaultConfig := config.DefaultConfig
+               defaultConfig.UserAgent = version.UserAgent
+
+               remote, fingerprint, err = defaultConfig.ParseRemote(image)
+               if err != nil {
+                       return err
+               }
+
+               d, err := defaultConfig.GetImageServer(remote)
+               if err != nil {
+                       return err
+               }
+
+               if fingerprint == "" {
+                       fingerprint = "default"
+               }
+
+               alias, _, err := d.GetImageAlias(fingerprint)
+               if err == nil {
+                       fingerprint = alias.Target
+               }
+
+               _, _, err = c.GetImage(fingerprint)
+               if err != nil {
+                       logf("Importing image into local store: %s", 
fingerprint)
+                       image, _, err := d.GetImage(fingerprint)
+                       if err != nil {
+                               logf(fmt.Sprintf("Failed to import image: %s", 
err))
+                               return err
+                       }
+
+                       op, err := c.CopyImage(d, *image, nil)
+                       if err != nil {
+                               logf(fmt.Sprintf("Failed to import image: %s", 
err))
+                               return err
+                       }
+
+                       err = op.Wait()
+                       if err != nil {
+                               logf(fmt.Sprintf("Failed to import image: %s", 
err))
+                               return err
+                       }
+               } else {
+                       logf("Found image in local store: %s", fingerprint)
+               }
+       } else {
+               fingerprint = image
+               logf("Found image in local store: %s", fingerprint)
+       }
+
+       // Start the containers
+       spawnedCount := 0
+       nameFormat := "benchmark-%." + fmt.Sprintf("%d", len(fmt.Sprintf("%d", 
count))) + "d"
+       wgBatch := sync.WaitGroup{}
+       nextStat := batch
+
+       startContainer := func(name string) {
+               defer wgBatch.Done()
+
+               // Configure
+               config := map[string]string{}
+               if privileged {
+                       config["security.privileged"] = "true"
+               }
+               config["user.lxd-benchmark"] = "true"
+
+               // Create
+               req := api.ContainersPost{
+                       Name: name,
+                       Source: api.ContainerSource{
+                               Type:        "image",
+                               Fingerprint: fingerprint,
+                       },
+               }
+               req.Config = config
+
+               op, err := c.CreateContainer(req)
+               if err != nil {
+                       logf(fmt.Sprintf("Failed to spawn container '%s': %s", 
name, err))
+                       return
+               }
+
+               err = op.Wait()
+               if err != nil {
+                       logf(fmt.Sprintf("Failed to spawn container '%s': %s", 
name, err))
+                       return
+               }
+
+               // Start
+               op, err = c.UpdateContainerState(name, 
api.ContainerStatePut{Action: "start", Timeout: -1}, "")
+               if err != nil {
+                       logf(fmt.Sprintf("Failed to spawn container '%s': %s", 
name, err))
+                       return
+               }
+
+               err = op.Wait()
+               if err != nil {
+                       logf(fmt.Sprintf("Failed to spawn container '%s': %s", 
name, err))
+                       return
+               }
+
+               // Freeze
+               if freeze {
+                       op, err := c.UpdateContainerState(name, 
api.ContainerStatePut{Action: "freeze", Timeout: -1}, "")
+                       if err != nil {
+                               logf(fmt.Sprintf("Failed to spawn container 
'%s': %s", name, err))
+                               return
+                       }
+
+                       err = op.Wait()
+                       if err != nil {
+                               logf(fmt.Sprintf("Failed to spawn container 
'%s': %s", name, err))
+                               return
+                       }
+               }
+       }
+
+       logf("Starting the test")
+       timeStart := time.Now()
+
+       for i := 0; i < batches; i++ {
+               for j := 0; j < batch; j++ {
+                       spawnedCount = spawnedCount + 1
+                       name := fmt.Sprintf(nameFormat, spawnedCount)
+
+                       wgBatch.Add(1)
+                       go startContainer(name)
+               }
+               wgBatch.Wait()
+
+               if spawnedCount >= nextStat {
+                       interval := time.Since(timeStart).Seconds()
+                       logf("Started %d containers in %.3fs (%.3f/s)", 
spawnedCount, interval, float64(spawnedCount)/interval)
+                       nextStat = nextStat * 2
+               }
+       }
+
+       for k := 0; k < remainder; k++ {
+               spawnedCount = spawnedCount + 1
+               name := fmt.Sprintf(nameFormat, spawnedCount)
+
+               wgBatch.Add(1)
+               go startContainer(name)
+       }
+       wgBatch.Wait()
+
+       logf("Test completed in %.3fs", time.Since(timeStart).Seconds())
+
+       return nil
+}
+
+func DeleteContainers(c lxd.ContainerServer, parallel int) error {
+       batch := parallel
+       if batch < 1 {
+               // Detect the number of parallel actions
+               cpus, err := ioutil.ReadDir("/sys/bus/cpu/devices")
+               if err != nil {
+                       return err
+               }
+
+               batch = len(cpus)
+       }
+
+       // List all the containers
+       allContainers, err := c.GetContainers()
+       if err != nil {
+               return err
+       }
+
+       containers := []api.Container{}
+       for _, container := range allContainers {
+               if container.Config["user.lxd-benchmark"] != "true" {
+                       continue
+               }
+
+               containers = append(containers, container)
+       }
+
+       // Delete them all
+       count := len(containers)
+       logf("%d containers to delete", count)
+
+       batches := count / batch
+
+       deletedCount := 0
+       wgBatch := sync.WaitGroup{}
+       nextStat := batch
+
+       deleteContainer := func(ct api.Container) {
+               defer wgBatch.Done()
+
+               // Stop
+               if ct.IsActive() {
+                       op, err := c.UpdateContainerState(ct.Name, 
api.ContainerStatePut{Action: "stop", Timeout: -1, Force: true}, "")
+                       if err != nil {
+                               logf(fmt.Sprintf("Failed to delete container 
'%s': %s", ct.Name, err))
+                               return
+                       }
+
+                       err = op.Wait()
+                       if err != nil {
+                               logf(fmt.Sprintf("Failed to delete container 
'%s': %s", ct.Name, err))
+                               return
+                       }
+               }
+
+               // Delete
+               op, err := c.DeleteContainer(ct.Name)
+               if err != nil {
+                       logf("Failed to delete container: %s", ct.Name)
+                       return
+               }
+
+               err = op.Wait()
+               if err != nil {
+                       logf("Failed to delete container: %s", ct.Name)
+                       return
+               }
+       }
+
+       logf("Starting the cleanup")
+       timeStart := time.Now()
+
+       for i := 0; i < batches; i++ {
+               for j := 0; j < batch; j++ {
+                       wgBatch.Add(1)
+                       go deleteContainer(containers[deletedCount])
+
+                       deletedCount = deletedCount + 1
+               }
+               wgBatch.Wait()
+
+               if deletedCount >= nextStat {
+                       interval := time.Since(timeStart).Seconds()
+                       logf("Deleted %d containers in %.3fs (%.3f/s)", 
deletedCount, interval, float64(deletedCount)/interval)
+                       nextStat = nextStat * 2
+               }
+       }
+
+       for k := deletedCount; k < count; k++ {
+               wgBatch.Add(1)
+               go deleteContainer(containers[deletedCount])
+
+               deletedCount = deletedCount + 1
+       }
+       wgBatch.Wait()
+
+       logf("Cleanup completed")
+
+       return nil
+}
+
+func logf(format string, args ...interface{}) {
+       fmt.Printf(fmt.Sprintf("[%s] %s\n", time.Now().Format(time.StampMilli), 
format), args...)
+}
diff --git a/test/lxd-benchmark/main.go b/test/lxd-benchmark/main.go
index 9c8eb3c11..55de62154 100644
--- a/test/lxd-benchmark/main.go
+++ b/test/lxd-benchmark/main.go
@@ -2,16 +2,11 @@ package main
 
 import (
        "fmt"
-       "io/ioutil"
        "os"
-       "strings"
-       "sync"
-       "time"
 
+       "github.com/lxc/lxd/benchmark"
        "github.com/lxc/lxd/client"
-       "github.com/lxc/lxd/lxc/config"
        "github.com/lxc/lxd/shared"
-       "github.com/lxc/lxd/shared/api"
        "github.com/lxc/lxd/shared/gnuflag"
        "github.com/lxc/lxd/shared/version"
 )
@@ -68,323 +63,10 @@ func run(args []string) error {
 
        switch os.Args[1] {
        case "spawn":
-               return spawnContainers(c, *argCount, *argImage, *argPrivileged)
+               return benchmark.SpawnContainers(c, *argCount, *argParallel, 
*argImage, *argPrivileged, *argFreeze)
        case "delete":
-               return deleteContainers(c)
+               return benchmark.DeleteContainers(c, *argParallel)
        }
 
        return nil
 }
-
-func logf(format string, args ...interface{}) {
-       fmt.Printf(fmt.Sprintf("[%s] %s\n", time.Now().Format(time.StampMilli), 
format), args...)
-}
-
-func spawnContainers(c lxd.ContainerServer, count int, image string, 
privileged bool) error {
-       batch := *argParallel
-       if batch < 1 {
-               // Detect the number of parallel actions
-               cpus, err := ioutil.ReadDir("/sys/bus/cpu/devices")
-               if err != nil {
-                       return err
-               }
-
-               batch = len(cpus)
-       }
-
-       batches := count / batch
-       remainder := count % batch
-
-       // Print the test header
-       st, _, err := c.GetServer()
-       if err != nil {
-               return err
-       }
-
-       privilegedStr := "unprivileged"
-       if privileged {
-               privilegedStr = "privileged"
-       }
-
-       mode := "normal startup"
-       if *argFreeze {
-               mode = "start and freeze"
-       }
-
-       fmt.Printf("Test environment:\n")
-       fmt.Printf("  Server backend: %s\n", st.Environment.Server)
-       fmt.Printf("  Server version: %s\n", st.Environment.ServerVersion)
-       fmt.Printf("  Kernel: %s\n", st.Environment.Kernel)
-       fmt.Printf("  Kernel architecture: %s\n", 
st.Environment.KernelArchitecture)
-       fmt.Printf("  Kernel version: %s\n", st.Environment.KernelVersion)
-       fmt.Printf("  Storage backend: %s\n", st.Environment.Storage)
-       fmt.Printf("  Storage version: %s\n", st.Environment.StorageVersion)
-       fmt.Printf("  Container backend: %s\n", st.Environment.Driver)
-       fmt.Printf("  Container version: %s\n", st.Environment.DriverVersion)
-       fmt.Printf("\n")
-       fmt.Printf("Test variables:\n")
-       fmt.Printf("  Container count: %d\n", count)
-       fmt.Printf("  Container mode: %s\n", privilegedStr)
-       fmt.Printf("  Startup mode: %s\n", mode)
-       fmt.Printf("  Image: %s\n", image)
-       fmt.Printf("  Batches: %d\n", batches)
-       fmt.Printf("  Batch size: %d\n", batch)
-       fmt.Printf("  Remainder: %d\n", remainder)
-       fmt.Printf("\n")
-
-       // Pre-load the image
-       var fingerprint string
-       if strings.Contains(image, ":") {
-               var remote string
-
-               defaultConfig := config.DefaultConfig
-               defaultConfig.UserAgent = version.UserAgent
-
-               remote, fingerprint, err = defaultConfig.ParseRemote(image)
-               if err != nil {
-                       return err
-               }
-
-               d, err := defaultConfig.GetImageServer(remote)
-               if err != nil {
-                       return err
-               }
-
-               if fingerprint == "" {
-                       fingerprint = "default"
-               }
-
-               alias, _, err := d.GetImageAlias(fingerprint)
-               if err == nil {
-                       fingerprint = alias.Target
-               }
-
-               _, _, err = c.GetImage(fingerprint)
-               if err != nil {
-                       logf("Importing image into local store: %s", 
fingerprint)
-                       image, _, err := d.GetImage(fingerprint)
-                       if err != nil {
-                               logf(fmt.Sprintf("Failed to import image: %s", 
err))
-                               return err
-                       }
-
-                       op, err := c.CopyImage(d, *image, nil)
-                       if err != nil {
-                               logf(fmt.Sprintf("Failed to import image: %s", 
err))
-                               return err
-                       }
-
-                       err = op.Wait()
-                       if err != nil {
-                               logf(fmt.Sprintf("Failed to import image: %s", 
err))
-                               return err
-                       }
-               } else {
-                       logf("Found image in local store: %s", fingerprint)
-               }
-       } else {
-               fingerprint = image
-               logf("Found image in local store: %s", fingerprint)
-       }
-
-       // Start the containers
-       spawnedCount := 0
-       nameFormat := "benchmark-%." + fmt.Sprintf("%d", len(fmt.Sprintf("%d", 
count))) + "d"
-       wgBatch := sync.WaitGroup{}
-       nextStat := batch
-
-       startContainer := func(name string) {
-               defer wgBatch.Done()
-
-               // Configure
-               config := map[string]string{}
-               if privileged {
-                       config["security.privileged"] = "true"
-               }
-               config["user.lxd-benchmark"] = "true"
-
-               // Create
-               req := api.ContainersPost{
-                       Name: name,
-                       Source: api.ContainerSource{
-                               Type:        "image",
-                               Fingerprint: fingerprint,
-                       },
-               }
-               req.Config = config
-
-               op, err := c.CreateContainer(req)
-               if err != nil {
-                       logf(fmt.Sprintf("Failed to spawn container '%s': %s", 
name, err))
-                       return
-               }
-
-               err = op.Wait()
-               if err != nil {
-                       logf(fmt.Sprintf("Failed to spawn container '%s': %s", 
name, err))
-                       return
-               }
-
-               // Start
-               op, err = c.UpdateContainerState(name, 
api.ContainerStatePut{Action: "start", Timeout: -1}, "")
-               if err != nil {
-                       logf(fmt.Sprintf("Failed to spawn container '%s': %s", 
name, err))
-                       return
-               }
-
-               err = op.Wait()
-               if err != nil {
-                       logf(fmt.Sprintf("Failed to spawn container '%s': %s", 
name, err))
-                       return
-               }
-
-               // Freeze
-               if *argFreeze {
-                       op, err := c.UpdateContainerState(name, 
api.ContainerStatePut{Action: "freeze", Timeout: -1}, "")
-                       if err != nil {
-                               logf(fmt.Sprintf("Failed to spawn container 
'%s': %s", name, err))
-                               return
-                       }
-
-                       err = op.Wait()
-                       if err != nil {
-                               logf(fmt.Sprintf("Failed to spawn container 
'%s': %s", name, err))
-                               return
-                       }
-               }
-       }
-
-       logf("Starting the test")
-       timeStart := time.Now()
-
-       for i := 0; i < batches; i++ {
-               for j := 0; j < batch; j++ {
-                       spawnedCount = spawnedCount + 1
-                       name := fmt.Sprintf(nameFormat, spawnedCount)
-
-                       wgBatch.Add(1)
-                       go startContainer(name)
-               }
-               wgBatch.Wait()
-
-               if spawnedCount >= nextStat {
-                       interval := time.Since(timeStart).Seconds()
-                       logf("Started %d containers in %.3fs (%.3f/s)", 
spawnedCount, interval, float64(spawnedCount)/interval)
-                       nextStat = nextStat * 2
-               }
-       }
-
-       for k := 0; k < remainder; k++ {
-               spawnedCount = spawnedCount + 1
-               name := fmt.Sprintf(nameFormat, spawnedCount)
-
-               wgBatch.Add(1)
-               go startContainer(name)
-       }
-       wgBatch.Wait()
-
-       logf("Test completed in %.3fs", time.Since(timeStart).Seconds())
-
-       return nil
-}
-
-func deleteContainers(c lxd.ContainerServer) error {
-       batch := *argParallel
-       if batch < 1 {
-               // Detect the number of parallel actions
-               cpus, err := ioutil.ReadDir("/sys/bus/cpu/devices")
-               if err != nil {
-                       return err
-               }
-
-               batch = len(cpus)
-       }
-
-       // List all the containers
-       allContainers, err := c.GetContainers()
-       if err != nil {
-               return err
-       }
-
-       containers := []api.Container{}
-       for _, container := range allContainers {
-               if container.Config["user.lxd-benchmark"] != "true" {
-                       continue
-               }
-
-               containers = append(containers, container)
-       }
-
-       // Delete them all
-       count := len(containers)
-       logf("%d containers to delete", count)
-
-       batches := count / batch
-
-       deletedCount := 0
-       wgBatch := sync.WaitGroup{}
-       nextStat := batch
-
-       deleteContainer := func(ct api.Container) {
-               defer wgBatch.Done()
-
-               // Stop
-               if ct.IsActive() {
-                       op, err := c.UpdateContainerState(ct.Name, 
api.ContainerStatePut{Action: "stop", Timeout: -1, Force: true}, "")
-                       if err != nil {
-                               logf(fmt.Sprintf("Failed to delete container 
'%s': %s", ct.Name, err))
-                               return
-                       }
-
-                       err = op.Wait()
-                       if err != nil {
-                               logf(fmt.Sprintf("Failed to delete container 
'%s': %s", ct.Name, err))
-                               return
-                       }
-               }
-
-               // Delete
-               op, err := c.DeleteContainer(ct.Name)
-               if err != nil {
-                       logf("Failed to delete container: %s", ct.Name)
-                       return
-               }
-
-               err = op.Wait()
-               if err != nil {
-                       logf("Failed to delete container: %s", ct.Name)
-                       return
-               }
-       }
-
-       logf("Starting the cleanup")
-       timeStart := time.Now()
-
-       for i := 0; i < batches; i++ {
-               for j := 0; j < batch; j++ {
-                       wgBatch.Add(1)
-                       go deleteContainer(containers[deletedCount])
-
-                       deletedCount = deletedCount + 1
-               }
-               wgBatch.Wait()
-
-               if deletedCount >= nextStat {
-                       interval := time.Since(timeStart).Seconds()
-                       logf("Deleted %d containers in %.3fs (%.3f/s)", 
deletedCount, interval, float64(deletedCount)/interval)
-                       nextStat = nextStat * 2
-               }
-       }
-
-       for k := deletedCount; k < count; k++ {
-               wgBatch.Add(1)
-               go deleteContainer(containers[deletedCount])
-
-               deletedCount = deletedCount + 1
-       }
-       wgBatch.Wait()
-
-       logf("Cleanup completed")
-
-       return nil
-}

From 6b459fc01bfea1e68bd9d6b0de6f5e22fc38b8a1 Mon Sep 17 00:00:00 2001
From: Alberto Donato <alberto.don...@canonical.com>
Date: Tue, 5 Sep 2017 12:24:36 +0200
Subject: [PATCH 2/7] benchmark: extract PrintServerInfo function

Signed-off-by: Alberto Donato <alberto.don...@canonical.com>
---
 benchmark/benchmark.go     | 41 +++++++++++++++++++++++------------------
 test/lxd-benchmark/main.go |  2 ++
 2 files changed, 25 insertions(+), 18 deletions(-)

diff --git a/benchmark/benchmark.go b/benchmark/benchmark.go
index e8fe5e8f7..26e2a60a1 100644
--- a/benchmark/benchmark.go
+++ b/benchmark/benchmark.go
@@ -13,6 +13,28 @@ import (
        "github.com/lxc/lxd/shared/version"
 )
 
+// PrintServerInfo prints out information about the server.
+func PrintServerInfo(c lxd.ContainerServer) error {
+       server, _, err := c.GetServer()
+       if err != nil {
+               return err
+       }
+       env := server.Environment
+       fmt.Printf("Test environment:\n")
+       fmt.Printf("  Server backend: %s\n", env.Server)
+       fmt.Printf("  Server version: %s\n", env.ServerVersion)
+       fmt.Printf("  Kernel: %s\n", env.Kernel)
+       fmt.Printf("  Kernel architecture: %s\n", env.KernelArchitecture)
+       fmt.Printf("  Kernel version: %s\n", env.KernelVersion)
+       fmt.Printf("  Storage backend: %s\n", env.Storage)
+       fmt.Printf("  Storage version: %s\n", env.StorageVersion)
+       fmt.Printf("  Container backend: %s\n", env.Driver)
+       fmt.Printf("  Container version: %s\n", env.DriverVersion)
+       fmt.Printf("\n")
+       return nil
+}
+
+// SpawnContainers launches a set of containers.
 func SpawnContainers(c lxd.ContainerServer, count int, parallel int, image 
string, privileged bool, freeze bool) error {
        batch := parallel
        if batch < 1 {
@@ -29,32 +51,14 @@ func SpawnContainers(c lxd.ContainerServer, count int, 
parallel int, image strin
        remainder := count % batch
 
        // Print the test header
-       st, _, err := c.GetServer()
-       if err != nil {
-               return err
-       }
-
        privilegedStr := "unprivileged"
        if privileged {
                privilegedStr = "privileged"
        }
-
        mode := "normal startup"
        if freeze {
                mode = "start and freeze"
        }
-
-       fmt.Printf("Test environment:\n")
-       fmt.Printf("  Server backend: %s\n", st.Environment.Server)
-       fmt.Printf("  Server version: %s\n", st.Environment.ServerVersion)
-       fmt.Printf("  Kernel: %s\n", st.Environment.Kernel)
-       fmt.Printf("  Kernel architecture: %s\n", 
st.Environment.KernelArchitecture)
-       fmt.Printf("  Kernel version: %s\n", st.Environment.KernelVersion)
-       fmt.Printf("  Storage backend: %s\n", st.Environment.Storage)
-       fmt.Printf("  Storage version: %s\n", st.Environment.StorageVersion)
-       fmt.Printf("  Container backend: %s\n", st.Environment.Driver)
-       fmt.Printf("  Container version: %s\n", st.Environment.DriverVersion)
-       fmt.Printf("\n")
        fmt.Printf("Test variables:\n")
        fmt.Printf("  Container count: %d\n", count)
        fmt.Printf("  Container mode: %s\n", privilegedStr)
@@ -69,6 +73,7 @@ func SpawnContainers(c lxd.ContainerServer, count int, 
parallel int, image strin
        var fingerprint string
        if strings.Contains(image, ":") {
                var remote string
+               var err error
 
                defaultConfig := config.DefaultConfig
                defaultConfig.UserAgent = version.UserAgent
diff --git a/test/lxd-benchmark/main.go b/test/lxd-benchmark/main.go
index 55de62154..f2a2a6f2b 100644
--- a/test/lxd-benchmark/main.go
+++ b/test/lxd-benchmark/main.go
@@ -61,6 +61,8 @@ func run(args []string) error {
                return err
        }
 
+       benchmark.PrintServerInfo(c)
+
        switch os.Args[1] {
        case "spawn":
                return benchmark.SpawnContainers(c, *argCount, *argParallel, 
*argImage, *argPrivileged, *argFreeze)

From 31e612a9620a56b9273cafa3661e0f452ee7f917 Mon Sep 17 00:00:00 2001
From: Alberto Donato <alberto.don...@canonical.com>
Date: Tue, 5 Sep 2017 12:40:01 +0200
Subject: [PATCH 3/7] benchmark: extract printTestConfig function

Signed-off-by: Alberto Donato <alberto.don...@canonical.com>
---
 benchmark/benchmark.go | 44 +++++++++++++++++++++++++-------------------
 1 file changed, 25 insertions(+), 19 deletions(-)

diff --git a/benchmark/benchmark.go b/benchmark/benchmark.go
index 26e2a60a1..141066bb4 100644
--- a/benchmark/benchmark.go
+++ b/benchmark/benchmark.go
@@ -47,28 +47,11 @@ func SpawnContainers(c lxd.ContainerServer, count int, 
parallel int, image strin
                batch = len(cpus)
        }
 
+       printTestConfig(count, batch, image, privileged, freeze)
+
        batches := count / batch
        remainder := count % batch
 
-       // Print the test header
-       privilegedStr := "unprivileged"
-       if privileged {
-               privilegedStr = "privileged"
-       }
-       mode := "normal startup"
-       if freeze {
-               mode = "start and freeze"
-       }
-       fmt.Printf("Test variables:\n")
-       fmt.Printf("  Container count: %d\n", count)
-       fmt.Printf("  Container mode: %s\n", privilegedStr)
-       fmt.Printf("  Startup mode: %s\n", mode)
-       fmt.Printf("  Image: %s\n", image)
-       fmt.Printf("  Batches: %d\n", batches)
-       fmt.Printf("  Batch size: %d\n", batch)
-       fmt.Printf("  Remainder: %d\n", remainder)
-       fmt.Printf("\n")
-
        // Pre-load the image
        var fingerprint string
        if strings.Contains(image, ":") {
@@ -330,3 +313,26 @@ func DeleteContainers(c lxd.ContainerServer, parallel int) 
error {
 func logf(format string, args ...interface{}) {
        fmt.Printf(fmt.Sprintf("[%s] %s\n", time.Now().Format(time.StampMilli), 
format), args...)
 }
+
+func printTestConfig(count int, batchSize int, image string, privileged bool, 
freeze bool) {
+       privilegedStr := "unprivileged"
+       if privileged {
+               privilegedStr = "privileged"
+       }
+       mode := "normal startup"
+       if freeze {
+               mode = "start and freeze"
+       }
+
+       batches := count / batchSize
+       remainder := count % batchSize
+       fmt.Printf("Test variables:\n")
+       fmt.Printf("  Container count: %d\n", count)
+       fmt.Printf("  Container mode: %s\n", privilegedStr)
+       fmt.Printf("  Startup mode: %s\n", mode)
+       fmt.Printf("  Image: %s\n", image)
+       fmt.Printf("  Batches: %d\n", batches)
+       fmt.Printf("  Batch size: %d\n", batchSize)
+       fmt.Printf("  Remainder: %d\n", remainder)
+       fmt.Printf("\n")
+}

From c5f93f0b5486387b1142e3ebfaadb0cc124861d1 Mon Sep 17 00:00:00 2001
From: Alberto Donato <alberto.don...@canonical.com>
Date: Tue, 5 Sep 2017 12:54:22 +0200
Subject: [PATCH 4/7] benchmark: extract getBatchSize function

Signed-off-by: Alberto Donato <alberto.don...@canonical.com>
---
 benchmark/benchmark.go | 56 +++++++++++++++++++++++++++-----------------------
 1 file changed, 30 insertions(+), 26 deletions(-)

diff --git a/benchmark/benchmark.go b/benchmark/benchmark.go
index 141066bb4..d9911fd64 100644
--- a/benchmark/benchmark.go
+++ b/benchmark/benchmark.go
@@ -36,21 +36,15 @@ func PrintServerInfo(c lxd.ContainerServer) error {
 
 // SpawnContainers launches a set of containers.
 func SpawnContainers(c lxd.ContainerServer, count int, parallel int, image 
string, privileged bool, freeze bool) error {
-       batch := parallel
-       if batch < 1 {
-               // Detect the number of parallel actions
-               cpus, err := ioutil.ReadDir("/sys/bus/cpu/devices")
-               if err != nil {
-                       return err
-               }
-
-               batch = len(cpus)
+       batchSize, err := getBatchSize(parallel)
+       if err != nil {
+               return err
        }
 
-       printTestConfig(count, batch, image, privileged, freeze)
+       printTestConfig(count, batchSize, image, privileged, freeze)
 
-       batches := count / batch
-       remainder := count % batch
+       batches := count / batchSize
+       remainder := count % batchSize
 
        // Pre-load the image
        var fingerprint string
@@ -112,7 +106,7 @@ func SpawnContainers(c lxd.ContainerServer, count int, 
parallel int, image strin
        spawnedCount := 0
        nameFormat := "benchmark-%." + fmt.Sprintf("%d", len(fmt.Sprintf("%d", 
count))) + "d"
        wgBatch := sync.WaitGroup{}
-       nextStat := batch
+       nextStat := batchSize
 
        startContainer := func(name string) {
                defer wgBatch.Done()
@@ -179,7 +173,7 @@ func SpawnContainers(c lxd.ContainerServer, count int, 
parallel int, image strin
        timeStart := time.Now()
 
        for i := 0; i < batches; i++ {
-               for j := 0; j < batch; j++ {
+               for j := 0; j < batchSize; j++ {
                        spawnedCount = spawnedCount + 1
                        name := fmt.Sprintf(nameFormat, spawnedCount)
 
@@ -209,16 +203,11 @@ func SpawnContainers(c lxd.ContainerServer, count int, 
parallel int, image strin
        return nil
 }
 
+// DeleteContainers removes containers created by the benchmark.
 func DeleteContainers(c lxd.ContainerServer, parallel int) error {
-       batch := parallel
-       if batch < 1 {
-               // Detect the number of parallel actions
-               cpus, err := ioutil.ReadDir("/sys/bus/cpu/devices")
-               if err != nil {
-                       return err
-               }
-
-               batch = len(cpus)
+       batchSize, err := getBatchSize(parallel)
+       if err != nil {
+               return err
        }
 
        // List all the containers
@@ -240,11 +229,11 @@ func DeleteContainers(c lxd.ContainerServer, parallel 
int) error {
        count := len(containers)
        logf("%d containers to delete", count)
 
-       batches := count / batch
+       batches := count / batchSize
 
        deletedCount := 0
        wgBatch := sync.WaitGroup{}
-       nextStat := batch
+       nextStat := batchSize
 
        deleteContainer := func(ct api.Container) {
                defer wgBatch.Done()
@@ -282,7 +271,7 @@ func DeleteContainers(c lxd.ContainerServer, parallel int) 
error {
        timeStart := time.Now()
 
        for i := 0; i < batches; i++ {
-               for j := 0; j < batch; j++ {
+               for j := 0; j < batchSize; j++ {
                        wgBatch.Add(1)
                        go deleteContainer(containers[deletedCount])
 
@@ -310,6 +299,21 @@ func DeleteContainers(c lxd.ContainerServer, parallel int) 
error {
        return nil
 }
 
+func getBatchSize(parallel int) (int, error) {
+       batchSize := parallel
+       if batchSize < 1 {
+               // Detect the number of parallel actions
+               cpus, err := ioutil.ReadDir("/sys/bus/cpu/devices")
+               if err != nil {
+                       return -1, err
+               }
+
+               batchSize = len(cpus)
+       }
+
+       return batchSize, nil
+}
+
 func logf(format string, args ...interface{}) {
        fmt.Printf(fmt.Sprintf("[%s] %s\n", time.Now().Format(time.StampMilli), 
format), args...)
 }

From b8bf7d20c70e5328c5944261205b146b07a5c8eb Mon Sep 17 00:00:00 2001
From: Alberto Donato <alberto.don...@canonical.com>
Date: Tue, 5 Sep 2017 13:01:34 +0200
Subject: [PATCH 5/7] benchmark: extract GetContainers function

Signed-off-by: Alberto Donato <alberto.don...@canonical.com>
---
 benchmark/benchmark.go | 30 ++++++++++++++++++++----------
 1 file changed, 20 insertions(+), 10 deletions(-)

diff --git a/benchmark/benchmark.go b/benchmark/benchmark.go
index d9911fd64..81bdcdda3 100644
--- a/benchmark/benchmark.go
+++ b/benchmark/benchmark.go
@@ -203,20 +203,15 @@ func SpawnContainers(c lxd.ContainerServer, count int, 
parallel int, image strin
        return nil
 }
 
-// DeleteContainers removes containers created by the benchmark.
-func DeleteContainers(c lxd.ContainerServer, parallel int) error {
-       batchSize, err := getBatchSize(parallel)
-       if err != nil {
-               return err
-       }
+// GetContainers returns containers created by the benchmark.
+func GetContainers(c lxd.ContainerServer) ([]api.Container, error) {
+       containers := []api.Container{}
 
-       // List all the containers
        allContainers, err := c.GetContainers()
        if err != nil {
-               return err
+               return containers, err
        }
 
-       containers := []api.Container{}
        for _, container := range allContainers {
                if container.Config["user.lxd-benchmark"] != "true" {
                        continue
@@ -225,7 +220,22 @@ func DeleteContainers(c lxd.ContainerServer, parallel int) 
error {
                containers = append(containers, container)
        }
 
-       // Delete them all
+       return containers, nil
+}
+
+// DeleteContainers removes containers created by the benchmark.
+func DeleteContainers(c lxd.ContainerServer, parallel int) error {
+       batchSize, err := getBatchSize(parallel)
+       if err != nil {
+               return err
+       }
+
+       // List all the containers
+       containers, err := GetContainers(c)
+       if err != nil {
+               return err
+       }
+
        count := len(containers)
        logf("%d containers to delete", count)
 

From 311f9a30f0c3fdfb7cb3ac84ffc60fe369fbc245 Mon Sep 17 00:00:00 2001
From: Alberto Donato <alberto.don...@canonical.com>
Date: Tue, 5 Sep 2017 16:25:20 +0200
Subject: [PATCH 6/7] benchmark: add processBatch function, use it in
 SpawnContainers and DeleteContainers

Signed-off-by: Alberto Donato <alberto.don...@canonical.com>
---
 benchmark/benchmark.go | 144 ++++++++++++++++++++-----------------------------
 1 file changed, 57 insertions(+), 87 deletions(-)

diff --git a/benchmark/benchmark.go b/benchmark/benchmark.go
index 81bdcdda3..cb54ed9a2 100644
--- a/benchmark/benchmark.go
+++ b/benchmark/benchmark.go
@@ -43,9 +43,6 @@ func SpawnContainers(c lxd.ContainerServer, count int, 
parallel int, image strin
 
        printTestConfig(count, batchSize, image, privileged, freeze)
 
-       batches := count / batchSize
-       remainder := count % batchSize
-
        // Pre-load the image
        var fingerprint string
        if strings.Contains(image, ":") {
@@ -102,14 +99,8 @@ func SpawnContainers(c lxd.ContainerServer, count int, 
parallel int, image strin
                logf("Found image in local store: %s", fingerprint)
        }
 
-       // Start the containers
-       spawnedCount := 0
-       nameFormat := "benchmark-%." + fmt.Sprintf("%d", len(fmt.Sprintf("%d", 
count))) + "d"
-       wgBatch := sync.WaitGroup{}
-       nextStat := batchSize
-
-       startContainer := func(name string) {
-               defer wgBatch.Done()
+       startContainer := func(index int, wg *sync.WaitGroup) {
+               defer wg.Done()
 
                // Configure
                config := map[string]string{}
@@ -119,6 +110,8 @@ func SpawnContainers(c lxd.ContainerServer, count int, 
parallel int, image strin
                config["user.lxd-benchmark"] = "true"
 
                // Create
+               nameFormat := "benchmark-%." + fmt.Sprintf("%d", 
len(fmt.Sprintf("%d", count))) + "d"
+               name := fmt.Sprintf(nameFormat, index+1)
                req := api.ContainersPost{
                        Name: name,
                        Source: api.ContainerSource{
@@ -130,26 +123,26 @@ func SpawnContainers(c lxd.ContainerServer, count int, 
parallel int, image strin
 
                op, err := c.CreateContainer(req)
                if err != nil {
-                       logf(fmt.Sprintf("Failed to spawn container '%s': %s", 
name, err))
+                       logf("Failed to spawn container '%s': %s", name, err)
                        return
                }
 
                err = op.Wait()
                if err != nil {
-                       logf(fmt.Sprintf("Failed to spawn container '%s': %s", 
name, err))
+                       logf("Failed to spawn container '%s': %s", name, err)
                        return
                }
 
                // Start
                op, err = c.UpdateContainerState(name, 
api.ContainerStatePut{Action: "start", Timeout: -1}, "")
                if err != nil {
-                       logf(fmt.Sprintf("Failed to spawn container '%s': %s", 
name, err))
+                       logf("Failed to spawn container '%s': %s", name, err)
                        return
                }
 
                err = op.Wait()
                if err != nil {
-                       logf(fmt.Sprintf("Failed to spawn container '%s': %s", 
name, err))
+                       logf("Failed to spawn container '%s': %s", name, err)
                        return
                }
 
@@ -157,49 +150,19 @@ func SpawnContainers(c lxd.ContainerServer, count int, 
parallel int, image strin
                if freeze {
                        op, err := c.UpdateContainerState(name, 
api.ContainerStatePut{Action: "freeze", Timeout: -1}, "")
                        if err != nil {
-                               logf(fmt.Sprintf("Failed to spawn container 
'%s': %s", name, err))
+                               logf("Failed to spawn container '%s': %s", 
name, err)
                                return
                        }
 
                        err = op.Wait()
                        if err != nil {
-                               logf(fmt.Sprintf("Failed to spawn container 
'%s': %s", name, err))
+                               logf("Failed to spawn container '%s': %s", 
name, err)
                                return
                        }
                }
        }
 
-       logf("Starting the test")
-       timeStart := time.Now()
-
-       for i := 0; i < batches; i++ {
-               for j := 0; j < batchSize; j++ {
-                       spawnedCount = spawnedCount + 1
-                       name := fmt.Sprintf(nameFormat, spawnedCount)
-
-                       wgBatch.Add(1)
-                       go startContainer(name)
-               }
-               wgBatch.Wait()
-
-               if spawnedCount >= nextStat {
-                       interval := time.Since(timeStart).Seconds()
-                       logf("Started %d containers in %.3fs (%.3f/s)", 
spawnedCount, interval, float64(spawnedCount)/interval)
-                       nextStat = nextStat * 2
-               }
-       }
-
-       for k := 0; k < remainder; k++ {
-               spawnedCount = spawnedCount + 1
-               name := fmt.Sprintf(nameFormat, spawnedCount)
-
-               wgBatch.Add(1)
-               go startContainer(name)
-       }
-       wgBatch.Wait()
-
-       logf("Test completed in %.3fs", time.Since(timeStart).Seconds())
-
+       processBatch(count, batchSize, startContainer)
        return nil
 }
 
@@ -237,28 +200,24 @@ func DeleteContainers(c lxd.ContainerServer, parallel 
int) error {
        }
 
        count := len(containers)
-       logf("%d containers to delete", count)
+       logf("%d containers to delete", len(containers))
 
-       batches := count / batchSize
+       deleteContainer := func(index int, wg *sync.WaitGroup) {
+               defer wg.Done()
 
-       deletedCount := 0
-       wgBatch := sync.WaitGroup{}
-       nextStat := batchSize
-
-       deleteContainer := func(ct api.Container) {
-               defer wgBatch.Done()
+               ct := containers[index]
 
                // Stop
                if ct.IsActive() {
                        op, err := c.UpdateContainerState(ct.Name, 
api.ContainerStatePut{Action: "stop", Timeout: -1, Force: true}, "")
                        if err != nil {
-                               logf(fmt.Sprintf("Failed to delete container 
'%s': %s", ct.Name, err))
+                               logf("Failed to delete container '%s': %s", 
ct.Name, err)
                                return
                        }
 
                        err = op.Wait()
                        if err != nil {
-                               logf(fmt.Sprintf("Failed to delete container 
'%s': %s", ct.Name, err))
+                               logf("Failed to delete container '%s': %s", 
ct.Name, err)
                                return
                        }
                }
@@ -277,35 +236,7 @@ func DeleteContainers(c lxd.ContainerServer, parallel int) 
error {
                }
        }
 
-       logf("Starting the cleanup")
-       timeStart := time.Now()
-
-       for i := 0; i < batches; i++ {
-               for j := 0; j < batchSize; j++ {
-                       wgBatch.Add(1)
-                       go deleteContainer(containers[deletedCount])
-
-                       deletedCount = deletedCount + 1
-               }
-               wgBatch.Wait()
-
-               if deletedCount >= nextStat {
-                       interval := time.Since(timeStart).Seconds()
-                       logf("Deleted %d containers in %.3fs (%.3f/s)", 
deletedCount, interval, float64(deletedCount)/interval)
-                       nextStat = nextStat * 2
-               }
-       }
-
-       for k := deletedCount; k < count; k++ {
-               wgBatch.Add(1)
-               go deleteContainer(containers[deletedCount])
-
-               deletedCount = deletedCount + 1
-       }
-       wgBatch.Wait()
-
-       logf("Cleanup completed")
-
+       processBatch(count, batchSize, deleteContainer)
        return nil
 }
 
@@ -324,6 +255,45 @@ func getBatchSize(parallel int) (int, error) {
        return batchSize, nil
 }
 
+func processBatch(count int, batchSize int, process func(index int, wg 
*sync.WaitGroup)) time.Duration {
+       batches := count / batchSize
+       remainder := count % batchSize
+       processed := 0
+       wg := sync.WaitGroup{}
+       nextStat := batchSize
+
+       logf("Batch processing start")
+       timeStart := time.Now()
+
+       for i := 0; i < batches; i++ {
+               for j := 0; j < batchSize; j++ {
+                       wg.Add(1)
+                       go process(processed, &wg)
+                       processed++
+               }
+               wg.Wait()
+
+               if processed >= nextStat {
+                       interval := time.Since(timeStart).Seconds()
+                       logf("Processed %d containers in %.3fs (%.3f/s)", 
processed, interval, float64(processed)/interval)
+                       nextStat = nextStat * 2
+               }
+
+       }
+
+       for k := 0; k < remainder; k++ {
+               wg.Add(1)
+               go process(processed, &wg)
+               processed++
+       }
+       wg.Wait()
+
+       timeEnd := time.Now()
+       duration := timeEnd.Sub(timeStart)
+       logf("Batch processing completed in %.3fs", duration.Seconds())
+       return duration
+}
+
 func logf(format string, args ...interface{}) {
        fmt.Printf(fmt.Sprintf("[%s] %s\n", time.Now().Format(time.StampMilli), 
format), args...)
 }

From 093963f7b628ac4728005c56c6dca198d90557f9 Mon Sep 17 00:00:00 2001
From: Alberto Donato <alberto.don...@canonical.com>
Date: Tue, 5 Sep 2017 16:51:46 +0200
Subject: [PATCH 7/7] benchmark: extract ensureImage function

Signed-off-by: Alberto Donato <alberto.don...@canonical.com>
---
 benchmark/benchmark.go | 114 ++++++++++++++++++++++++++-----------------------
 1 file changed, 60 insertions(+), 54 deletions(-)

diff --git a/benchmark/benchmark.go b/benchmark/benchmark.go
index cb54ed9a2..eea503b8c 100644
--- a/benchmark/benchmark.go
+++ b/benchmark/benchmark.go
@@ -43,60 +43,9 @@ func SpawnContainers(c lxd.ContainerServer, count int, 
parallel int, image strin
 
        printTestConfig(count, batchSize, image, privileged, freeze)
 
-       // Pre-load the image
-       var fingerprint string
-       if strings.Contains(image, ":") {
-               var remote string
-               var err error
-
-               defaultConfig := config.DefaultConfig
-               defaultConfig.UserAgent = version.UserAgent
-
-               remote, fingerprint, err = defaultConfig.ParseRemote(image)
-               if err != nil {
-                       return err
-               }
-
-               d, err := defaultConfig.GetImageServer(remote)
-               if err != nil {
-                       return err
-               }
-
-               if fingerprint == "" {
-                       fingerprint = "default"
-               }
-
-               alias, _, err := d.GetImageAlias(fingerprint)
-               if err == nil {
-                       fingerprint = alias.Target
-               }
-
-               _, _, err = c.GetImage(fingerprint)
-               if err != nil {
-                       logf("Importing image into local store: %s", 
fingerprint)
-                       image, _, err := d.GetImage(fingerprint)
-                       if err != nil {
-                               logf(fmt.Sprintf("Failed to import image: %s", 
err))
-                               return err
-                       }
-
-                       op, err := c.CopyImage(d, *image, nil)
-                       if err != nil {
-                               logf(fmt.Sprintf("Failed to import image: %s", 
err))
-                               return err
-                       }
-
-                       err = op.Wait()
-                       if err != nil {
-                               logf(fmt.Sprintf("Failed to import image: %s", 
err))
-                               return err
-                       }
-               } else {
-                       logf("Found image in local store: %s", fingerprint)
-               }
-       } else {
-               fingerprint = image
-               logf("Found image in local store: %s", fingerprint)
+       fingerprint, err := ensureImage(c, image)
+       if err != nil {
+               return err
        }
 
        startContainer := func(index int, wg *sync.WaitGroup) {
@@ -255,6 +204,63 @@ func getBatchSize(parallel int) (int, error) {
        return batchSize, nil
 }
 
+func ensureImage(c lxd.ContainerServer, image string) (string, error) {
+       var fingerprint string
+
+       if strings.Contains(image, ":") {
+               defaultConfig := config.DefaultConfig
+               defaultConfig.UserAgent = version.UserAgent
+
+               remote, fp, err := defaultConfig.ParseRemote(image)
+               if err != nil {
+                       return "", err
+               }
+               fingerprint = fp
+
+               imageServer, err := defaultConfig.GetImageServer(remote)
+               if err != nil {
+                       return "", err
+               }
+
+               if fingerprint == "" {
+                       fingerprint = "default"
+               }
+
+               alias, _, err := imageServer.GetImageAlias(fingerprint)
+               if err == nil {
+                       fingerprint = alias.Target
+               }
+
+               _, _, err = c.GetImage(fingerprint)
+               if err != nil {
+                       logf("Importing image into local store: %s", 
fingerprint)
+                       image, _, err := imageServer.GetImage(fingerprint)
+                       if err != nil {
+                               logf("Failed to import image: %s", err)
+                               return "", err
+                       }
+
+                       op, err := c.CopyImage(imageServer, *image, nil)
+                       if err != nil {
+                               logf("Failed to import image: %s", err)
+                               return "", err
+                       }
+
+                       err = op.Wait()
+                       if err != nil {
+                               logf("Failed to import image: %s", err)
+                               return "", err
+                       }
+               } else {
+                       logf("Found image in local store: %s", fingerprint)
+               }
+       } else {
+               fingerprint = image
+               logf("Found image in local store: %s", fingerprint)
+       }
+       return fingerprint, nil
+}
+
 func processBatch(count int, batchSize int, process func(index int, wg 
*sync.WaitGroup)) time.Duration {
        batches := count / batchSize
        remainder := count % batchSize
_______________________________________________
lxc-devel mailing list
lxc-devel@lists.linuxcontainers.org
http://lists.linuxcontainers.org/listinfo/lxc-devel

Reply via email to