[lxc-devel] [lxd/master] API Instances Command

tomponline on Github lxc-bot at linuxcontainers.org
Fri Sep 6 13:35:53 UTC 2019


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 513 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20190906/ffd5a994/attachment-0001.bin>
-------------- next part --------------
From 074cc355a89dc10aca3823ca1f2b0d1c770b28c2 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Fri, 6 Sep 2019 11:38:53 +0100
Subject: [PATCH 01/12] lxd/api: Contructs endpoint alias routes

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/api.go | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/lxd/api.go b/lxd/api.go
index fb19a9975c..a22297b741 100644
--- a/lxd/api.go
+++ b/lxd/api.go
@@ -31,6 +31,16 @@ func RestServer(d *Daemon) *http.Server {
 
 	for _, c := range api10 {
 		d.createCmd(mux, "1.0", c)
+
+		// Create any alias endpoints using the same handlers as the parent endpoint but
+		// with a different path and name (so the handler can differentiate being called via
+		// a different endpoint) if it wants to.
+		for _, alias := range c.Aliases {
+			ac := c
+			ac.Name = alias.Name
+			ac.Path = alias.Path
+			d.createCmd(mux, "1.0", ac)
+		}
 	}
 
 	for _, c := range apiInternal {

From 2f222f2671ed752d3b701a5d83e51a70c423e917 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Fri, 6 Sep 2019 11:42:50 +0100
Subject: [PATCH 02/12] lxd/containers/get: Fixes comment

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/containers_get.go | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lxd/containers_get.go b/lxd/containers_get.go
index d93190be33..e45c458aa5 100644
--- a/lxd/containers_get.go
+++ b/lxd/containers_get.go
@@ -28,7 +28,7 @@ func containersGet(d *Daemon, r *http.Request) Response {
 			logger.Debugf("DBERR: containersGet: error %q", err)
 			return SmartError(err)
 		}
-		// 1 s may seem drastic, but we really don't want to thrash
+		// 100 ms may seem drastic, but we really don't want to thrash
 		// perhaps we should use a random amount
 		time.Sleep(100 * time.Millisecond)
 	}

From 6d8941cf091863c653564eaeabbead28831fe824 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Fri, 6 Sep 2019 11:43:15 +0100
Subject: [PATCH 03/12] lxd/api/1.0: Renames container endpoint vars to
 instance prefix

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/api_1.0.go | 30 +++++++++++++++---------------
 1 file changed, 15 insertions(+), 15 deletions(-)

diff --git a/lxd/api_1.0.go b/lxd/api_1.0.go
index 0b6e1a9db4..9bc2db9ea0 100644
--- a/lxd/api_1.0.go
+++ b/lxd/api_1.0.go
@@ -36,21 +36,21 @@ var api10 = []APIEndpoint{
 	clusterCmd,
 	clusterNodeCmd,
 	clusterNodesCmd,
-	containerBackupCmd,
-	containerBackupExportCmd,
-	containerBackupsCmd,
-	containerCmd,
-	containerConsoleCmd,
-	containerExecCmd,
-	containerFileCmd,
-	containerLogCmd,
-	containerLogsCmd,
-	containerMetadataCmd,
-	containerMetadataTemplatesCmd,
-	containersCmd,
-	containerSnapshotCmd,
-	containerSnapshotsCmd,
-	containerStateCmd,
+	instanceBackupCmd,
+	instanceBackupExportCmd,
+	instanceBackupsCmd,
+	instanceCmd,
+	instanceConsoleCmd,
+	instanceExecCmd,
+	instanceFileCmd,
+	instanceLogCmd,
+	instanceLogsCmd,
+	instanceMetadataCmd,
+	instanceMetadataTemplatesCmd,
+	instancesCmd,
+	instanceSnapshotCmd,
+	instanceSnapshotsCmd,
+	instanceStateCmd,
 	eventsCmd,
 	imageAliasCmd,
 	imageAliasesCmd,

From 26fca57b2a023299fb2b9aae013a0f3949e881e6 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Fri, 6 Sep 2019 11:44:26 +0100
Subject: [PATCH 04/12] lxd/daemon: Adds Name and Aliases support to
 APIEndpoint

The APIEndpoint struct has been modified as follows:

- Repurposes the Name field to represent the name of the endpoint rather than the path.
- Adds a Path field which is what the previous Name field was used for - the path template of the endpoint.
- Add an Aliases field which is contructed of APIEndpointAlias structs - allowing for a single endpoint to be exposed over multiple endpoint paths, each with a separate name.

This field naming scheme matches the concepts used in the gorilla mux router we use.

The purpose of this change is:

- To allow a single set of handler functions to be exposed at multiple URL paths.
- To allow the handler functions to access the name of the endpoint used to access the handler function. In case they need to behave differently based on which path was used (but without having to rely on examing the raw URL path which is unreliable).

The name of the endpoint can be accessed inside the handler function as follows:

 mux.CurrentRoute().GetName()

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/daemon.go | 34 ++++++++++++++++++++++++----------
 1 file changed, 24 insertions(+), 10 deletions(-)

diff --git a/lxd/daemon.go b/lxd/daemon.go
index dca17d0f9a..583af879ad 100644
--- a/lxd/daemon.go
+++ b/lxd/daemon.go
@@ -155,12 +155,20 @@ func DefaultDaemon() *Daemon {
 
 // APIEndpoint represents a URL in our API.
 type APIEndpoint struct {
-	Name   string
-	Get    APIEndpointAction
-	Put    APIEndpointAction
-	Post   APIEndpointAction
-	Delete APIEndpointAction
-	Patch  APIEndpointAction
+	Name    string             // Name for this endpoint.
+	Path    string             // Path pattern for this endpoint.
+	Aliases []APIEndpointAlias // Any aliases for this endpoint.
+	Get     APIEndpointAction
+	Put     APIEndpointAction
+	Post    APIEndpointAction
+	Delete  APIEndpointAction
+	Patch   APIEndpointAction
+}
+
+// APIEndpointAlias represents an alias URL of and APIEndpoint in our API.
+type APIEndpointAlias struct {
+	Name string // Name for this alias.
+	Path string // Path pattern for this alias.
 }
 
 // APIEndpointAction represents an action on an API endpoint.
@@ -338,13 +346,13 @@ func (d *Daemon) UnixSocket() string {
 
 func (d *Daemon) createCmd(restAPI *mux.Router, version string, c APIEndpoint) {
 	var uri string
-	if c.Name == "" {
+	if c.Path == "" {
 		uri = fmt.Sprintf("/%s", version)
 	} else {
-		uri = fmt.Sprintf("/%s/%s", version, c.Name)
+		uri = fmt.Sprintf("/%s/%s", version, c.Path)
 	}
 
-	restAPI.HandleFunc(uri, func(w http.ResponseWriter, r *http.Request) {
+	route := restAPI.HandleFunc(uri, func(w http.ResponseWriter, r *http.Request) {
 		w.Header().Set("Content-Type", "application/json")
 
 		// Block public API requests until we're done with basic
@@ -371,7 +379,7 @@ func (d *Daemon) createCmd(restAPI *mux.Router, version string, c APIEndpoint) {
 		// Reject internal queries to remote, non-cluster, clients
 		if version == "internal" && !shared.StringInSlice(protocol, []string{"unix", "cluster"}) {
 			// Except for the initial cluster accept request (done over trusted TLS)
-			if !trusted || c.Name != "cluster/accept" || protocol != "tls" {
+			if !trusted || c.Path != "cluster/accept" || protocol != "tls" {
 				logger.Warn("Rejecting remote internal API request", log.Ctx{"ip": r.RemoteAddr})
 				Forbidden(nil).Render(w)
 				return
@@ -455,6 +463,12 @@ func (d *Daemon) createCmd(restAPI *mux.Router, version string, c APIEndpoint) {
 			}
 		}
 	})
+
+	// If the endpoint has a canonical name then record it so it can be used to build URLS
+	// and accessed in the context of the request by the handler function.
+	if c.Name != "" {
+		route.Name(c.Name)
+	}
 }
 
 // have we setup shared mounts?

From e3ff0f938b25930acbbb815c536e7f10556db029 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Fri, 6 Sep 2019 11:52:45 +0100
Subject: [PATCH 05/12] lxd: Adds /1.0/instances aliases of /1.0/containers
 routes

 - Renames Name property to Path property for all routes
 - Renames container routes to instance prefix
 - Adds new Name property to all instance endpoints
 - Adds equivalent container alias to all instance routes

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/api_cluster.go              | 12 ++---
 lxd/api_internal.go             | 18 ++++----
 lxd/api_project.go              |  4 +-
 lxd/certificates.go             |  4 +-
 lxd/container_logs.go           | 12 +++--
 lxd/container_post.go           |  2 +-
 lxd/containers.go               | 78 ++++++++++++++++++++++-----------
 lxd/events.go                   |  2 +-
 lxd/images.go                   | 14 +++---
 lxd/networks.go                 |  8 ++--
 lxd/operations.go               |  8 ++--
 lxd/profiles.go                 |  4 +-
 lxd/resources.go                |  4 +-
 lxd/storage_pools.go            |  4 +-
 lxd/storage_volumes.go          | 10 ++---
 lxd/storage_volumes_snapshot.go |  4 +-
 16 files changed, 109 insertions(+), 79 deletions(-)

diff --git a/lxd/api_cluster.go b/lxd/api_cluster.go
index 0caabc658c..544d417894 100644
--- a/lxd/api_cluster.go
+++ b/lxd/api_cluster.go
@@ -29,20 +29,20 @@ import (
 )
 
 var clusterCmd = APIEndpoint{
-	Name: "cluster",
+	Path: "cluster",
 
 	Get: APIEndpointAction{Handler: clusterGet, AccessHandler: AllowAuthenticated},
 	Put: APIEndpointAction{Handler: clusterPut},
 }
 
 var clusterNodesCmd = APIEndpoint{
-	Name: "cluster/members",
+	Path: "cluster/members",
 
 	Get: APIEndpointAction{Handler: clusterNodesGet, AccessHandler: AllowAuthenticated},
 }
 
 var clusterNodeCmd = APIEndpoint{
-	Name: "cluster/members/{name}",
+	Path: "cluster/members/{name}",
 
 	Delete: APIEndpointAction{Handler: clusterNodeDelete},
 	Get:    APIEndpointAction{Handler: clusterNodeGet, AccessHandler: AllowAuthenticated},
@@ -50,19 +50,19 @@ var clusterNodeCmd = APIEndpoint{
 }
 
 var internalClusterAcceptCmd = APIEndpoint{
-	Name: "cluster/accept",
+	Path: "cluster/accept",
 
 	Post: APIEndpointAction{Handler: internalClusterPostAccept},
 }
 
 var internalClusterRebalanceCmd = APIEndpoint{
-	Name: "cluster/rebalance",
+	Path: "cluster/rebalance",
 
 	Post: APIEndpointAction{Handler: internalClusterPostRebalance},
 }
 
 var internalClusterPromoteCmd = APIEndpoint{
-	Name: "cluster/promote",
+	Path: "cluster/promote",
 
 	Post: APIEndpointAction{Handler: internalClusterPostPromote},
 }
diff --git a/lxd/api_internal.go b/lxd/api_internal.go
index 758f17b407..1f48eda280 100644
--- a/lxd/api_internal.go
+++ b/lxd/api_internal.go
@@ -48,56 +48,56 @@ var apiInternal = []APIEndpoint{
 }
 
 var internalShutdownCmd = APIEndpoint{
-	Name: "shutdown",
+	Path: "shutdown",
 
 	Put: APIEndpointAction{Handler: internalShutdown},
 }
 
 var internalReadyCmd = APIEndpoint{
-	Name: "ready",
+	Path: "ready",
 
 	Get: APIEndpointAction{Handler: internalWaitReady},
 }
 
 var internalContainerOnStartCmd = APIEndpoint{
-	Name: "containers/{id}/onstart",
+	Path: "containers/{id}/onstart",
 
 	Get: APIEndpointAction{Handler: internalContainerOnStart},
 }
 
 var internalContainerOnStopNSCmd = APIEndpoint{
-	Name: "containers/{id}/onstopns",
+	Path: "containers/{id}/onstopns",
 
 	Get: APIEndpointAction{Handler: internalContainerOnStopNS},
 }
 
 var internalContainerOnStopCmd = APIEndpoint{
-	Name: "containers/{id}/onstop",
+	Path: "containers/{id}/onstop",
 
 	Get: APIEndpointAction{Handler: internalContainerOnStop},
 }
 
 var internalSQLCmd = APIEndpoint{
-	Name: "sql",
+	Path: "sql",
 
 	Get:  APIEndpointAction{Handler: internalSQLGet},
 	Post: APIEndpointAction{Handler: internalSQLPost},
 }
 
 var internalContainersCmd = APIEndpoint{
-	Name: "containers",
+	Path: "containers",
 
 	Post: APIEndpointAction{Handler: internalImport},
 }
 
 var internalGarbageCollectorCmd = APIEndpoint{
-	Name: "gc",
+	Path: "gc",
 
 	Get: APIEndpointAction{Handler: internalGC},
 }
 
 var internalRAFTSnapshotCmd = APIEndpoint{
-	Name: "raft-snapshot",
+	Path: "raft-snapshot",
 
 	Get: APIEndpointAction{Handler: internalRAFTSnapshot},
 }
diff --git a/lxd/api_project.go b/lxd/api_project.go
index 12f0e76d2c..4802c1d15c 100644
--- a/lxd/api_project.go
+++ b/lxd/api_project.go
@@ -19,14 +19,14 @@ import (
 )
 
 var projectsCmd = APIEndpoint{
-	Name: "projects",
+	Path: "projects",
 
 	Get:  APIEndpointAction{Handler: projectsGet, AccessHandler: AllowAuthenticated},
 	Post: APIEndpointAction{Handler: projectsPost},
 }
 
 var projectCmd = APIEndpoint{
-	Name: "projects/{name}",
+	Path: "projects/{name}",
 
 	Delete: APIEndpointAction{Handler: projectDelete},
 	Get:    APIEndpointAction{Handler: projectGet, AccessHandler: AllowAuthenticated},
diff --git a/lxd/certificates.go b/lxd/certificates.go
index 2eb131311f..3630ec59ec 100644
--- a/lxd/certificates.go
+++ b/lxd/certificates.go
@@ -25,14 +25,14 @@ import (
 )
 
 var certificatesCmd = APIEndpoint{
-	Name: "certificates",
+	Path: "certificates",
 
 	Get:  APIEndpointAction{Handler: certificatesGet, AccessHandler: AllowAuthenticated},
 	Post: APIEndpointAction{Handler: certificatesPost, AllowUntrusted: true},
 }
 
 var certificateCmd = APIEndpoint{
-	Name: "certificates/{fingerprint}",
+	Path: "certificates/{fingerprint}",
 
 	Delete: APIEndpointAction{Handler: certificateDelete},
 	Get:    APIEndpointAction{Handler: certificateGet, AccessHandler: AllowAuthenticated},
diff --git a/lxd/container_logs.go b/lxd/container_logs.go
index 54d2d478ba..92e026d3e2 100644
--- a/lxd/container_logs.go
+++ b/lxd/container_logs.go
@@ -13,15 +13,19 @@ import (
 	"github.com/lxc/lxd/shared/version"
 )
 
-var containerLogCmd = APIEndpoint{
-	Name: "containers/{name}/logs/{file}",
+var instanceLogCmd = APIEndpoint{
+	Name:    "instanceLog",
+	Path:    "instances/{name}/logs/{file}",
+	Aliases: []APIEndpointAlias{{Name: "containerLog", Path: "containers/{name}/logs/{file}"}},
 
 	Delete: APIEndpointAction{Handler: containerLogDelete, AccessHandler: AllowProjectPermission("containers", "operate-containers")},
 	Get:    APIEndpointAction{Handler: containerLogGet, AccessHandler: AllowProjectPermission("containers", "view")},
 }
 
-var containerLogsCmd = APIEndpoint{
-	Name: "containers/{name}/logs",
+var instanceLogsCmd = APIEndpoint{
+	Name:    "instanceLogs",
+	Path:    "instances/{name}/logs",
+	Aliases: []APIEndpointAlias{{Name: "containerLogs", Path: "containers/{name}/logs"}},
 
 	Get: APIEndpointAction{Handler: containerLogsGet, AccessHandler: AllowProjectPermission("containers", "view")},
 }
diff --git a/lxd/container_post.go b/lxd/container_post.go
index 8bd33b2dfa..3865b646c1 100644
--- a/lxd/container_post.go
+++ b/lxd/container_post.go
@@ -21,7 +21,7 @@ import (
 )
 
 var internalClusterContainerMovedCmd = APIEndpoint{
-	Name: "cluster/container-moved/{name}",
+	Path: "cluster/container-moved/{name}",
 
 	Post: APIEndpointAction{Handler: internalClusterContainerMovedPost},
 }
diff --git a/lxd/containers.go b/lxd/containers.go
index 7468e3c33f..2bbb0897c1 100644
--- a/lxd/containers.go
+++ b/lxd/containers.go
@@ -16,15 +16,19 @@ import (
 	log "github.com/lxc/lxd/shared/log15"
 )
 
-var containersCmd = APIEndpoint{
-	Name: "containers",
+var instancesCmd = APIEndpoint{
+	Name:    "instances",
+	Path:    "instances",
+	Aliases: []APIEndpointAlias{{Name: "containers", Path: "containers"}},
 
 	Get:  APIEndpointAction{Handler: containersGet, AccessHandler: AllowProjectPermission("containers", "view")},
 	Post: APIEndpointAction{Handler: containersPost, AccessHandler: AllowProjectPermission("containers", "manage-containers")},
 }
 
-var containerCmd = APIEndpoint{
-	Name: "containers/{name}",
+var instanceCmd = APIEndpoint{
+	Name:    "instance",
+	Path:    "instances/{name}",
+	Aliases: []APIEndpointAlias{{Name: "container", Path: "containers/{name}"}},
 
 	Get:    APIEndpointAction{Handler: containerGet, AccessHandler: AllowProjectPermission("containers", "view")},
 	Put:    APIEndpointAction{Handler: containerPut, AccessHandler: AllowProjectPermission("containers", "manage-containers")},
@@ -33,30 +37,38 @@ var containerCmd = APIEndpoint{
 	Patch:  APIEndpointAction{Handler: containerPatch, AccessHandler: AllowProjectPermission("containers", "manage-containers")},
 }
 
-var containerStateCmd = APIEndpoint{
-	Name: "containers/{name}/state",
+var instanceStateCmd = APIEndpoint{
+	Name:    "instanceState",
+	Path:    "instances/{name}/state",
+	Aliases: []APIEndpointAlias{{Name: "containerState", Path: "containers/{name}/state"}},
 
 	Get: APIEndpointAction{Handler: containerState, AccessHandler: AllowProjectPermission("containers", "view")},
 	Put: APIEndpointAction{Handler: containerStatePut, AccessHandler: AllowProjectPermission("containers", "operate-containers")},
 }
 
-var containerFileCmd = APIEndpoint{
-	Name: "containers/{name}/files",
+var instanceFileCmd = APIEndpoint{
+	Name:    "instanceFile",
+	Path:    "instances/{name}/files",
+	Aliases: []APIEndpointAlias{{Name: "containerFile", Path: "containers/{name}/files"}},
 
 	Get:    APIEndpointAction{Handler: containerFileHandler, AccessHandler: AllowProjectPermission("containers", "operate-containers")},
 	Post:   APIEndpointAction{Handler: containerFileHandler, AccessHandler: AllowProjectPermission("containers", "operate-containers")},
 	Delete: APIEndpointAction{Handler: containerFileHandler, AccessHandler: AllowProjectPermission("containers", "operate-containers")},
 }
 
-var containerSnapshotsCmd = APIEndpoint{
-	Name: "containers/{name}/snapshots",
+var instanceSnapshotsCmd = APIEndpoint{
+	Name:    "instanceSnapshots",
+	Path:    "instances/{name}/snapshots",
+	Aliases: []APIEndpointAlias{{Name: "containerSnapshots", Path: "containers/{name}/snapshots"}},
 
 	Get:  APIEndpointAction{Handler: containerSnapshotsGet, AccessHandler: AllowProjectPermission("containers", "view")},
 	Post: APIEndpointAction{Handler: containerSnapshotsPost, AccessHandler: AllowProjectPermission("containers", "operate-containers")},
 }
 
-var containerSnapshotCmd = APIEndpoint{
-	Name: "containers/{name}/snapshots/{snapshotName}",
+var instanceSnapshotCmd = APIEndpoint{
+	Name:    "instanceSnapshot",
+	Path:    "instances/{name}/snapshots/{snapshotName}",
+	Aliases: []APIEndpointAlias{{Name: "containerSnapshot", Path: "containers/{name}/snapshots/{snapshotName}"}},
 
 	Get:    APIEndpointAction{Handler: containerSnapshotHandler, AccessHandler: AllowProjectPermission("containers", "operate-containers")},
 	Post:   APIEndpointAction{Handler: containerSnapshotHandler, AccessHandler: AllowProjectPermission("containers", "operate-containers")},
@@ -64,29 +76,37 @@ var containerSnapshotCmd = APIEndpoint{
 	Put:    APIEndpointAction{Handler: containerSnapshotHandler, AccessHandler: AllowProjectPermission("containers", "operate-containers")},
 }
 
-var containerConsoleCmd = APIEndpoint{
-	Name: "containers/{name}/console",
+var instanceConsoleCmd = APIEndpoint{
+	Name:    "instanceConsole",
+	Path:    "instances/{name}/console",
+	Aliases: []APIEndpointAlias{{Name: "containerConsole", Path: "containers/{name}/console"}},
 
 	Get:    APIEndpointAction{Handler: containerConsoleLogGet, AccessHandler: AllowProjectPermission("containers", "view")},
 	Post:   APIEndpointAction{Handler: containerConsolePost, AccessHandler: AllowProjectPermission("containers", "operate-containers")},
 	Delete: APIEndpointAction{Handler: containerConsoleLogDelete, AccessHandler: AllowProjectPermission("containers", "operate-containers")},
 }
 
-var containerExecCmd = APIEndpoint{
-	Name: "containers/{name}/exec",
+var instanceExecCmd = APIEndpoint{
+	Name:    "instanceExec",
+	Path:    "instances/{name}/exec",
+	Aliases: []APIEndpointAlias{{Name: "containerExec", Path: "containers/{name}/exec"}},
 
 	Post: APIEndpointAction{Handler: containerExecPost, AccessHandler: AllowProjectPermission("containers", "operate-containers")},
 }
 
-var containerMetadataCmd = APIEndpoint{
-	Name: "containers/{name}/metadata",
+var instanceMetadataCmd = APIEndpoint{
+	Name:    "instanceMetadata",
+	Path:    "instances/{name}/metadata",
+	Aliases: []APIEndpointAlias{{Name: "containerMetadata", Path: "containers/{name}/metadata"}},
 
 	Get: APIEndpointAction{Handler: containerMetadataGet, AccessHandler: AllowProjectPermission("containers", "view")},
 	Put: APIEndpointAction{Handler: containerMetadataPut, AccessHandler: AllowProjectPermission("containers", "manage-containers")},
 }
 
-var containerMetadataTemplatesCmd = APIEndpoint{
-	Name: "containers/{name}/metadata/templates",
+var instanceMetadataTemplatesCmd = APIEndpoint{
+	Name:    "instanceMetadataTemplates",
+	Path:    "instances/{name}/metadata/templates",
+	Aliases: []APIEndpointAlias{{Name: "containerMetadataTemplates", Path: "containers/{name}/metadata/templates"}},
 
 	Get:    APIEndpointAction{Handler: containerMetadataTemplatesGet, AccessHandler: AllowProjectPermission("containers", "view")},
 	Post:   APIEndpointAction{Handler: containerMetadataTemplatesPostPut, AccessHandler: AllowProjectPermission("containers", "manage-containers")},
@@ -94,23 +114,29 @@ var containerMetadataTemplatesCmd = APIEndpoint{
 	Delete: APIEndpointAction{Handler: containerMetadataTemplatesDelete, AccessHandler: AllowProjectPermission("containers", "manage-containers")},
 }
 
-var containerBackupsCmd = APIEndpoint{
-	Name: "containers/{name}/backups",
+var instanceBackupsCmd = APIEndpoint{
+	Name:    "instanceBackups",
+	Path:    "instances/{name}/backups",
+	Aliases: []APIEndpointAlias{{Name: "containerBackups", Path: "containers/{name}/backups"}},
 
 	Get:  APIEndpointAction{Handler: containerBackupsGet, AccessHandler: AllowProjectPermission("containers", "view")},
 	Post: APIEndpointAction{Handler: containerBackupsPost, AccessHandler: AllowProjectPermission("containers", "operate-containers")},
 }
 
-var containerBackupCmd = APIEndpoint{
-	Name: "containers/{name}/backups/{backupName}",
+var instanceBackupCmd = APIEndpoint{
+	Name:    "instanceBackup",
+	Path:    "instances/{name}/backups/{backupName}",
+	Aliases: []APIEndpointAlias{{Name: "containerBackup", Path: "containers/{name}/backups/{backupName}"}},
 
 	Get:    APIEndpointAction{Handler: containerBackupGet, AccessHandler: AllowProjectPermission("containers", "view")},
 	Post:   APIEndpointAction{Handler: containerBackupPost, AccessHandler: AllowProjectPermission("containers", "operate-containers")},
 	Delete: APIEndpointAction{Handler: containerBackupDelete, AccessHandler: AllowProjectPermission("containers", "operate-containers")},
 }
 
-var containerBackupExportCmd = APIEndpoint{
-	Name: "containers/{name}/backups/{backupName}/export",
+var instanceBackupExportCmd = APIEndpoint{
+	Name:    "instanceBackupExport",
+	Path:    "instances/{name}/backups/{backupName}/export",
+	Aliases: []APIEndpointAlias{{Name: "containerBackupExport", Path: "containers/{name}/backups/{backupName}/export"}},
 
 	Get: APIEndpointAction{Handler: containerBackupExportGet, AccessHandler: AllowProjectPermission("containers", "view")},
 }
diff --git a/lxd/events.go b/lxd/events.go
index 9bed552ca6..8040d20a4a 100644
--- a/lxd/events.go
+++ b/lxd/events.go
@@ -19,7 +19,7 @@ import (
 )
 
 var eventsCmd = APIEndpoint{
-	Name: "events",
+	Path: "events",
 
 	Get: APIEndpointAction{Handler: eventsGet, AccessHandler: AllowAuthenticated},
 }
diff --git a/lxd/images.go b/lxd/images.go
index efb35eee83..d5b97ecc89 100644
--- a/lxd/images.go
+++ b/lxd/images.go
@@ -44,14 +44,14 @@ import (
 )
 
 var imagesCmd = APIEndpoint{
-	Name: "images",
+	Path: "images",
 
 	Get:  APIEndpointAction{Handler: imagesGet, AllowUntrusted: true},
 	Post: APIEndpointAction{Handler: imagesPost, AccessHandler: AllowProjectPermission("images", "manage-images")},
 }
 
 var imageCmd = APIEndpoint{
-	Name: "images/{fingerprint}",
+	Path: "images/{fingerprint}",
 
 	Delete: APIEndpointAction{Handler: imageDelete, AccessHandler: AllowProjectPermission("images", "manage-images")},
 	Get:    APIEndpointAction{Handler: imageGet, AllowUntrusted: true},
@@ -60,32 +60,32 @@ var imageCmd = APIEndpoint{
 }
 
 var imageExportCmd = APIEndpoint{
-	Name: "images/{fingerprint}/export",
+	Path: "images/{fingerprint}/export",
 
 	Get: APIEndpointAction{Handler: imageExport, AllowUntrusted: true},
 }
 
 var imageSecretCmd = APIEndpoint{
-	Name: "images/{fingerprint}/secret",
+	Path: "images/{fingerprint}/secret",
 
 	Post: APIEndpointAction{Handler: imageSecret, AccessHandler: AllowProjectPermission("images", "view")},
 }
 
 var imageRefreshCmd = APIEndpoint{
-	Name: "images/{fingerprint}/refresh",
+	Path: "images/{fingerprint}/refresh",
 
 	Post: APIEndpointAction{Handler: imageRefresh, AccessHandler: AllowProjectPermission("images", "manage-images")},
 }
 
 var imageAliasesCmd = APIEndpoint{
-	Name: "images/aliases",
+	Path: "images/aliases",
 
 	Get:  APIEndpointAction{Handler: imageAliasesGet, AccessHandler: AllowProjectPermission("images", "view")},
 	Post: APIEndpointAction{Handler: imageAliasesPost, AccessHandler: AllowProjectPermission("images", "manage-images")},
 }
 
 var imageAliasCmd = APIEndpoint{
-	Name: "images/aliases/{name:.*}",
+	Path: "images/aliases/{name:.*}",
 
 	Delete: APIEndpointAction{Handler: imageAliasDelete, AccessHandler: AllowProjectPermission("images", "manage-images")},
 	Get:    APIEndpointAction{Handler: imageAliasGet, AllowUntrusted: true},
diff --git a/lxd/networks.go b/lxd/networks.go
index 2875232cbe..156c68efe3 100644
--- a/lxd/networks.go
+++ b/lxd/networks.go
@@ -36,14 +36,14 @@ import (
 var networkCreateLock sync.Mutex
 
 var networksCmd = APIEndpoint{
-	Name: "networks",
+	Path: "networks",
 
 	Get:  APIEndpointAction{Handler: networksGet, AccessHandler: AllowAuthenticated},
 	Post: APIEndpointAction{Handler: networksPost},
 }
 
 var networkCmd = APIEndpoint{
-	Name: "networks/{name}",
+	Path: "networks/{name}",
 
 	Delete: APIEndpointAction{Handler: networkDelete},
 	Get:    APIEndpointAction{Handler: networkGet, AccessHandler: AllowAuthenticated},
@@ -53,13 +53,13 @@ var networkCmd = APIEndpoint{
 }
 
 var networkLeasesCmd = APIEndpoint{
-	Name: "networks/{name}/leases",
+	Path: "networks/{name}/leases",
 
 	Get: APIEndpointAction{Handler: networkLeasesGet, AccessHandler: AllowAuthenticated},
 }
 
 var networkStateCmd = APIEndpoint{
-	Name: "networks/{name}/state",
+	Path: "networks/{name}/state",
 
 	Get: APIEndpointAction{Handler: networkStateGet, AccessHandler: AllowAuthenticated},
 }
diff --git a/lxd/operations.go b/lxd/operations.go
index 8539dae4aa..d0908e31e3 100644
--- a/lxd/operations.go
+++ b/lxd/operations.go
@@ -24,26 +24,26 @@ import (
 )
 
 var operationCmd = APIEndpoint{
-	Name: "operations/{id}",
+	Path: "operations/{id}",
 
 	Delete: APIEndpointAction{Handler: operationDelete, AccessHandler: AllowAuthenticated},
 	Get:    APIEndpointAction{Handler: operationGet, AccessHandler: AllowAuthenticated},
 }
 
 var operationsCmd = APIEndpoint{
-	Name: "operations",
+	Path: "operations",
 
 	Get: APIEndpointAction{Handler: operationsGet, AccessHandler: AllowAuthenticated},
 }
 
 var operationWait = APIEndpoint{
-	Name: "operations/{id}/wait",
+	Path: "operations/{id}/wait",
 
 	Get: APIEndpointAction{Handler: operationWaitGet, AccessHandler: AllowAuthenticated},
 }
 
 var operationWebsocket = APIEndpoint{
-	Name: "operations/{id}/websocket",
+	Path: "operations/{id}/websocket",
 
 	Get: APIEndpointAction{Handler: operationWebsocketGet, AllowUntrusted: true},
 }
diff --git a/lxd/profiles.go b/lxd/profiles.go
index 06755c2fcd..cc2354320d 100644
--- a/lxd/profiles.go
+++ b/lxd/profiles.go
@@ -22,14 +22,14 @@ import (
 )
 
 var profilesCmd = APIEndpoint{
-	Name: "profiles",
+	Path: "profiles",
 
 	Get:  APIEndpointAction{Handler: profilesGet, AccessHandler: AllowProjectPermission("profiles", "view")},
 	Post: APIEndpointAction{Handler: profilesPost, AccessHandler: AllowProjectPermission("profiles", "manage-profiles")},
 }
 
 var profileCmd = APIEndpoint{
-	Name: "profiles/{name}",
+	Path: "profiles/{name}",
 
 	Delete: APIEndpointAction{Handler: profileDelete, AccessHandler: AllowProjectPermission("profiles", "manage-profiles")},
 	Get:    APIEndpointAction{Handler: profileGet, AccessHandler: AllowProjectPermission("profiles", "view")},
diff --git a/lxd/resources.go b/lxd/resources.go
index 58e6a5a3ec..fba15ac86f 100644
--- a/lxd/resources.go
+++ b/lxd/resources.go
@@ -9,13 +9,13 @@ import (
 )
 
 var api10ResourcesCmd = APIEndpoint{
-	Name: "resources",
+	Path: "resources",
 
 	Get: APIEndpointAction{Handler: api10ResourcesGet, AccessHandler: AllowAuthenticated},
 }
 
 var storagePoolResourcesCmd = APIEndpoint{
-	Name: "storage-pools/{name}/resources",
+	Path: "storage-pools/{name}/resources",
 
 	Get: APIEndpointAction{Handler: storagePoolResourcesGet, AccessHandler: AllowAuthenticated},
 }
diff --git a/lxd/storage_pools.go b/lxd/storage_pools.go
index 741bdfa699..443323eee8 100644
--- a/lxd/storage_pools.go
+++ b/lxd/storage_pools.go
@@ -24,14 +24,14 @@ import (
 var storagePoolCreateLock sync.Mutex
 
 var storagePoolsCmd = APIEndpoint{
-	Name: "storage-pools",
+	Path: "storage-pools",
 
 	Get:  APIEndpointAction{Handler: storagePoolsGet, AccessHandler: AllowAuthenticated},
 	Post: APIEndpointAction{Handler: storagePoolsPost},
 }
 
 var storagePoolCmd = APIEndpoint{
-	Name: "storage-pools/{name}",
+	Path: "storage-pools/{name}",
 
 	Delete: APIEndpointAction{Handler: storagePoolDelete},
 	Get:    APIEndpointAction{Handler: storagePoolGet, AccessHandler: AllowAuthenticated},
diff --git a/lxd/storage_volumes.go b/lxd/storage_volumes.go
index 815b32064f..e9baf04748 100644
--- a/lxd/storage_volumes.go
+++ b/lxd/storage_volumes.go
@@ -22,21 +22,21 @@ import (
 )
 
 var storagePoolVolumesCmd = APIEndpoint{
-	Name: "storage-pools/{name}/volumes",
+	Path: "storage-pools/{name}/volumes",
 
 	Get:  APIEndpointAction{Handler: storagePoolVolumesGet, AccessHandler: AllowAuthenticated},
 	Post: APIEndpointAction{Handler: storagePoolVolumesPost},
 }
 
 var storagePoolVolumesTypeCmd = APIEndpoint{
-	Name: "storage-pools/{name}/volumes/{type}",
+	Path: "storage-pools/{name}/volumes/{type}",
 
 	Get:  APIEndpointAction{Handler: storagePoolVolumesTypeGet, AccessHandler: AllowAuthenticated},
 	Post: APIEndpointAction{Handler: storagePoolVolumesTypePost},
 }
 
 var storagePoolVolumeTypeContainerCmd = APIEndpoint{
-	Name: "storage-pools/{pool}/volumes/container/{name:.*}",
+	Path: "storage-pools/{pool}/volumes/container/{name:.*}",
 
 	Delete: APIEndpointAction{Handler: storagePoolVolumeTypeContainerDelete},
 	Get:    APIEndpointAction{Handler: storagePoolVolumeTypeContainerGet, AccessHandler: AllowAuthenticated},
@@ -46,7 +46,7 @@ var storagePoolVolumeTypeContainerCmd = APIEndpoint{
 }
 
 var storagePoolVolumeTypeCustomCmd = APIEndpoint{
-	Name: "storage-pools/{pool}/volumes/custom/{name}",
+	Path: "storage-pools/{pool}/volumes/custom/{name}",
 
 	Delete: APIEndpointAction{Handler: storagePoolVolumeTypeCustomDelete},
 	Get:    APIEndpointAction{Handler: storagePoolVolumeTypeCustomGet, AccessHandler: AllowAuthenticated},
@@ -56,7 +56,7 @@ var storagePoolVolumeTypeCustomCmd = APIEndpoint{
 }
 
 var storagePoolVolumeTypeImageCmd = APIEndpoint{
-	Name: "storage-pools/{pool}/volumes/image/{name}",
+	Path: "storage-pools/{pool}/volumes/image/{name}",
 
 	Delete: APIEndpointAction{Handler: storagePoolVolumeTypeImageDelete},
 	Get:    APIEndpointAction{Handler: storagePoolVolumeTypeImageGet, AccessHandler: AllowAuthenticated},
diff --git a/lxd/storage_volumes_snapshot.go b/lxd/storage_volumes_snapshot.go
index 8c3c8b6de3..7a5ff87ee7 100644
--- a/lxd/storage_volumes_snapshot.go
+++ b/lxd/storage_volumes_snapshot.go
@@ -17,14 +17,14 @@ import (
 )
 
 var storagePoolVolumeSnapshotsTypeCmd = APIEndpoint{
-	Name: "storage-pools/{pool}/volumes/{type}/{name}/snapshots",
+	Path: "storage-pools/{pool}/volumes/{type}/{name}/snapshots",
 
 	Get:  APIEndpointAction{Handler: storagePoolVolumeSnapshotsTypeGet, AccessHandler: AllowAuthenticated},
 	Post: APIEndpointAction{Handler: storagePoolVolumeSnapshotsTypePost},
 }
 
 var storagePoolVolumeSnapshotTypeCmd = APIEndpoint{
-	Name: "storage-pools/{pool}/volumes/{type}/{name}/snapshots/{snapshotName}",
+	Path: "storage-pools/{pool}/volumes/{type}/{name}/snapshots/{snapshotName}",
 
 	Delete: APIEndpointAction{Handler: storagePoolVolumeSnapshotTypeDelete},
 	Get:    APIEndpointAction{Handler: storagePoolVolumeSnapshotTypeGet, AccessHandler: AllowAuthenticated},

From bb97a92d113c26d0b6e80ecc76a1ab1d66b08dd5 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Fri, 6 Sep 2019 14:29:22 +0100
Subject: [PATCH 06/12] lxd/instance/instance: Changes instance types to own
 int type

 - Adds constants for Any and Container types

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/instance/instance.go | 11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)

diff --git a/lxd/instance/instance.go b/lxd/instance/instance.go
index 1dff9a626d..10952e4821 100644
--- a/lxd/instance/instance.go
+++ b/lxd/instance/instance.go
@@ -1,4 +1,11 @@
 package instance
 
-// TypeContainer represents a container instance type.
-const TypeContainer = "container"
+// Type indicates the type of instance.
+type Type int
+
+const (
+	// TypeAny represents any type of instance.
+	TypeAny = Type(-1)
+	// TypeContainer represents a container instance type.
+	TypeContainer = Type(0)
+)

From 62c6b7e5e86a4aeec32c8aa3aab5dd03538837b2 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Fri, 6 Sep 2019 14:30:15 +0100
Subject: [PATCH 07/12] lxd/device/device/instance/id: Updates interface to use
 instance.Type

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/device/device_instance_id.go | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/lxd/device/device_instance_id.go b/lxd/device/device_instance_id.go
index f1923aa17e..c92bb6ae95 100644
--- a/lxd/device/device_instance_id.go
+++ b/lxd/device/device_instance_id.go
@@ -2,6 +2,7 @@ package device
 
 import (
 	"github.com/lxc/lxd/lxd/device/config"
+	"github.com/lxc/lxd/lxd/instance"
 )
 
 // InstanceIdentifier is an interface that allows us to identify an Instance and its properties.
@@ -9,7 +10,7 @@ import (
 // independent of when they're called in the instance lifecycle.
 type InstanceIdentifier interface {
 	Name() string
-	Type() string
+	Type() instance.Type
 	Project() string
 	DevicesPath() string
 	RootfsPath() string

From 03345e3e1c1e2fbc83a7d885c4822ccfb4ff4019 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Fri, 6 Sep 2019 14:30:53 +0100
Subject: [PATCH 08/12] lxd/db/containers: InstancesListByNodeAddress

- Renames ContainersListByNodeAddress to InstancesListByNodeAddress.
- Updates to accept an instanceType filter.

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/db/containers.go | 33 +++++++++++++++++++++++----------
 1 file changed, 23 insertions(+), 10 deletions(-)

diff --git a/lxd/db/containers.go b/lxd/db/containers.go
index 7bd85e7b88..872f20bfa9 100644
--- a/lxd/db/containers.go
+++ b/lxd/db/containers.go
@@ -10,6 +10,7 @@ import (
 	"github.com/lxc/lxd/lxd/db/query"
 	"github.com/lxc/lxd/lxd/device/config"
 	deviceConfig "github.com/lxc/lxd/lxd/device/config"
+	"github.com/lxc/lxd/lxd/instance"
 	"github.com/lxc/lxd/shared"
 	"github.com/lxc/lxd/shared/api"
 	"github.com/lxc/lxd/shared/logger"
@@ -239,29 +240,41 @@ SELECT nodes.id, nodes.address
 	return address, nil
 }
 
-// ContainersListByNodeAddress returns the names of all containers grouped by
-// cluster node address.
+// InstancesListByNodeAddress returns the names of all instances grouped by cluster node address.
 //
-// The node address of containers running on the local node is set to the empty
-// string, to distinguish it from remote nodes.
+// The node address of instances running on the local node is set to the empty string, to
+// distinguish it from remote nodes.
 //
 // Containers whose node is down are addeded to the special address "0.0.0.0".
-func (c *ClusterTx) ContainersListByNodeAddress(project string) (map[string][]string, error) {
+func (c *ClusterTx) InstancesListByNodeAddress(project string, instanceType instance.Type) (map[string][]string, error) {
 	offlineThreshold, err := c.NodeOfflineThreshold()
 	if err != nil {
 		return nil, err
 	}
 
-	stmt := `
+	var filters strings.Builder
+	args := make([]interface{}, 0, 2) // Pre-allocate for 2 filters to avoid extra allocations.
+
+	// Add project filter.
+	args = append(args, project)
+	filters.WriteString("projects.name = ?")
+
+	// Add instance type filter if supplied.
+	if instanceType != instance.TypeAny {
+		filters.WriteString(" AND instances.type = ?")
+		args = append(args, instanceType)
+	}
+
+	stmt := fmt.Sprintf(`
 SELECT instances.name, nodes.id, nodes.address, nodes.heartbeat
   FROM instances
   JOIN nodes ON nodes.id = instances.node_id
   JOIN projects ON projects.id = instances.project_id
-  WHERE instances.type=?
-    AND projects.name = ?
+  WHERE %s
   ORDER BY instances.id
-`
-	rows, err := c.tx.Query(stmt, CTypeRegular, project)
+`, filters.String())
+
+	rows, err := c.tx.Query(stmt, args...)
 	if err != nil {
 		return nil, err
 	}

From bf0672a093825985885844f349f067837c5d5757 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Fri, 6 Sep 2019 14:32:01 +0100
Subject: [PATCH 09/12] lxd/container: Updates container interface to use
 instance.Type

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/container.go | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/lxd/container.go b/lxd/container.go
index dba6aea933..666e0a8fe9 100644
--- a/lxd/container.go
+++ b/lxd/container.go
@@ -20,6 +20,7 @@ import (
 	"github.com/lxc/lxd/lxd/db"
 	"github.com/lxc/lxd/lxd/device"
 	"github.com/lxc/lxd/lxd/device/config"
+	"github.com/lxc/lxd/lxd/instance"
 	"github.com/lxc/lxd/lxd/state"
 	"github.com/lxc/lxd/lxd/sys"
 	"github.com/lxc/lxd/lxd/task"
@@ -292,7 +293,7 @@ type container interface {
 	Location() string
 	Project() string
 	Name() string
-	Type() string
+	Type() instance.Type
 	Description() string
 	Architecture() int
 	CreationDate() time.Time

From ece8ed4d83c8fd66d744d2d89240f09fcce4f604 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Fri, 6 Sep 2019 14:32:21 +0100
Subject: [PATCH 10/12] lxd/container/lxc: Updates containerLXC to use
 instance.Type

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/container_lxc.go | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lxd/container_lxc.go b/lxd/container_lxc.go
index 425b5e53da..6ab80d9dcc 100644
--- a/lxd/container_lxc.go
+++ b/lxd/container_lxc.go
@@ -630,7 +630,7 @@ type containerLXC struct {
 	expiryDate time.Time
 }
 
-func (c *containerLXC) Type() string {
+func (c *containerLXC) Type() instance.Type {
 	return instance.TypeContainer
 }
 

From 84b64f707b65cb1af7f4228f7680cab494ea7ebd Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Fri, 6 Sep 2019 14:32:46 +0100
Subject: [PATCH 11/12] lxd/containers: Updates instancesCmd to use
 instancesGet handler

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/containers.go | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lxd/containers.go b/lxd/containers.go
index 2bbb0897c1..199a1638b7 100644
--- a/lxd/containers.go
+++ b/lxd/containers.go
@@ -21,7 +21,7 @@ var instancesCmd = APIEndpoint{
 	Path:    "instances",
 	Aliases: []APIEndpointAlias{{Name: "containers", Path: "containers"}},
 
-	Get:  APIEndpointAction{Handler: containersGet, AccessHandler: AllowProjectPermission("containers", "view")},
+	Get:  APIEndpointAction{Handler: instancesGet, AccessHandler: AllowProjectPermission("containers", "view")},
 	Post: APIEndpointAction{Handler: containersPost, AccessHandler: AllowProjectPermission("containers", "manage-containers")},
 }
 

From 30a1b6cbc36ea1191f44bc35c83ad93ecf2e90b8 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parrott at canonical.com>
Date: Fri, 6 Sep 2019 14:33:26 +0100
Subject: [PATCH 12/12] lxd/containers/get: Renames containers get functions

- Adds support for instance type filters

Signed-off-by: Thomas Parrott <thomas.parrott at canonical.com>
---
 lxd/containers_get.go | 17 +++++++++++++----
 1 file changed, 13 insertions(+), 4 deletions(-)

diff --git a/lxd/containers_get.go b/lxd/containers_get.go
index e45c458aa5..64e1f859ab 100644
--- a/lxd/containers_get.go
+++ b/lxd/containers_get.go
@@ -5,12 +5,16 @@ import (
 	"net/http"
 	"sort"
 	"strconv"
+	"strings"
 	"sync"
 	"time"
 
+	"github.com/gorilla/mux"
+
 	"github.com/lxc/lxd/lxd/cluster"
 	"github.com/lxc/lxd/lxd/db"
 	"github.com/lxc/lxd/lxd/db/query"
+	"github.com/lxc/lxd/lxd/instance"
 	"github.com/lxc/lxd/shared"
 	"github.com/lxc/lxd/shared/api"
 	"github.com/lxc/lxd/shared/logger"
@@ -18,9 +22,14 @@ import (
 	"github.com/pkg/errors"
 )
 
-func containersGet(d *Daemon, r *http.Request) Response {
+func instancesGet(d *Daemon, r *http.Request) Response {
+	instanceType := instance.TypeAny
+	if strings.HasPrefix(mux.CurrentRoute(r).GetName(), "container") {
+		instanceType = instance.TypeContainer
+	}
+
 	for i := 0; i < 100; i++ {
-		result, err := doContainersGet(d, r)
+		result, err := doInstancesGet(d, r, instanceType)
 		if err == nil {
 			return SyncResponse(true, result)
 		}
@@ -38,7 +47,7 @@ func containersGet(d *Daemon, r *http.Request) Response {
 	return InternalError(fmt.Errorf("DB is locked"))
 }
 
-func doContainersGet(d *Daemon, r *http.Request) (interface{}, error) {
+func doInstancesGet(d *Daemon, r *http.Request, instanceType instance.Type) (interface{}, error) {
 	resultString := []string{}
 	resultList := []*api.Container{}
 	resultFullList := []*api.ContainerFull{}
@@ -61,7 +70,7 @@ func doContainersGet(d *Daemon, r *http.Request) (interface{}, error) {
 	err = d.cluster.Transaction(func(tx *db.ClusterTx) error {
 		var err error
 
-		result, err = tx.ContainersListByNodeAddress(project)
+		result, err = tx.InstancesListByNodeAddress(project, instanceType)
 		if err != nil {
 			return err
 		}


More information about the lxc-devel mailing list