[lxc-devel] [lxd/master] Bugfixes and tests
stgraber on Github
lxc-bot at linuxcontainers.org
Sun Apr 3 08:56:15 UTC 2016
A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 301 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20160403/94f090d3/attachment.bin>
-------------- next part --------------
From 130177131f8914e4a182fcf2482a59009ba91e32 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at ubuntu.com>
Date: Sun, 3 Apr 2016 03:00:33 -0400
Subject: [PATCH 1/2] Update bash completion to use --fast
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
That helps when dealing with hundreds of containers.
Signed-off-by: Stéphane Graber <stgraber at ubuntu.com>
---
config/bash/lxd-client | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/config/bash/lxd-client b/config/bash/lxd-client
index 97d46ef..631d7e8 100644
--- a/config/bash/lxd-client
+++ b/config/bash/lxd-client
@@ -4,7 +4,7 @@ _have lxc && {
_lxd_names()
{
COMPREPLY=( $( compgen -W \
- "$( lxc list | tail -n +4 | awk '{print $2}' | egrep -v '^(\||^$)' )" "$cur" )
+ "$( lxc list --fast | tail -n +4 | awk '{print $2}' | egrep -v '^(\||^$)' )" "$cur" )
)
}
From cb88ef949e3b4d81f50172fd09ef6a85ccfe5bfb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at ubuntu.com>
Date: Sun, 3 Apr 2016 04:55:46 -0400
Subject: [PATCH 2/2] tests: Add benchmarking tool
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Stéphane Graber <stgraber at ubuntu.com>
---
test/lxd-benchmark/main.go | 311 +++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 311 insertions(+)
create mode 100644 test/lxd-benchmark/main.go
diff --git a/test/lxd-benchmark/main.go b/test/lxd-benchmark/main.go
new file mode 100644
index 0000000..b3ce881
--- /dev/null
+++ b/test/lxd-benchmark/main.go
@@ -0,0 +1,311 @@
+package main
+
+import (
+ "fmt"
+ "io/ioutil"
+ "os"
+ "strings"
+ "sync"
+ "time"
+
+ "github.com/lxc/lxd"
+ "github.com/lxc/lxd/shared"
+ "github.com/lxc/lxd/shared/gnuflag"
+)
+
+var argCount = gnuflag.Int("count", 100, "Number of containers to create")
+var argImage = gnuflag.String("image", "ubuntu:", "Image to use for the test")
+var argPrivileged = gnuflag.Bool("privileged", false, "Use privileged containers")
+
+func main() {
+ err := run(os.Args)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "error: %s\n", err)
+ os.Exit(1)
+ }
+
+ os.Exit(0)
+}
+
+func run(args []string) error {
+ // Parse command line
+ gnuflag.Parse(true)
+
+ if len(os.Args) == 1 || !shared.StringInSlice(os.Args[1], []string{"spawn", "delete"}) {
+ fmt.Printf("Usage: %s spawn [--count=COUNT] [--image=IMAGE] [--privileged=BOOL]\n", os.Args[0])
+ fmt.Printf(" %s delete\n\n", os.Args[0])
+ gnuflag.Usage()
+ fmt.Printf("\n")
+ return fmt.Errorf("An action (spawn or delete) must be passed.")
+ }
+
+ // Connect to LXD
+ c, err := lxd.NewClient(&lxd.DefaultConfig, "local")
+ if err != nil {
+ return err
+ }
+
+ switch os.Args[1] {
+ case "spawn":
+ return spawnContainers(c, *argCount, *argImage, *argPrivileged)
+ case "delete":
+ return deleteContainers(c)
+ }
+
+ 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.Client, count int, image string, privileged bool) error {
+ // 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.ServerStatus()
+ if err != nil {
+ return err
+ }
+
+ privilegedStr := "unprivileged"
+ if privileged {
+ privilegedStr = "privileged"
+ }
+
+ 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(" 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
+ remote, fingerprint = lxd.DefaultConfig.ParseRemoteAndContainer(image)
+
+ if fingerprint == "" {
+ fingerprint = "default"
+ }
+
+ d, err := lxd.NewClient(&lxd.DefaultConfig, remote)
+ if err != nil {
+ return err
+ }
+
+ target := d.GetAlias(fingerprint)
+ if target != "" {
+ fingerprint = target
+ }
+
+ _, err = c.GetImageInfo(fingerprint)
+ if err != nil {
+ logf("Importing image into local store: %s", fingerprint)
+ err := d.CopyImage(fingerprint, c, false, nil, false, false, nil)
+ if err != nil {
+ 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
+ resp, err := c.Init(name, "local", fingerprint, nil, config, false)
+ if err != nil {
+ logf(fmt.Sprintf("Failed to spawn container '%s': %s", name, err))
+ return
+ }
+
+ err = c.WaitForSuccess(resp.Operation)
+ if err != nil {
+ logf(fmt.Sprintf("Failed to spawn container '%s': %s", name, err))
+ return
+ }
+
+ // Start
+ resp, err = c.Action(name, "start", -1, false, false)
+ if err != nil {
+ logf(fmt.Sprintf("Failed to spawn container '%s': %s", name, err))
+ return
+ }
+
+ err = c.WaitForSuccess(resp.Operation)
+ 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.Client) error {
+ // Detect the number of parallel actions
+ cpus, err := ioutil.ReadDir("/sys/bus/cpu/devices")
+ if err != nil {
+ return err
+ }
+
+ // List all the containers
+ allContainers, err := c.ListContainers()
+ if err != nil {
+ return err
+ }
+
+ containers := []shared.ContainerInfo{}
+ 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)
+
+ batch := len(cpus)
+ batches := count / batch
+
+ deletedCount := 0
+ wgBatch := sync.WaitGroup{}
+ nextStat := batch
+
+ deleteContainer := func(ct shared.ContainerInfo) {
+ defer wgBatch.Done()
+
+ // Stop
+ if ct.IsActive() {
+ resp, err := c.Action(ct.Name, "stop", -1, true, false)
+ if err != nil {
+ logf("Failed to delete container: %s", ct.Name)
+ return
+ }
+
+ err = c.WaitForSuccess(resp.Operation)
+ if err != nil {
+ logf("Failed to delete container: %s", ct.Name)
+ return
+ }
+ }
+
+ // Delete
+ resp, err := c.Delete(ct.Name)
+ if err != nil {
+ logf("Failed to delete container: %s", ct.Name)
+ return
+ }
+
+ err = c.WaitForSuccess(resp.Operation)
+ 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
+}
More information about the lxc-devel
mailing list