[lxc-devel] [lxd/master] Cluster performance improvements

stgraber on Github lxc-bot at linuxcontainers.org
Tue Aug 7 22:39:59 UTC 2018


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/20180807/77e31c3d/attachment.bin>
-------------- next part --------------
From 500c9d8e2e92fad5619332793736ff13505bea31 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at ubuntu.com>
Date: Tue, 7 Aug 2018 15:53:15 -0400
Subject: [PATCH 1/6] tests: Don't leak networks
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/suites/clustering.sh | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/test/suites/clustering.sh b/test/suites/clustering.sh
index f30bf0ce4b..fc909aa040 100644
--- a/test/suites/clustering.sh
+++ b/test/suites/clustering.sh
@@ -148,6 +148,9 @@ test_clustering_membership() {
   LXD_DIR="${LXD_ONE_DIR}" lxc cluster remove node3
   ! LXD_DIR="${LXD_FOUR_DIR}" lxc cluster list
 
+  # Cleanup network
+  LXD_DIR="${LXD_ONE_DIR}" lxc network delete net1 --target node2
+
   LXD_DIR="${LXD_FIVE_DIR}" lxd shutdown
   LXD_DIR="${LXD_FOUR_DIR}" lxd shutdown
   LXD_DIR="${LXD_TWO_DIR}" lxd shutdown

From 70cf50cc7b64435cbdd4b8465ba877ceee1e0438 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at ubuntu.com>
Date: Tue, 7 Aug 2018 16:04:44 -0400
Subject: [PATCH 2/6] lxd/containers: Add helpers for retrieving containers
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>
---
 lxd/container.go | 60 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 60 insertions(+)

diff --git a/lxd/container.go b/lxd/container.go
index b01640aa24..b245d1800d 100644
--- a/lxd/container.go
+++ b/lxd/container.go
@@ -1172,6 +1172,66 @@ func containerLoadByName(s *state.State, name string) (container, error) {
 	return containerLXCLoad(s, args)
 }
 
+func containerLoadAll(s *state.State) ([]container, error) {
+	// Get all the container arguments
+	var cts []db.ContainerArgs
+	err := s.Cluster.Transaction(func(tx *db.ClusterTx) error {
+		var err error
+		cts, err = tx.ContainerArgsList()
+		if err != nil {
+			return err
+		}
+
+		return nil
+	})
+	if err != nil {
+		return nil, err
+	}
+
+	// Load the container structs
+	containers := []container{}
+	for _, args := range cts {
+		ct, err := containerLXCLoad(s, args)
+		if err != nil {
+			return nil, err
+		}
+
+		containers = append(containers, ct)
+	}
+
+	return containers, nil
+}
+
+func containerLoadNodeAll(s *state.State) ([]container, error) {
+	// Get all the container arguments
+	var cts []db.ContainerArgs
+	err := s.Cluster.Transaction(func(tx *db.ClusterTx) error {
+		var err error
+		cts, err = tx.ContainerArgsNodeList()
+		if err != nil {
+			return err
+		}
+
+		return nil
+	})
+	if err != nil {
+		return nil, err
+	}
+
+	// Load the container structs
+	containers := []container{}
+	for _, args := range cts {
+		ct, err := containerLXCLoad(s, args)
+		if err != nil {
+			return nil, err
+		}
+
+		containers = append(containers, ct)
+	}
+
+	return containers, nil
+}
+
 func containerBackupLoadByName(s *state.State, name string) (*backup, error) {
 	// Get the DB record
 	args, err := s.Cluster.ContainerGetBackup(name)

From 2c27fdc8d3615a2c1d4ad21ee54b3ca70ed0ff88 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at ubuntu.com>
Date: Tue, 7 Aug 2018 16:35:21 -0400
Subject: [PATCH 3/6] lxd: Port over to new containerLoadNodeAll function
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>
---
 lxd/containers.go     | 39 ++++++++--------------------
 lxd/daemon.go         |  9 ++-----
 lxd/devices.go        | 59 +++++++++----------------------------------
 lxd/devlxd.go         | 10 ++------
 lxd/networks_utils.go | 22 ++++++----------
 5 files changed, 35 insertions(+), 104 deletions(-)

diff --git a/lxd/containers.go b/lxd/containers.go
index b7a171c441..47612014bf 100644
--- a/lxd/containers.go
+++ b/lxd/containers.go
@@ -125,19 +125,14 @@ func (slice containerAutostartList) Swap(i, j int) {
 
 func containersRestart(s *state.State) error {
 	// Get all the containers
-	result, err := s.Cluster.ContainersNodeList(db.CTypeRegular)
+	result, err := containerLoadNodeAll(s)
 	if err != nil {
 		return err
 	}
 
 	containers := []container{}
 
-	for _, name := range result {
-		c, err := containerLoadByName(s, name)
-		if err != nil {
-			return err
-		}
-
+	for _, c := range result {
 		containers = append(containers, c)
 	}
 
@@ -200,10 +195,11 @@ func containersShutdown(s *state.State) error {
 	dbAvailable := true
 
 	// Get all the containers
-	results, err := s.Cluster.ContainersNodeList(db.CTypeRegular)
+	containers, err := containerLoadNodeAll(s)
 	if err != nil {
 		// Mark database as offline
 		dbAvailable = false
+		containers = []container{}
 
 		// List all containers on disk
 		files, err := ioutil.ReadDir(shared.VarPath("containers"))
@@ -212,29 +208,16 @@ func containersShutdown(s *state.State) error {
 		}
 
 		for _, file := range files {
-			results = append(results, file.Name())
-		}
-	}
-
-	containers := []container{}
-
-	for _, name := range results {
-		var c container
-		var err error
-
-		if dbAvailable {
-			c, err = containerLoadByName(s, name)
-		} else {
-			c, err = containerLXCLoad(s, db.ContainerArgs{
-				Name:   name,
+			c, err := containerLXCLoad(s, db.ContainerArgs{
+				Name:   file.Name(),
 				Config: make(map[string]string),
 			})
-		}
-		if err != nil {
-			return err
-		}
+			if err != nil {
+				return err
+			}
 
-		containers = append(containers, c)
+			containers = append(containers, c)
+		}
 	}
 
 	sort.Sort(containerStopList(containers))
diff --git a/lxd/daemon.go b/lxd/daemon.go
index 354c493033..f8703fe4bc 100644
--- a/lxd/daemon.go
+++ b/lxd/daemon.go
@@ -696,18 +696,13 @@ func (d *Daemon) Ready() error {
 }
 
 func (d *Daemon) numRunningContainers() (int, error) {
-	results, err := d.cluster.ContainersNodeList(db.CTypeRegular)
+	results, err := containerLoadNodeAll(d.State())
 	if err != nil {
 		return 0, err
 	}
 
 	count := 0
-	for _, r := range results {
-		container, err := containerLoadByName(d.State(), r)
-		if err != nil {
-			continue
-		}
-
+	for _, container := range results {
 		if container.IsRunning() {
 			count = count + 1
 		}
diff --git a/lxd/devices.go b/lxd/devices.go
index fde7f1f174..493b14a06b 100644
--- a/lxd/devices.go
+++ b/lxd/devices.go
@@ -18,7 +18,6 @@ import (
 	"syscall"
 	"unsafe"
 
-	"github.com/lxc/lxd/lxd/db"
 	"github.com/lxc/lxd/lxd/state"
 	"github.com/lxc/lxd/lxd/sys"
 	"github.com/lxc/lxd/lxd/util"
@@ -652,7 +651,7 @@ func deviceTaskBalance(s *state.State) {
 	}
 
 	// Iterate through the containers
-	containers, err := s.Cluster.ContainersNodeList(db.CTypeRegular)
+	containers, err := containerLoadNodeAll(s)
 	if err != nil {
 		logger.Error("Problem loading containers list", log.Ctx{"err": err})
 		return
@@ -660,12 +659,7 @@ func deviceTaskBalance(s *state.State) {
 
 	fixedContainers := map[int][]container{}
 	balancedContainers := map[container]int{}
-	for _, name := range containers {
-		c, err := containerLoadByName(s, name)
-		if err != nil {
-			continue
-		}
-
+	for _, c := range containers {
 		conf := c.ExpandedConfig()
 		cpulimit, ok := conf["limits.cpu"]
 		if !ok || cpulimit == "" {
@@ -779,18 +773,12 @@ func deviceNetworkPriority(s *state.State, netif string) {
 		return
 	}
 
-	containers, err := s.Cluster.ContainersNodeList(db.CTypeRegular)
+	containers, err := containerLoadNodeAll(s)
 	if err != nil {
 		return
 	}
 
-	for _, name := range containers {
-		// Get the container struct
-		c, err := containerLoadByName(s, name)
-		if err != nil {
-			continue
-		}
-
+	for _, c := range containers {
 		// Extract the current priority
 		networkPriority := c.ExpandedConfig()["limits.network.priority"]
 		if networkPriority == "" {
@@ -810,18 +798,13 @@ func deviceNetworkPriority(s *state.State, netif string) {
 }
 
 func deviceUSBEvent(s *state.State, usb usbDevice) {
-	containers, err := s.Cluster.ContainersNodeList(db.CTypeRegular)
+	containers, err := containerLoadNodeAll(s)
 	if err != nil {
 		logger.Error("Problem loading containers list", log.Ctx{"err": err})
 		return
 	}
 
-	for _, name := range containers {
-		containerIf, err := containerLoadByName(s, name)
-		if err != nil {
-			continue
-		}
-
+	for _, containerIf := range containers {
 		c, ok := containerIf.(*containerLXC)
 		if !ok {
 			logger.Errorf("Got device event on non-LXC container?")
@@ -1858,19 +1841,13 @@ func deviceInotifyDirDeleteEvent(s *state.State, target *sys.InotifyTargetInfo)
 }
 
 func deviceInotifyDirRescan(s *state.State) {
-	containers, err := s.Cluster.ContainersNodeList(db.CTypeRegular)
+	containers, err := containerLoadNodeAll(s)
 	if err != nil {
 		logger.Errorf("Failed to load containers: %s", err)
 		return
 	}
 
-	for _, name := range containers {
-		containerIf, err := containerLoadByName(s, name)
-		if err != nil {
-			logger.Errorf("Failed to load container \"%s\": %s", name, err)
-			continue
-		}
-
+	for _, containerIf := range containers {
 		c, ok := containerIf.(*containerLXC)
 		if !ok {
 			logger.Errorf("Received device event on non-LXC container")
@@ -1923,7 +1900,7 @@ func deviceInotifyDirCreateEvent(s *state.State, target *sys.InotifyTargetInfo)
 		return
 	}
 
-	containers, err := s.Cluster.ContainersNodeList(db.CTypeRegular)
+	containers, err := containerLoadNodeAll(s)
 	if err != nil {
 		logger.Errorf("Failed to load containers: %s", err)
 		return
@@ -1936,13 +1913,7 @@ func deviceInotifyDirCreateEvent(s *state.State, target *sys.InotifyTargetInfo)
 	// ancestors
 	del := createAncestorPaths(targetName)
 	keep := []string{}
-	for _, name := range containers {
-		containerIf, err := containerLoadByName(s, name)
-		if err != nil {
-			logger.Errorf("Failed to load container \"%s\": %s", name, err)
-			continue
-		}
-
+	for _, containerIf := range containers {
 		c, ok := containerIf.(*containerLXC)
 		if !ok {
 			logger.Errorf("Received device event on non-LXC container")
@@ -2026,7 +1997,7 @@ func deviceInotifyFileEvent(s *state.State, target *sys.InotifyTargetInfo) {
 		return
 	}
 
-	containers, err := s.Cluster.ContainersNodeList(db.CTypeRegular)
+	containers, err := containerLoadNodeAll(s)
 	if err != nil {
 		logger.Errorf("Failed to load containers: %s", err)
 		return
@@ -2036,13 +2007,7 @@ func deviceInotifyFileEvent(s *state.State, target *sys.InotifyTargetInfo) {
 	hasWatchers := false
 	// The absolute path of the file for which we received an event?
 	targetName := filepath.Join(parent.Path, target.Path)
-	for _, name := range containers {
-		containerIf, err := containerLoadByName(s, name)
-		if err != nil {
-			logger.Errorf("Failed to load container \"%s\": %s", name, err)
-			continue
-		}
-
+	for _, containerIf := range containers {
 		c, ok := containerIf.(*containerLXC)
 		if !ok {
 			logger.Errorf("Received device event on non-LXC container")
diff --git a/lxd/devlxd.go b/lxd/devlxd.go
index cac19791e1..814e69747b 100644
--- a/lxd/devlxd.go
+++ b/lxd/devlxd.go
@@ -19,7 +19,6 @@ import (
 	"github.com/gorilla/websocket"
 	"github.com/pborman/uuid"
 
-	"github.com/lxc/lxd/lxd/db"
 	"github.com/lxc/lxd/lxd/util"
 	"github.com/lxc/lxd/shared"
 	"github.com/lxc/lxd/shared/logger"
@@ -467,17 +466,12 @@ func findContainerForPid(pid int32, d *Daemon) (container, error) {
 		return nil, err
 	}
 
-	containers, err := d.cluster.ContainersNodeList(db.CTypeRegular)
+	containers, err := containerLoadNodeAll(d.State())
 	if err != nil {
 		return nil, err
 	}
 
-	for _, container := range containers {
-		c, err := containerLoadByName(d.State(), container)
-		if err != nil {
-			return nil, err
-		}
-
+	for _, c := range containers {
 		if !c.IsRunning() {
 			continue
 		}
diff --git a/lxd/networks_utils.go b/lxd/networks_utils.go
index 4c63e29dd1..684614bbf0 100644
--- a/lxd/networks_utils.go
+++ b/lxd/networks_utils.go
@@ -758,12 +758,6 @@ func networkUpdateStatic(s *state.State, networkName string) error {
 	networkStaticLock.Lock()
 	defer networkStaticLock.Unlock()
 
-	// Get all the containers
-	containers, err := s.Cluster.ContainersNodeList(db.CTypeRegular)
-	if err != nil {
-		return err
-	}
-
 	// Get all the networks
 	var networks []string
 	if networkName == "" {
@@ -776,15 +770,15 @@ func networkUpdateStatic(s *state.State, networkName string) error {
 		networks = []string{networkName}
 	}
 
+	// Get all the containers
+	containers, err := containerLoadNodeAll(s)
+	if err != nil {
+		return err
+	}
+
 	// Build a list of dhcp host entries
 	entries := map[string][][]string{}
-	for _, cName := range containers {
-		// Load the container
-		c, err := containerLoadByName(s, cName)
-		if err != nil {
-			continue
-		}
-
+	for _, c := range containers {
 		// Go through all its devices (including profiles
 		for k, d := range c.ExpandedDevices() {
 			// Skip uninteresting entries
@@ -804,7 +798,7 @@ func networkUpdateStatic(s *state.State, networkName string) error {
 				entries[d["parent"]] = [][]string{}
 			}
 
-			entries[d["parent"]] = append(entries[d["parent"]], []string{d["hwaddr"], cName, d["ipv4.address"], d["ipv6.address"]})
+			entries[d["parent"]] = append(entries[d["parent"]], []string{d["hwaddr"], c.Name(), d["ipv4.address"], d["ipv6.address"]})
 		}
 	}
 

From b287a8ad09103d5b6eebef9189987ffa9e4daee6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at ubuntu.com>
Date: Tue, 7 Aug 2018 16:54:30 -0400
Subject: [PATCH 4/6] lxd: Port over to new containerLoadAll function
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>
---
 lxd/container_lxc.go         | 11 ++++-------
 lxd/networks.go              | 34 +++++++++-------------------------
 lxd/storage_volumes_utils.go | 31 ++++++++-----------------------
 3 files changed, 21 insertions(+), 55 deletions(-)

diff --git a/lxd/container_lxc.go b/lxd/container_lxc.go
index 1ae39c1034..a58811ddfd 100644
--- a/lxd/container_lxc.go
+++ b/lxd/container_lxc.go
@@ -780,7 +780,7 @@ func findIdmap(state *state.State, cName string, isolatedStr string, configBase
 	idmapLock.Lock()
 	defer idmapLock.Unlock()
 
-	cs, err := state.Cluster.ContainersList(db.CTypeRegular)
+	cts, err := containerLoadAll(state)
 	if err != nil {
 		return nil, 0, err
 	}
@@ -788,17 +788,14 @@ func findIdmap(state *state.State, cName string, isolatedStr string, configBase
 	offset := state.OS.IdmapSet.Idmap[0].Hostid + 65536
 
 	mapentries := idmap.ByHostid{}
-	for _, name := range cs {
+	for _, container := range cts {
+		name := container.Name()
+
 		/* Don't change our map Just Because. */
 		if name == cName {
 			continue
 		}
 
-		container, err := containerLoadByName(state, name)
-		if err != nil {
-			return nil, 0, err
-		}
-
 		if container.IsPrivileged() {
 			continue
 		}
diff --git a/lxd/networks.go b/lxd/networks.go
index 66b21a4c56..3c1ed5bd3b 100644
--- a/lxd/networks.go
+++ b/lxd/networks.go
@@ -421,19 +421,14 @@ func doNetworkGet(d *Daemon, name string) (api.Network, error) {
 
 	// Look for containers using the interface
 	if n.Type != "loopback" {
-		cts, err := d.cluster.ContainersList(db.CTypeRegular)
+		cts, err := containerLoadAll(d.State())
 		if err != nil {
 			return api.Network{}, err
 		}
 
-		for _, ct := range cts {
-			c, err := containerLoadByName(d.State(), ct)
-			if err != nil {
-				return api.Network{}, err
-			}
-
+		for _, c := range cts {
 			if networkIsInUse(c, n.Name) {
-				n.UsedBy = append(n.UsedBy, fmt.Sprintf("/%s/containers/%s", version.APIVersion, ct))
+				n.UsedBy = append(n.UsedBy, fmt.Sprintf("/%s/containers/%s", version.APIVersion, c.Name()))
 			}
 		}
 	}
@@ -685,19 +680,13 @@ func networkLeasesGet(d *Daemon, r *http.Request) Response {
 	leases := []api.NetworkLease{}
 
 	// Get all the containers
-	containers, err := d.cluster.ContainersList(db.CTypeRegular)
+	containers, err := containerLoadAll(d.State())
 	if err != nil {
 		return SmartError(err)
 	}
 
 	// Get static leases
-	for _, cName := range containers {
-		// Load the container
-		c, err := containerLoadByName(d.State(), cName)
-		if err != nil {
-			continue
-		}
-
+	for _, c := range containers {
 		// Go through all its devices (including profiles
 		for k, d := range c.ExpandedDevices() {
 			// Skip uninteresting entries
@@ -714,7 +703,7 @@ func networkLeasesGet(d *Daemon, r *http.Request) Response {
 			// Add the lease
 			if d["ipv4.address"] != "" {
 				leases = append(leases, api.NetworkLease{
-					Hostname: cName,
+					Hostname: c.Name(),
 					Address:  d["ipv4.address"],
 					Hwaddr:   d["hwaddr"],
 					Type:     "static",
@@ -723,7 +712,7 @@ func networkLeasesGet(d *Daemon, r *http.Request) Response {
 
 			if d["ipv6.address"] != "" {
 				leases = append(leases, api.NetworkLease{
-					Hostname: cName,
+					Hostname: c.Name(),
 					Address:  d["ipv6.address"],
 					Hwaddr:   d["hwaddr"],
 					Type:     "static",
@@ -870,17 +859,12 @@ func (n *network) IsRunning() bool {
 
 func (n *network) IsUsed() bool {
 	// Look for containers using the interface
-	cts, err := n.state.Cluster.ContainersList(db.CTypeRegular)
+	cts, err := containerLoadAll(n.state)
 	if err != nil {
 		return true
 	}
 
-	for _, ct := range cts {
-		c, err := containerLoadByName(n.state, ct)
-		if err != nil {
-			return true
-		}
-
+	for _, c := range cts {
 		if networkIsInUse(c, n.name) {
 			return true
 		}
diff --git a/lxd/storage_volumes_utils.go b/lxd/storage_volumes_utils.go
index f552976693..6c1916dfaa 100644
--- a/lxd/storage_volumes_utils.go
+++ b/lxd/storage_volumes_utils.go
@@ -174,19 +174,14 @@ func storagePoolVolumeUpdate(state *state.State, poolName string, volumeName str
 
 func storagePoolVolumeUsedByContainersGet(s *state.State, volumeName string,
 	volumeTypeName string) ([]string, error) {
-	cts, err := s.Cluster.ContainersList(db.CTypeRegular)
+	cts, err := containerLoadAll(s)
 	if err != nil {
 		return []string{}, err
 	}
 
 	ctsUsingVolume := []string{}
 	volumeNameWithType := fmt.Sprintf("%s/%s", volumeTypeName, volumeName)
-	for _, ct := range cts {
-		c, err := containerLoadByName(s, ct)
-		if err != nil {
-			continue
-		}
-
+	for _, c := range cts {
 		for _, dev := range c.LocalDevices() {
 			if dev["type"] != "disk" {
 				continue
@@ -196,7 +191,7 @@ func storagePoolVolumeUsedByContainersGet(s *state.State, volumeName string,
 			// "container////bla" but only against "container/bla".
 			cleanSource := filepath.Clean(dev["source"])
 			if cleanSource == volumeName || cleanSource == volumeNameWithType {
-				ctsUsingVolume = append(ctsUsingVolume, ct)
+				ctsUsingVolume = append(ctsUsingVolume, c.Name())
 			}
 		}
 	}
@@ -209,17 +204,12 @@ func storagePoolVolumeUpdateUsers(d *Daemon, oldPoolName string,
 
 	s := d.State()
 	// update all containers
-	cts, err := s.Cluster.ContainersList(db.CTypeRegular)
+	cts, err := containerLoadAll(s)
 	if err != nil {
 		return err
 	}
 
-	for _, ct := range cts {
-		c, err := containerLoadByName(s, ct)
-		if err != nil {
-			continue
-		}
-
+	for _, c := range cts {
 		devices := c.LocalDevices()
 		for k := range devices {
 			if devices[k]["type"] != "disk" {
@@ -344,19 +334,14 @@ func storagePoolVolumeUpdateUsers(d *Daemon, oldPoolName string,
 func storagePoolVolumeUsedByRunningContainersWithProfilesGet(s *state.State,
 	poolName string, volumeName string, volumeTypeName string,
 	runningOnly bool) ([]string, error) {
-	cts, err := s.Cluster.ContainersList(db.CTypeRegular)
+	cts, err := containerLoadAll(s)
 	if err != nil {
 		return []string{}, err
 	}
 
 	ctsUsingVolume := []string{}
 	volumeNameWithType := fmt.Sprintf("%s/%s", volumeTypeName, volumeName)
-	for _, ct := range cts {
-		c, err := containerLoadByName(s, ct)
-		if err != nil {
-			continue
-		}
-
+	for _, c := range cts {
 		if runningOnly && !c.IsRunning() {
 			continue
 		}
@@ -374,7 +359,7 @@ func storagePoolVolumeUsedByRunningContainersWithProfilesGet(s *state.State,
 			// "container////bla" but only against "container/bla".
 			cleanSource := filepath.Clean(dev["source"])
 			if cleanSource == volumeName || cleanSource == volumeNameWithType {
-				ctsUsingVolume = append(ctsUsingVolume, ct)
+				ctsUsingVolume = append(ctsUsingVolume, c.Name())
 			}
 		}
 	}

From 89498fb5198f5343f58c8a2ffb1e5c0e98df7103 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at ubuntu.com>
Date: Tue, 7 Aug 2018 17:43:13 -0400
Subject: [PATCH 5/6] lxd: Only get the profiles once
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>
---
 lxd/container.go       | 48 ++++++++++++++++++++++++-----------
 lxd/container_lxc.go   | 57 ++++++++++++++++++++++++++++--------------
 lxd/containers.go      |  2 +-
 lxd/containers_post.go |  2 +-
 4 files changed, 74 insertions(+), 35 deletions(-)

diff --git a/lxd/container.go b/lxd/container.go
index b245d1800d..52349b711c 100644
--- a/lxd/container.go
+++ b/lxd/container.go
@@ -1169,7 +1169,7 @@ func containerLoadByName(s *state.State, name string) (container, error) {
 		return nil, err
 	}
 
-	return containerLXCLoad(s, args)
+	return containerLXCLoad(s, args, nil)
 }
 
 func containerLoadAll(s *state.State) ([]container, error) {
@@ -1188,18 +1188,7 @@ func containerLoadAll(s *state.State) ([]container, error) {
 		return nil, err
 	}
 
-	// Load the container structs
-	containers := []container{}
-	for _, args := range cts {
-		ct, err := containerLXCLoad(s, args)
-		if err != nil {
-			return nil, err
-		}
-
-		containers = append(containers, ct)
-	}
-
-	return containers, nil
+	return containerLoadAllInternal(cts, s)
 }
 
 func containerLoadNodeAll(s *state.State) ([]container, error) {
@@ -1218,10 +1207,41 @@ func containerLoadNodeAll(s *state.State) ([]container, error) {
 		return nil, err
 	}
 
+	return containerLoadAllInternal(cts, s)
+}
+
+func containerLoadAllInternal(cts []db.ContainerArgs, s *state.State) ([]container, error) {
+	// Figure out what profiles are in use
+	profiles := map[string]api.Profile{}
+	for _, cArgs := range cts {
+		for _, profile := range cArgs.Profiles {
+			_, ok := profiles[profile]
+			if !ok {
+				profiles[profile] = api.Profile{}
+			}
+		}
+	}
+
+	// Get the profile data
+	for name := range profiles {
+		_, profile, err := s.Cluster.ProfileGet(name)
+		if err != nil {
+			return nil, err
+		}
+
+		profiles[name] = *profile
+	}
+
 	// Load the container structs
 	containers := []container{}
 	for _, args := range cts {
-		ct, err := containerLXCLoad(s, args)
+		// Figure out the container's profiles
+		cProfiles := []api.Profile{}
+		for _, name := range args.Profiles {
+			cProfiles = append(cProfiles, profiles[name])
+		}
+
+		ct, err := containerLXCLoad(s, args, cProfiles)
 		if err != nil {
 			return nil, err
 		}
diff --git a/lxd/container_lxc.go b/lxd/container_lxc.go
index a58811ddfd..cde7b09e38 100644
--- a/lxd/container_lxc.go
+++ b/lxd/container_lxc.go
@@ -467,15 +467,20 @@ func containerLXCCreate(s *state.State, args db.ContainerArgs) (container, error
 	return c, nil
 }
 
-func containerLXCLoad(s *state.State, args db.ContainerArgs) (container, error) {
+func containerLXCLoad(s *state.State, args db.ContainerArgs, profiles []api.Profile) (container, error) {
 	// Create the container struct
 	c := containerLXCInstantiate(s, args)
 
 	// Setup finalizer
 	runtime.SetFinalizer(c, containerLXCUnload)
 
-	// Load the config.
-	err := c.init()
+	// Expand config and devices
+	err := c.expandConfig(profiles)
+	if err != nil {
+		return nil, err
+	}
+
+	err = c.expandDevices(profiles)
 	if err != nil {
 		return nil, err
 	}
@@ -868,12 +873,12 @@ func findIdmap(state *state.State, cName string, isolatedStr string, configBase
 
 func (c *containerLXC) init() error {
 	// Compute the expanded config and device list
-	err := c.expandConfig()
+	err := c.expandConfig(nil)
 	if err != nil {
 		return err
 	}
 
-	err = c.expandDevices()
+	err = c.expandDevices(nil)
 	if err != nil {
 		return err
 	}
@@ -1717,17 +1722,23 @@ func (c *containerLXC) initStorage() error {
 }
 
 // Config handling
-func (c *containerLXC) expandConfig() error {
+func (c *containerLXC) expandConfig(profiles []api.Profile) error {
 	// Fetch profile configs
 	profileConfigs := make([]map[string]string, len(c.profiles))
 
 	// Apply all the profiles
-	for i, name := range c.profiles {
-		profileConfig, err := c.state.Cluster.ProfileConfig(name)
-		if err != nil {
-			return err
+	if profiles != nil {
+		for i, profile := range profiles {
+			profileConfigs[i] = profile.Config
+		}
+	} else {
+		for i, name := range c.profiles {
+			profileConfig, err := c.state.Cluster.ProfileConfig(name)
+			if err != nil {
+				return err
+			}
+			profileConfigs[i] = profileConfig
 		}
-		profileConfigs[i] = profileConfig
 	}
 
 	c.expandConfigFromProfiles(profileConfigs)
@@ -1754,15 +1765,23 @@ func (c *containerLXC) expandConfigFromProfiles(profileConfigs []map[string]stri
 	c.expandedConfig = config
 }
 
-func (c *containerLXC) expandDevices() error {
+func (c *containerLXC) expandDevices(profiles []api.Profile) error {
 	// Fetch profile devices
 	profileDevices := make([]types.Devices, len(c.profiles))
-	for _, p := range c.profiles {
-		devices, err := c.state.Cluster.Devices(p, true)
-		if err != nil {
-			return err
+
+	// Apply all the profiles
+	if profiles != nil {
+		for i, profile := range profiles {
+			profileDevices[i] = profile.Devices
+		}
+	} else {
+		for _, p := range c.profiles {
+			devices, err := c.state.Cluster.Devices(p, true)
+			if err != nil {
+				return err
+			}
+			profileDevices = append(profileDevices, devices)
 		}
-		profileDevices = append(profileDevices, devices)
 	}
 
 	c.expandDevicesFromProfiles(profileDevices)
@@ -3815,12 +3834,12 @@ func (c *containerLXC) Update(args db.ContainerArgs, userRequested bool) error {
 	c.profiles = args.Profiles
 
 	// Expand the config and refresh the LXC config
-	err = c.expandConfig()
+	err = c.expandConfig(nil)
 	if err != nil {
 		return err
 	}
 
-	err = c.expandDevices()
+	err = c.expandDevices(nil)
 	if err != nil {
 		return err
 	}
diff --git a/lxd/containers.go b/lxd/containers.go
index 47612014bf..b2e2b4ae0b 100644
--- a/lxd/containers.go
+++ b/lxd/containers.go
@@ -211,7 +211,7 @@ func containersShutdown(s *state.State) error {
 			c, err := containerLXCLoad(s, db.ContainerArgs{
 				Name:   file.Name(),
 				Config: make(map[string]string),
-			})
+			}, nil)
 			if err != nil {
 				return err
 			}
diff --git a/lxd/containers_post.go b/lxd/containers_post.go
index 26db451744..202a4eb4ea 100644
--- a/lxd/containers_post.go
+++ b/lxd/containers_post.go
@@ -321,7 +321,7 @@ func createFromMigration(d *Daemon, req *api.ContainersPost) Response {
 		}
 	} else {
 		// Retrieve the future storage pool
-		cM, err := containerLXCLoad(d.State(), args)
+		cM, err := containerLXCLoad(d.State(), args, nil)
 		if err != nil {
 			return InternalError(err)
 		}

From 15d380b1ec963399bc31599bfbee33064b4f3765 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at ubuntu.com>
Date: Tue, 7 Aug 2018 18:25:32 -0400
Subject: [PATCH 6/6] lxd/containers: Speed up recursive list
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>
---
 lxd/containers_get.go | 26 ++++++++++++++++++++------
 1 file changed, 20 insertions(+), 6 deletions(-)

diff --git a/lxd/containers_get.go b/lxd/containers_get.go
index 3a4fe94e4d..6afd27f34b 100644
--- a/lxd/containers_get.go
+++ b/lxd/containers_get.go
@@ -40,6 +40,12 @@ func containersGet(d *Daemon, r *http.Request) Response {
 }
 
 func doContainersGet(d *Daemon, r *http.Request) (interface{}, error) {
+	recursion := util.IsRecursionRequest(r)
+	resultString := []string{}
+	resultList := []*api.Container{}
+	resultMu := sync.Mutex{}
+
+	// Get the list and location of all containers
 	var result map[string][]string // Containers by node address
 	var nodes map[string]string    // Node names by container
 	err := d.cluster.Transaction(func(tx *db.ClusterTx) error {
@@ -61,10 +67,18 @@ func doContainersGet(d *Daemon, r *http.Request) (interface{}, error) {
 		return []string{}, err
 	}
 
-	recursion := util.IsRecursionRequest(r)
-	resultString := []string{}
-	resultList := []*api.Container{}
-	resultMu := sync.Mutex{}
+	// Get the local containers
+	nodeCts := map[string]container{}
+	if recursion {
+		cts, err := containerLoadNodeAll(d.State())
+		if err != nil {
+			return nil, err
+		}
+
+		for _, ct := range cts {
+			nodeCts[ct.Name()] = ct
+		}
+	}
 
 	resultAppend := func(name string, c api.Container, err error) {
 		if err != nil {
@@ -130,11 +144,11 @@ func doContainersGet(d *Daemon, r *http.Request) (interface{}, error) {
 				continue
 			}
 
-			c, err := doContainerGet(d.State(), container)
+			c, _, err := nodeCts[container].Render()
 			if err != nil {
 				resultAppend(container, api.Container{}, err)
 			} else {
-				resultAppend(container, *c, err)
+				resultAppend(container, *c.(*api.Container), err)
 			}
 		}
 	}


More information about the lxc-devel mailing list