[lxc-devel] [lxd/master] Refactor lxd-benchmark

albertodonato on Github lxc-bot at linuxcontainers.org
Tue Sep 5 15:01:23 UTC 2017


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 503 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20170905/d8868148/attachment.bin>
-------------- next part --------------
From 2dfef81b8a04a2a8aa61ac4565757c04d77d5453 Mon Sep 17 00:00:00 2001
From: Alberto Donato <alberto.donato at 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.donato at 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.donato at 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.donato at 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.donato at 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.donato at 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.donato at 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.donato at 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.donato at 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.donato at 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.donato at 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.donato at 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.donato at 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.donato at 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


More information about the lxc-devel mailing list