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

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

=== Description (from pull-request) ===
Includes https://github.com/lxc/lxd/pull/6500
From 8b6d7fcd51fd8d948d1a34bebc67ed451cde593e Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Mon, 25 Nov 2019 10:49:53 +0000
Subject: [PATCH 01/12] lxd: Instance is not container type error consistency

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/container.go          | 2 +-
 lxd/container_snapshot.go | 2 +-
 lxd/migrate_container.go  | 4 ++--
 3 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/lxd/container.go b/lxd/container.go
index 54ab1691d7..daf9da1394 100644
--- a/lxd/container.go
+++ b/lxd/container.go
@@ -763,7 +763,7 @@ func instanceCreateAsCopy(s *state.State, args 
db.InstanceArgs, sourceInst insta
 
 func containerCreateAsSnapshot(s *state.State, args db.InstanceArgs, 
sourceInstance instance.Instance) (instance.Instance, error) {
        if sourceInstance.Type() != instancetype.Container {
-               return nil, fmt.Errorf("Instance not container type")
+               return nil, fmt.Errorf("Instance is not container type")
        }
 
        // Deal with state
diff --git a/lxd/container_snapshot.go b/lxd/container_snapshot.go
index f0b37f1ff8..4ad2de4b1d 100644
--- a/lxd/container_snapshot.go
+++ b/lxd/container_snapshot.go
@@ -220,7 +220,7 @@ func containerSnapshotHandler(d *Daemon, r *http.Request) 
response.Response {
        }
 
        if inst.Type() != instancetype.Container {
-               return response.SmartError(fmt.Errorf("Instance not container 
type"))
+               return response.SmartError(fmt.Errorf("Instance is not 
container type"))
        }
 
        sc := inst.(container)
diff --git a/lxd/migrate_container.go b/lxd/migrate_container.go
index 739f75cdb5..4acbe28c7c 100644
--- a/lxd/migrate_container.go
+++ b/lxd/migrate_container.go
@@ -255,7 +255,7 @@ func (s *migrationSourceWs) preDumpLoop(args 
*preDumpLoopArgs) (bool, error) {
        final := args.final
 
        if s.instance.Type() != instancetype.Container {
-               return false, fmt.Errorf("Instance not container type")
+               return false, fmt.Errorf("Instance is not container type")
        }
 
        c := s.instance.(container)
@@ -343,7 +343,7 @@ func (s *migrationSourceWs) Do(migrateOp 
*operations.Operation) error {
        }
 
        if s.instance.Type() != instancetype.Container {
-               return fmt.Errorf("Instance not container type")
+               return fmt.Errorf("Instance is not container type")
        }
 
        c := s.instance.(container)

From 77542e9ffb471cbbf12fba2195eb999050aff2d1 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Mon, 25 Nov 2019 12:19:26 +0000
Subject: [PATCH 02/12] lxd/container: Removes container type

- Will use instance.Instance, containerLXC or vmQemu instead.

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/container.go | 27 ---------------------------
 1 file changed, 27 deletions(-)

diff --git a/lxd/container.go b/lxd/container.go
index daf9da1394..38069bed62 100644
--- a/lxd/container.go
+++ b/lxd/container.go
@@ -33,7 +33,6 @@ import (
        "github.com/lxc/lxd/lxd/task"
        "github.com/lxc/lxd/shared"
        "github.com/lxc/lxd/shared/api"
-       "github.com/lxc/lxd/shared/idmap"
        "github.com/lxc/lxd/shared/ioprogress"
        log "github.com/lxc/lxd/shared/log15"
        "github.com/lxc/lxd/shared/logger"
@@ -256,32 +255,6 @@ func instanceValidDevices(state *state.State, cluster 
*db.Cluster, instanceType
        return nil
 }
 
-// The container interface
-type container interface {
-       instance.Instance
-
-       /* actionScript here is a script called action.sh in the stateDir, to
-        * be passed to CRIU as --action-script
-        */
-       Migrate(args *CriuMigrationArgs) error
-
-       ConsoleLog(opts lxc.ConsoleLogOptions) (string, error)
-
-       // Status
-       IsNesting() bool
-
-       // Hooks
-       OnStart() error
-       OnStopNS(target string, netns string) error
-       OnStop(target string) error
-
-       InsertSeccompUnixDevice(prefix string, m deviceConfig.Device, pid int) 
error
-
-       CurrentIdmap() (*idmap.IdmapSet, error)
-       DiskIdmap() (*idmap.IdmapSet, error)
-       NextIdmap() (*idmap.IdmapSet, error)
-}
-
 // instanceCreateAsEmpty creates an empty instance.
 func instanceCreateAsEmpty(d *Daemon, args db.InstanceArgs) 
(instance.Instance, error) {
        // Create the instance record.

From 7203430ffafcd551674d23ba7d58dd8101ffcb26 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Mon, 25 Nov 2019 12:20:05 +0000
Subject: [PATCH 03/12] lxd: Removes use of container type

- Where instance.Instance can satisfy dependency, uses that.
- Otherwise uses containerLXC directly.

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/api_internal.go       |  6 +++---
 lxd/container.go          |  2 +-
 lxd/container_console.go  |  4 ++--
 lxd/container_exec.go     |  2 +-
 lxd/container_lxc.go      |  6 +++---
 lxd/container_post.go     |  3 +--
 lxd/container_snapshot.go | 23 ++++++++++-------------
 lxd/devlxd.go             | 27 ++++++++++++++-------------
 lxd/migrate_container.go  |  8 +++-----
 lxd/storage.go            |  6 +++---
 lxd/storage_btrfs.go      |  3 +--
 lxd/storage_ceph.go       |  4 ++--
 lxd/storage_ceph_utils.go |  2 +-
 lxd/storage_dir.go        |  2 +-
 lxd/storage_lvm.go        |  4 ++--
 lxd/storage_lvm_utils.go  |  2 +-
 lxd/storage_migration.go  |  6 +++---
 lxd/storage_utils.go      |  3 ++-
 lxd/storage_zfs.go        |  4 ++--
 19 files changed, 56 insertions(+), 61 deletions(-)

diff --git a/lxd/api_internal.go b/lxd/api_internal.go
index 934ea7ad7c..957b0107b7 100644
--- a/lxd/api_internal.go
+++ b/lxd/api_internal.go
@@ -135,7 +135,7 @@ func internalContainerOnStart(d *Daemon, r *http.Request) 
response.Response {
                return response.SmartError(fmt.Errorf("Instance is not 
container type"))
        }
 
-       c := inst.(container)
+       c := inst.(*containerLXC)
        err = c.OnStart()
        if err != nil {
                logger.Error("The start hook failed", log.Ctx{"container": 
c.Name(), "err": err})
@@ -166,7 +166,7 @@ func internalContainerOnStopNS(d *Daemon, r *http.Request) 
response.Response {
                return response.SmartError(fmt.Errorf("Instance is not 
container type"))
        }
 
-       c := inst.(container)
+       c := inst.(*containerLXC)
        err = c.OnStopNS(target, netns)
        if err != nil {
                logger.Error("The stopns hook failed", log.Ctx{"container": 
c.Name(), "err": err})
@@ -196,7 +196,7 @@ func internalContainerOnStop(d *Daemon, r *http.Request) 
response.Response {
                return response.SmartError(fmt.Errorf("Instance is not 
container type"))
        }
 
-       c := inst.(container)
+       c := inst.(*containerLXC)
        err = c.OnStop(target)
        if err != nil {
                logger.Error("The stop hook failed", log.Ctx{"container": 
c.Name(), "err": err})
diff --git a/lxd/container.go b/lxd/container.go
index 38069bed62..d95bcbf389 100644
--- a/lxd/container.go
+++ b/lxd/container.go
@@ -776,7 +776,7 @@ func containerCreateAsSnapshot(s *state.State, args 
db.InstanceArgs, sourceInsta
                        preDumpDir:   "",
                }
 
-               c := sourceInstance.(container)
+               c := sourceInstance.(*containerLXC)
                err = c.Migrate(&criuMigrationArgs)
                if err != nil {
                        os.RemoveAll(sourceInstance.StatePath())
diff --git a/lxd/container_console.go b/lxd/container_console.go
index 3ac9fd45e0..41440db208 100644
--- a/lxd/container_console.go
+++ b/lxd/container_console.go
@@ -352,7 +352,7 @@ func containerConsoleLogGet(d *Daemon, r *http.Request) 
response.Response {
                return response.SmartError(fmt.Errorf("Instance is not 
container type"))
        }
 
-       c := inst.(container)
+       c := inst.(*containerLXC)
        ent := response.FileResponseEntry{}
        if !c.IsRunning() {
                // Hand back the contents of the console ringbuffer logfile.
@@ -406,7 +406,7 @@ func containerConsoleLogDelete(d *Daemon, r *http.Request) 
response.Response {
                return response.SmartError(fmt.Errorf("Instance is not 
container type"))
        }
 
-       c := inst.(container)
+       c := inst.(*containerLXC)
 
        truncateConsoleLogFile := func(path string) error {
                // Check that this is a regular file. We don't want to try and 
unlink
diff --git a/lxd/container_exec.go b/lxd/container_exec.go
index de7e6ded6d..dd2e95c2d6 100644
--- a/lxd/container_exec.go
+++ b/lxd/container_exec.go
@@ -434,7 +434,7 @@ func containerExecPost(d *Daemon, r *http.Request) 
response.Response {
                ws.fds = map[int]string{}
 
                if inst.Type() == instancetype.Container {
-                       c := inst.(container)
+                       c := inst.(*containerLXC)
                        idmapset, err := c.CurrentIdmap()
                        if err != nil {
                                return response.InternalError(err)
diff --git a/lxd/container_lxc.go b/lxd/container_lxc.go
index 5b03cfd138..c93b7c22bf 100644
--- a/lxd/container_lxc.go
+++ b/lxd/container_lxc.go
@@ -236,7 +236,7 @@ func lxcStatusCode(state lxc.State) api.StatusCode {
 }
 
 // Loader functions
-func containerLXCCreate(s *state.State, args db.InstanceArgs) (container, 
error) {
+func containerLXCCreate(s *state.State, args db.InstanceArgs) 
(instance.Instance, error) {
        // Create the container struct
        c := &containerLXC{
                state:        s,
@@ -442,7 +442,7 @@ func containerLXCCreate(s *state.State, args 
db.InstanceArgs) (container, error)
        return c, nil
 }
 
-func containerLXCLoad(s *state.State, args db.InstanceArgs, profiles 
[]api.Profile) (container, error) {
+func containerLXCLoad(s *state.State, args db.InstanceArgs, profiles 
[]api.Profile) (instance.Instance, error) {
        // Create the container struct
        c := containerLXCInstantiate(s, args)
 
@@ -5097,7 +5097,7 @@ func (c *containerLXC) Export(w io.Writer, properties 
map[string]string) error {
        return nil
 }
 
-func collectCRIULogFile(c container, imagesDir string, function string, method 
string) error {
+func collectCRIULogFile(c instance.Instance, imagesDir string, function 
string, method string) error {
        t := time.Now().Format(time.RFC3339)
        newPath := shared.LogPath(c.Name(), fmt.Sprintf("%s_%s_%s.log", 
function, method, t))
        return shared.FileCopy(filepath.Join(imagesDir, fmt.Sprintf("%s.log", 
method)), newPath)
diff --git a/lxd/container_post.go b/lxd/container_post.go
index 65010b9338..b8b6ba8592 100644
--- a/lxd/container_post.go
+++ b/lxd/container_post.go
@@ -211,8 +211,7 @@ func containerPost(d *Daemon, r *http.Request) 
response.Response {
                        return response.SmartError(fmt.Errorf("Instance is not 
container type"))
                }
 
-               c := inst.(container)
-               ws, err := NewMigrationSource(c, stateful, instanceOnly)
+               ws, err := NewMigrationSource(inst, stateful, instanceOnly)
                if err != nil {
                        return response.InternalError(err)
                }
diff --git a/lxd/container_snapshot.go b/lxd/container_snapshot.go
index 4ad2de4b1d..06f463b048 100644
--- a/lxd/container_snapshot.go
+++ b/lxd/container_snapshot.go
@@ -13,6 +13,7 @@ import (
        "github.com/gorilla/mux"
 
        "github.com/lxc/lxd/lxd/db"
+       "github.com/lxc/lxd/lxd/instance"
        "github.com/lxc/lxd/lxd/instance/instancetype"
        "github.com/lxc/lxd/lxd/operations"
        "github.com/lxc/lxd/lxd/response"
@@ -167,9 +168,7 @@ func containerSnapshotsPost(d *Daemon, r *http.Request) 
response.Response {
                        return fmt.Errorf("Instance is not container type")
                }
 
-               c := inst.(container)
-
-               _, err := containerCreateAsSnapshot(d.State(), args, c)
+               _, err := containerCreateAsSnapshot(d.State(), args, inst)
                if err != nil {
                        return err
                }
@@ -223,23 +222,21 @@ func containerSnapshotHandler(d *Daemon, r *http.Request) 
response.Response {
                return response.SmartError(fmt.Errorf("Instance is not 
container type"))
        }
 
-       sc := inst.(container)
-
        switch r.Method {
        case "GET":
-               return snapshotGet(sc, snapshotName)
+               return snapshotGet(inst, snapshotName)
        case "POST":
-               return snapshotPost(d, r, sc, containerName)
+               return snapshotPost(d, r, inst, containerName)
        case "DELETE":
-               return snapshotDelete(sc, snapshotName)
+               return snapshotDelete(inst, snapshotName)
        case "PUT":
-               return snapshotPut(d, r, sc, snapshotName)
+               return snapshotPut(d, r, inst, snapshotName)
        default:
                return response.NotFound(fmt.Errorf("Method '%s' not found", 
r.Method))
        }
 }
 
-func snapshotPut(d *Daemon, r *http.Request, sc container, name string) 
response.Response {
+func snapshotPut(d *Daemon, r *http.Request, sc instance.Instance, name 
string) response.Response {
        // Validate the ETag
        etag := []interface{}{sc.ExpiryDate()}
        err := util.EtagCheck(r, etag)
@@ -313,7 +310,7 @@ func snapshotPut(d *Daemon, r *http.Request, sc container, 
name string) response
        return operations.OperationResponse(op)
 }
 
-func snapshotGet(sc container, name string) response.Response {
+func snapshotGet(sc instance.Instance, name string) response.Response {
        render, _, err := sc.Render()
        if err != nil {
                return response.SmartError(err)
@@ -322,7 +319,7 @@ func snapshotGet(sc container, name string) 
response.Response {
        return response.SyncResponse(true, render.(*api.InstanceSnapshot))
 }
 
-func snapshotPost(d *Daemon, r *http.Request, sc container, containerName 
string) response.Response {
+func snapshotPost(d *Daemon, r *http.Request, sc instance.Instance, 
containerName string) response.Response {
        body, err := ioutil.ReadAll(r.Body)
        if err != nil {
                return response.InternalError(err)
@@ -432,7 +429,7 @@ func snapshotPost(d *Daemon, r *http.Request, sc container, 
containerName string
        return operations.OperationResponse(op)
 }
 
-func snapshotDelete(sc container, name string) response.Response {
+func snapshotDelete(sc instance.Instance, name string) response.Response {
        remove := func(op *operations.Operation) error {
                return sc.Delete()
        }
diff --git a/lxd/devlxd.go b/lxd/devlxd.go
index 24fb8811b3..aa113de9c9 100644
--- a/lxd/devlxd.go
+++ b/lxd/devlxd.go
@@ -18,6 +18,7 @@ import (
        "github.com/gorilla/mux"
 
        "github.com/lxc/lxd/lxd/daemon"
+       "github.com/lxc/lxd/lxd/instance"
        "github.com/lxc/lxd/lxd/instance/instancetype"
        "github.com/lxc/lxd/lxd/project"
        "github.com/lxc/lxd/lxd/state"
@@ -56,10 +57,10 @@ type devLxdHandler struct {
         * server side right now either, I went the simple route to avoid
         * needless noise.
         */
-       f func(d *Daemon, c container, w http.ResponseWriter, r *http.Request) 
*devLxdResponse
+       f func(d *Daemon, c instance.Instance, w http.ResponseWriter, r 
*http.Request) *devLxdResponse
 }
 
-var devlxdConfigGet = devLxdHandler{"/1.0/config", func(d *Daemon, c 
container, w http.ResponseWriter, r *http.Request) *devLxdResponse {
+var devlxdConfigGet = devLxdHandler{"/1.0/config", func(d *Daemon, c 
instance.Instance, w http.ResponseWriter, r *http.Request) *devLxdResponse {
        filtered := []string{}
        for k := range c.ExpandedConfig() {
                if strings.HasPrefix(k, "user.") {
@@ -69,7 +70,7 @@ var devlxdConfigGet = devLxdHandler{"/1.0/config", func(d 
*Daemon, c container,
        return okResponse(filtered, "json")
 }}
 
-var devlxdConfigKeyGet = devLxdHandler{"/1.0/config/{key}", func(d *Daemon, c 
container, w http.ResponseWriter, r *http.Request) *devLxdResponse {
+var devlxdConfigKeyGet = devLxdHandler{"/1.0/config/{key}", func(d *Daemon, c 
instance.Instance, w http.ResponseWriter, r *http.Request) *devLxdResponse {
        key := mux.Vars(r)["key"]
        if !strings.HasPrefix(key, "user.") {
                return &devLxdResponse{"not authorized", http.StatusForbidden, 
"raw"}
@@ -83,7 +84,7 @@ var devlxdConfigKeyGet = devLxdHandler{"/1.0/config/{key}", 
func(d *Daemon, c co
        return okResponse(value, "raw")
 }}
 
-var devlxdImageExport = devLxdHandler{"/1.0/images/{fingerprint}/export", 
func(d *Daemon, c container, w http.ResponseWriter, r *http.Request) 
*devLxdResponse {
+var devlxdImageExport = devLxdHandler{"/1.0/images/{fingerprint}/export", 
func(d *Daemon, c instance.Instance, w http.ResponseWriter, r *http.Request) 
*devLxdResponse {
        if !shared.IsTrue(c.ExpandedConfig()["security.devlxd.images"]) {
                return &devLxdResponse{"not authorized", http.StatusForbidden, 
"raw"}
        }
@@ -100,12 +101,12 @@ var devlxdImageExport = 
devLxdHandler{"/1.0/images/{fingerprint}/export", func(d
        return &devLxdResponse{"", http.StatusOK, "raw"}
 }}
 
-var devlxdMetadataGet = devLxdHandler{"/1.0/meta-data", func(d *Daemon, c 
container, w http.ResponseWriter, r *http.Request) *devLxdResponse {
+var devlxdMetadataGet = devLxdHandler{"/1.0/meta-data", func(d *Daemon, c 
instance.Instance, w http.ResponseWriter, r *http.Request) *devLxdResponse {
        value := c.ExpandedConfig()["user.meta-data"]
        return okResponse(fmt.Sprintf("#cloud-config\ninstance-id: 
%s\nlocal-hostname: %s\n%s", c.Name(), c.Name(), value), "raw")
 }}
 
-var devlxdEventsGet = devLxdHandler{"/1.0/events", func(d *Daemon, c 
container, w http.ResponseWriter, r *http.Request) *devLxdResponse {
+var devlxdEventsGet = devLxdHandler{"/1.0/events", func(d *Daemon, c 
instance.Instance, w http.ResponseWriter, r *http.Request) *devLxdResponse {
        typeStr := r.FormValue("type")
        if typeStr == "" {
                typeStr = "config,device"
@@ -150,7 +151,7 @@ var devlxdEventsGet = devLxdHandler{"/1.0/events", func(d 
*Daemon, c container,
        return &devLxdResponse{"websocket", http.StatusOK, "websocket"}
 }}
 
-func devlxdEventSend(c container, eventType string, eventMessage interface{}) 
error {
+func devlxdEventSend(c instance.Instance, eventType string, eventMessage 
interface{}) error {
        event := shared.Jmap{}
        event["type"] = eventType
        event["timestamp"] = time.Now()
@@ -160,10 +161,10 @@ func devlxdEventSend(c container, eventType string, 
eventMessage interface{}) er
 }
 
 var handlers = []devLxdHandler{
-       {"/", func(d *Daemon, c container, w http.ResponseWriter, r 
*http.Request) *devLxdResponse {
+       {"/", func(d *Daemon, c instance.Instance, w http.ResponseWriter, r 
*http.Request) *devLxdResponse {
                return okResponse([]string{"/1.0"}, "json")
        }},
-       {"/1.0", func(d *Daemon, c container, w http.ResponseWriter, r 
*http.Request) *devLxdResponse {
+       {"/1.0", func(d *Daemon, c instance.Instance, w http.ResponseWriter, r 
*http.Request) *devLxdResponse {
                return okResponse(shared.Jmap{"api_version": 
version.APIVersion}, "json")
        }},
        devlxdConfigGet,
@@ -173,7 +174,7 @@ var handlers = []devLxdHandler{
        devlxdImageExport,
 }
 
-func hoistReq(f func(*Daemon, container, http.ResponseWriter, *http.Request) 
*devLxdResponse, d *Daemon) func(http.ResponseWriter, *http.Request) {
+func hoistReq(f func(*Daemon, instance.Instance, http.ResponseWriter, 
*http.Request) *devLxdResponse, d *Daemon) func(http.ResponseWriter, 
*http.Request) {
        return func(w http.ResponseWriter, r *http.Request) {
                conn := extractUnderlyingConn(w)
                cred, ok := pidMapper.m[conn]
@@ -317,7 +318,7 @@ func extractUnderlyingConn(w http.ResponseWriter) 
*net.UnixConn {
 
 var pidNotInContainerErr = fmt.Errorf("pid not in container?")
 
-func findContainerForPid(pid int32, s *state.State) (container, error) {
+func findContainerForPid(pid int32, s *state.State) (*containerLXC, error) {
        /*
         * Try and figure out which container a pid is in. There is probably a
         * better way to do this. Based on rharper's initial performance
@@ -364,7 +365,7 @@ func findContainerForPid(pid int32, s *state.State) 
(container, error) {
                                return nil, fmt.Errorf("Instance is not 
container type")
                        }
 
-                       c := inst.(container)
+                       c := inst.(*containerLXC)
                        return c, nil
                }
 
@@ -414,7 +415,7 @@ func findContainerForPid(pid int32, s *state.State) 
(container, error) {
                }
 
                if origPidNs == pidNs {
-                       return inst.(container), nil
+                       return inst.(*containerLXC), nil
                }
        }
 
diff --git a/lxd/migrate_container.go b/lxd/migrate_container.go
index 4acbe28c7c..280a94311f 100644
--- a/lxd/migrate_container.go
+++ b/lxd/migrate_container.go
@@ -142,8 +142,7 @@ func (s *migrationSourceWs) checkForPreDumpSupport() (bool, 
int) {
                return false, 0
        }
 
-       c := s.instance.(container)
-
+       c := s.instance.(*containerLXC)
        err := c.Migrate(&criuMigrationArgs)
 
        if err != nil {
@@ -258,8 +257,7 @@ func (s *migrationSourceWs) preDumpLoop(args 
*preDumpLoopArgs) (bool, error) {
                return false, fmt.Errorf("Instance is not container type")
        }
 
-       c := s.instance.(container)
-
+       c := s.instance.(*containerLXC)
        err := c.Migrate(&criuMigrationArgs)
        if err != nil {
                return final, err
@@ -346,7 +344,7 @@ func (s *migrationSourceWs) Do(migrateOp 
*operations.Operation) error {
                return fmt.Errorf("Instance is not container type")
        }
 
-       c := s.instance.(container)
+       c := s.instance.(*containerLXC)
 
        // Storage needs to start unconditionally now, since we need to
        // initialize a new storage interface.
diff --git a/lxd/storage.go b/lxd/storage.go
index 4fca279fca..64172414df 100644
--- a/lxd/storage.go
+++ b/lxd/storage.go
@@ -436,7 +436,7 @@ func storagePoolInit(s *state.State, poolName string) 
(storage, error) {
        return storageInit(s, "default", poolName, "", -1)
 }
 
-func storagePoolVolumeAttachInit(s *state.State, poolName string, volumeName 
string, volumeType int, c container) (storage, error) {
+func storagePoolVolumeAttachInit(s *state.State, poolName string, volumeName 
string, volumeType int, c *containerLXC) (storage, error) {
        st, err := storageInit(s, "default", poolName, volumeName, volumeType)
        if err != nil {
                return nil, err
@@ -505,7 +505,7 @@ func storagePoolVolumeAttachInit(s *state.State, poolName 
string, volumeName str
                                                continue
                                        }
 
-                                       ct := instt.(container)
+                                       ct := instt.(*containerLXC)
 
                                        var ctNextIdmap *idmap.IdmapSet
                                        if ct.IsRunning() {
@@ -712,7 +712,7 @@ func deleteSnapshotMountpoint(snapshotMountpoint string, 
snapshotsSymlinkTarget
        return nil
 }
 
-func resetContainerDiskIdmap(container container, srcIdmap *idmap.IdmapSet) 
error {
+func resetContainerDiskIdmap(container *containerLXC, srcIdmap 
*idmap.IdmapSet) error {
        dstIdmap, err := container.DiskIdmap()
        if err != nil {
                return err
diff --git a/lxd/storage_btrfs.go b/lxd/storage_btrfs.go
index 99fa8d264d..d800f931e3 100644
--- a/lxd/storage_btrfs.go
+++ b/lxd/storage_btrfs.go
@@ -2701,11 +2701,10 @@ func (s *storageBtrfs) btrfsLookupFsUUID(fs string) 
(string, error) {
 func (s *storageBtrfs) StorageEntitySetQuota(volumeType int, size int64, data 
interface{}) error {
        logger.Debugf(`Setting BTRFS quota for "%s"`, s.volume.Name)
 
-       var c container
+       var c instance.Instance
        var subvol string
        switch volumeType {
        case storagePoolVolumeTypeContainer:
-               c = data.(container)
                subvol = driver.GetContainerMountPoint(c.Project(), 
s.pool.Name, c.Name())
        case storagePoolVolumeTypeCustom:
                subvol = driver.GetStoragePoolVolumeMountPoint(s.pool.Name, 
s.volume.Name)
diff --git a/lxd/storage_ceph.go b/lxd/storage_ceph.go
index 2b19a9134a..30be16af00 100644
--- a/lxd/storage_ceph.go
+++ b/lxd/storage_ceph.go
@@ -2379,14 +2379,14 @@ func (s *storageCeph) StorageEntitySetQuota(volumeType 
int, size int64, data int
        }
 
        var ret int
-       var c container
+       var c instance.Instance
        fsType := s.getRBDFilesystem()
        mountpoint := ""
        RBDDevPath := ""
        volumeName := ""
        switch volumeType {
        case storagePoolVolumeTypeContainer:
-               c = data.(container)
+               c = data.(instance.Instance)
                ctName := c.Name()
                if c.IsRunning() {
                        msg := fmt.Sprintf(`Cannot resize RBD storage volume `+
diff --git a/lxd/storage_ceph_utils.go b/lxd/storage_ceph_utils.go
index 41cf3e8e97..f5d5b75f74 100644
--- a/lxd/storage_ceph_utils.go
+++ b/lxd/storage_ceph_utils.go
@@ -1553,7 +1553,7 @@ func (s *storageCeph) rbdGrow(path string, size int64, 
fsType string,
        // Mount the filesystem
        switch volumeType {
        case storagePoolVolumeTypeContainer:
-               c := data.(container)
+               c := data.(instance.Instance)
                ourMount, err := c.StorageStart()
                if err != nil {
                        return err
diff --git a/lxd/storage_dir.go b/lxd/storage_dir.go
index a72fc65b66..e9db7b8c23 100644
--- a/lxd/storage_dir.go
+++ b/lxd/storage_dir.go
@@ -1309,7 +1309,7 @@ func (s *storageDir) StorageEntitySetQuota(volumeType 
int, size int64, data inte
        var path string
        switch volumeType {
        case storagePoolVolumeTypeContainer:
-               c := data.(container)
+               c := data.(instance.Instance)
                path = driver.GetContainerMountPoint(c.Project(), s.pool.Name, 
c.Name())
        case storagePoolVolumeTypeCustom:
                path = driver.GetStoragePoolVolumeMountPoint(s.pool.Name, 
s.volume.Name)
diff --git a/lxd/storage_lvm.go b/lxd/storage_lvm.go
index 8fef0f4735..e13a9652f6 100644
--- a/lxd/storage_lvm.go
+++ b/lxd/storage_lvm.go
@@ -2074,13 +2074,13 @@ func (s *storageLvm) StorageEntitySetQuota(volumeType 
int, size int64, data inte
        }
 
        poolName := s.getOnDiskPoolName()
-       var c container
+       var c instance.Instance
        fsType := s.getLvmFilesystem()
        lvDevPath := ""
        mountpoint := ""
        switch volumeType {
        case storagePoolVolumeTypeContainer:
-               c = data.(container)
+               c = data.(instance.Instance)
                ctName := c.Name()
                if c.IsRunning() {
                        msg := fmt.Sprintf(`Cannot resize LVM storage volume `+
diff --git a/lxd/storage_lvm_utils.go b/lxd/storage_lvm_utils.go
index c6cdaf0793..cec7c223f9 100644
--- a/lxd/storage_lvm_utils.go
+++ b/lxd/storage_lvm_utils.go
@@ -41,7 +41,7 @@ func (s *storageLvm) lvExtend(lvPath string, lvSize int64, 
fsType string, fsMntP
 
        switch volumeType {
        case storagePoolVolumeTypeContainer:
-               c := data.(container)
+               c := data.(instance.Instance)
                ourMount, err := c.StorageStart()
                if err != nil {
                        return err
diff --git a/lxd/storage_migration.go b/lxd/storage_migration.go
index c1bcdd2c37..19626c1375 100644
--- a/lxd/storage_migration.go
+++ b/lxd/storage_migration.go
@@ -369,7 +369,7 @@ func rsyncMigrationSink(conn *websocket.Conn, op 
*operations.Operation, args Mig
                                }
 
                                if args.Instance.Type() == 
instancetype.Container {
-                                       c := args.Instance.(container)
+                                       c := args.Instance.(*containerLXC)
                                        err = resetContainerDiskIdmap(c, 
args.Idmap)
                                        if err != nil {
                                                return err
@@ -423,7 +423,7 @@ func rsyncMigrationSink(conn *websocket.Conn, op 
*operations.Operation, args Mig
                                }
 
                                if args.Instance.Type() == 
instancetype.Container {
-                                       c := args.Instance.(container)
+                                       c := args.Instance.(*containerLXC)
                                        err = resetContainerDiskIdmap(c, 
args.Idmap)
                                        if err != nil {
                                                return err
@@ -458,7 +458,7 @@ func rsyncMigrationSink(conn *websocket.Conn, op 
*operations.Operation, args Mig
        }
 
        if args.Instance.Type() == instancetype.Container {
-               c := args.Instance.(container)
+               c := args.Instance.(*containerLXC)
                err = resetContainerDiskIdmap(c, args.Idmap)
                if err != nil {
                        return err
diff --git a/lxd/storage_utils.go b/lxd/storage_utils.go
index fd4ecf11bd..468a1982ff 100644
--- a/lxd/storage_utils.go
+++ b/lxd/storage_utils.go
@@ -3,6 +3,7 @@ package main
 import (
        "fmt"
 
+       "github.com/lxc/lxd/lxd/instance"
        driver "github.com/lxc/lxd/lxd/storage"
        "github.com/lxc/lxd/shared/logger"
 )
@@ -20,7 +21,7 @@ func shrinkVolumeFilesystem(s storage, volumeType int, fsType 
string, devPath st
        case "ext4":
                switch volumeType {
                case storagePoolVolumeTypeContainer:
-                       c := data.(container)
+                       c := data.(instance.Instance)
                        ourMount, err := c.StorageStop()
                        if err != nil {
                                return nil, err
diff --git a/lxd/storage_zfs.go b/lxd/storage_zfs.go
index f154dcb1fe..1692a3be23 100644
--- a/lxd/storage_zfs.go
+++ b/lxd/storage_zfs.go
@@ -2743,11 +2743,11 @@ func (s *storageZfs) StorageEntitySetQuota(volumeType 
int, size int64, data inte
                return fmt.Errorf("Invalid storage type")
        }
 
-       var c container
+       var c instance.Instance
        var fs string
        switch volumeType {
        case storagePoolVolumeTypeContainer:
-               c = data.(container)
+               c = data.(instance.Instance)
                fs = fmt.Sprintf("containers/%s", project.Prefix(c.Project(), 
c.Name()))
        case storagePoolVolumeTypeCustom:
                fs = fmt.Sprintf("custom/%s", s.volume.Name)

From 9e331019655cf63a41391b9815651f2b8920afe4 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Mon, 25 Nov 2019 10:44:37 +0000
Subject: [PATCH 04/12] lxd: Renames containerCreateAsSnapshot to
 instanceCreateAsSnapshot

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/container.go          | 4 ++--
 lxd/container_snapshot.go | 2 +-
 lxd/storage_migration.go  | 2 +-
 3 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/lxd/container.go b/lxd/container.go
index d95bcbf389..96b5df7006 100644
--- a/lxd/container.go
+++ b/lxd/container.go
@@ -734,7 +734,7 @@ func instanceCreateAsCopy(s *state.State, args 
db.InstanceArgs, sourceInst insta
        return inst, nil
 }
 
-func containerCreateAsSnapshot(s *state.State, args db.InstanceArgs, 
sourceInstance instance.Instance) (instance.Instance, error) {
+func instanceCreateAsSnapshot(s *state.State, args db.InstanceArgs, 
sourceInstance instance.Instance) (instance.Instance, error) {
        if sourceInstance.Type() != instancetype.Container {
                return nil, fmt.Errorf("Instance is not container type")
        }
@@ -1470,7 +1470,7 @@ func autoCreateContainerSnapshots(ctx context.Context, d 
*Daemon, instances []in
                                ExpiryDate:   expiry,
                        }
 
-                       _, err = containerCreateAsSnapshot(d.State(), args, c)
+                       _, err = instanceCreateAsSnapshot(d.State(), args, c)
                        if err != nil {
                                logger.Error("Error creating snapshots", 
log.Ctx{"err": err, "container": c})
                        }
diff --git a/lxd/container_snapshot.go b/lxd/container_snapshot.go
index 06f463b048..016cc28f08 100644
--- a/lxd/container_snapshot.go
+++ b/lxd/container_snapshot.go
@@ -168,7 +168,7 @@ func containerSnapshotsPost(d *Daemon, r *http.Request) 
response.Response {
                        return fmt.Errorf("Instance is not container type")
                }
 
-               _, err := containerCreateAsSnapshot(d.State(), args, inst)
+               _, err := instanceCreateAsSnapshot(d.State(), args, inst)
                if err != nil {
                        return err
                }
diff --git a/lxd/storage_migration.go b/lxd/storage_migration.go
index 19626c1375..8391030b63 100644
--- a/lxd/storage_migration.go
+++ b/lxd/storage_migration.go
@@ -433,7 +433,7 @@ func rsyncMigrationSink(conn *websocket.Conn, op 
*operations.Operation, args Mig
                                _, err = 
instanceLoadByProjectAndName(args.Instance.DaemonState(),
                                        args.Instance.Project(), snapArgs.Name)
                                if err != nil {
-                                       _, err = 
containerCreateAsSnapshot(args.Instance.DaemonState(), snapArgs, args.Instance)
+                                       _, err = 
instanceCreateAsSnapshot(args.Instance.DaemonState(), snapArgs, args.Instance)
                                        if err != nil {
                                                return err
                                        }

From bb0eb16332384056a283827dfca1dd75fb347474 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Mon, 25 Nov 2019 10:46:31 +0000
Subject: [PATCH 05/12] lxd/container/snaphot: Returns instances property in
 response

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

diff --git a/lxd/container_snapshot.go b/lxd/container_snapshot.go
index 016cc28f08..c3c832857d 100644
--- a/lxd/container_snapshot.go
+++ b/lxd/container_snapshot.go
@@ -177,7 +177,8 @@ func containerSnapshotsPost(d *Daemon, r *http.Request) 
response.Response {
        }
 
        resources := map[string][]string{}
-       resources["containers"] = []string{name}
+       resources["instances"] = []string{name}
+       resources["containers"] = resources["instances"]
 
        op, err := operations.OperationCreate(d.State(), project, 
operations.OperationClassTask, db.OperationSnapshotCreate, resources, nil, 
snapshot, nil, nil)
        if err != nil {

From d444c4f4944eab792cdfc68425efacc5d6d76fc7 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Mon, 25 Nov 2019 10:48:48 +0000
Subject: [PATCH 06/12] lxd/container/snapshot: Removes duplicated instance
 type check

This is also done inside instanceCreateAsSnapshot

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/container_snapshot.go | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/lxd/container_snapshot.go b/lxd/container_snapshot.go
index c3c832857d..8004c1810d 100644
--- a/lxd/container_snapshot.go
+++ b/lxd/container_snapshot.go
@@ -164,10 +164,6 @@ func containerSnapshotsPost(d *Daemon, r *http.Request) 
response.Response {
                        ExpiryDate:   expiry,
                }
 
-               if inst.Type() != instancetype.Container {
-                       return fmt.Errorf("Instance is not container type")
-               }
-
                _, err := instanceCreateAsSnapshot(d.State(), args, inst)
                if err != nil {
                        return err

From 8754e2925a3fab8bf7db6937c22fe43c2673aed1 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Mon, 25 Nov 2019 13:34:19 +0000
Subject: [PATCH 07/12] lxd/storage: Changes CreateInstanceSnapshot signature
 to accept source instance

- This aligns with the current storage layer's ContainerSnapshotCreate() 
function that accepts an instance for the snapshot and a source instance.
- This allows the API layer to create the new snapshot's instance DB record, 
initialise an instance and pass it in.

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/storage/backend_lxd.go  | 2 +-
 lxd/storage/backend_mock.go | 2 +-
 lxd/storage/interfaces.go   | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/lxd/storage/backend_lxd.go b/lxd/storage/backend_lxd.go
index 99d47a7d60..ce4b10d20e 100644
--- a/lxd/storage/backend_lxd.go
+++ b/lxd/storage/backend_lxd.go
@@ -1045,7 +1045,7 @@ func (b *lxdBackend) GetInstanceDisk(inst Instance) 
(string, error) {
        return diskPath, nil
 }
 
-func (b *lxdBackend) CreateInstanceSnapshot(inst Instance, name string, op 
*operations.Operation) error {
+func (b *lxdBackend) CreateInstanceSnapshot(inst Instance, src Instance, op 
*operations.Operation) error {
        return ErrNotImplemented
 }
 
diff --git a/lxd/storage/backend_mock.go b/lxd/storage/backend_mock.go
index 0bc13b66b7..c8e14c1aa1 100644
--- a/lxd/storage/backend_mock.go
+++ b/lxd/storage/backend_mock.go
@@ -119,7 +119,7 @@ func (b *mockBackend) GetInstanceDisk(i Instance) (string, 
error) {
        return "", nil
 }
 
-func (b *mockBackend) CreateInstanceSnapshot(i Instance, name string, op 
*operations.Operation) error {
+func (b *mockBackend) CreateInstanceSnapshot(i instance.Instance, src 
instance.Instance, op *operations.Operation) error {
        return nil
 }
 
diff --git a/lxd/storage/interfaces.go b/lxd/storage/interfaces.go
index 27ed6bf7de..11695abe4b 100644
--- a/lxd/storage/interfaces.go
+++ b/lxd/storage/interfaces.go
@@ -62,7 +62,7 @@ type Pool interface {
        GetInstanceDisk(inst Instance) (string, error)
 
        // Instance snapshots.
-       CreateInstanceSnapshot(inst Instance, name string, op 
*operations.Operation) error
+       CreateInstanceSnapshot(inst instance.Instance, src instance.Instance, 
op *operations.Operation) error
        RenameInstanceSnapshot(inst Instance, newName string, op 
*operations.Operation) error
        DeleteInstanceSnapshot(inst Instance, op *operations.Operation) error
        RestoreInstanceSnapshot(inst Instance, op *operations.Operation) error

From bddbcf4d4e82696aa5827077f3afb5afbb6597f9 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Mon, 25 Nov 2019 14:05:38 +0000
Subject: [PATCH 08/12] lxd: Hooks instanceCreateAsSnapshot up to new storage
 pkg

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/container.go          | 77 +++++++++++++++++++++++++++------------
 lxd/container_snapshot.go |  2 +-
 lxd/storage_migration.go  |  2 +-
 3 files changed, 56 insertions(+), 25 deletions(-)

diff --git a/lxd/container.go b/lxd/container.go
index 96b5df7006..1406e91ebf 100644
--- a/lxd/container.go
+++ b/lxd/container.go
@@ -734,11 +734,15 @@ func instanceCreateAsCopy(s *state.State, args 
db.InstanceArgs, sourceInst insta
        return inst, nil
 }
 
-func instanceCreateAsSnapshot(s *state.State, args db.InstanceArgs, 
sourceInstance instance.Instance) (instance.Instance, error) {
+func instanceCreateAsSnapshot(s *state.State, args db.InstanceArgs, 
sourceInstance instance.Instance, op *operations.Operation) (instance.Instance, 
error) {
        if sourceInstance.Type() != instancetype.Container {
                return nil, fmt.Errorf("Instance is not container type")
        }
 
+       if sourceInstance.Type() != args.Type {
+               return nil, fmt.Errorf("Source instance and snapshot instance 
types do not match")
+       }
+
        // Deal with state
        if args.Stateful {
                if !sourceInstance.IsRunning() {
@@ -785,37 +789,63 @@ func instanceCreateAsSnapshot(s *state.State, args 
db.InstanceArgs, sourceInstan
        }
 
        // Create the snapshot
-       c, err := instanceCreateInternal(s, args)
+       inst, err := instanceCreateInternal(s, args)
        if err != nil {
                return nil, err
        }
 
-       // Clone the container
-       if sourceInstance.Type() != instancetype.Container {
-               return nil, fmt.Errorf("Instance type must be container")
-       }
+       revert := true
+       defer func() {
+               if !revert {
+                       return
+               }
 
-       ct := sourceInstance.(*containerLXC)
+               inst.Delete()
+       }()
 
-       err = ct.Storage().ContainerSnapshotCreate(c, sourceInstance)
-       if err != nil {
-               c.Delete()
-               return nil, err
-       }
+       // Check if we can load new storage layer for pool driver type.
+       pool, err := storagePools.GetPoolByInstance(s, inst)
+       if err != storageDrivers.ErrUnknownDriver && err != 
storageDrivers.ErrNotImplemented {
+               if err != nil {
+                       return nil, err
+               }
 
-       // Attempt to update backup.yaml on container
-       ourStart, err := sourceInstance.StorageStart()
-       if err != nil {
-               c.Delete()
-               return nil, err
-       }
-       if ourStart {
-               defer sourceInstance.StorageStop()
+               err = pool.CreateInstanceSnapshot(inst, sourceInstance, op)
+               if err != nil {
+                       return nil, errors.Wrap(err, "Create instance snapshot")
+               }
+
+               // Mount volume for backup.yaml writing.
+               ourStart, err := pool.MountInstance(sourceInstance, op)
+               if err != nil {
+                       return nil, errors.Wrap(err, "Create instance snapshot 
(mount source)")
+               }
+               if ourStart {
+                       defer pool.UnmountInstance(sourceInstance, op)
+               }
+       } else if inst.Type() == instancetype.Container {
+               ct := sourceInstance.(*containerLXC)
+               err = ct.Storage().ContainerSnapshotCreate(inst, sourceInstance)
+               if err != nil {
+                       return nil, err
+               }
+
+               // Mount volume for backup.yaml writing.
+               ourStart, err := sourceInstance.StorageStart()
+               if err != nil {
+                       return nil, err
+               }
+               if ourStart {
+                       defer sourceInstance.StorageStop()
+               }
+
+       } else {
+               return nil, fmt.Errorf("Instance type not supported")
        }
 
+       // Attempt to update backup.yaml for instance
        err = writeBackupFile(sourceInstance)
        if err != nil {
-               c.Delete()
                return nil, err
        }
 
@@ -830,7 +860,8 @@ func instanceCreateAsSnapshot(s *state.State, args 
db.InstanceArgs, sourceInstan
                        "snapshot_name": args.Name,
                })
 
-       return c, nil
+       revert = false
+       return inst, nil
 }
 
 // instanceCreateInternal creates an instance record and storage volume record 
in the database.
@@ -1470,7 +1501,7 @@ func autoCreateContainerSnapshots(ctx context.Context, d 
*Daemon, instances []in
                                ExpiryDate:   expiry,
                        }
 
-                       _, err = instanceCreateAsSnapshot(d.State(), args, c)
+                       _, err = instanceCreateAsSnapshot(d.State(), args, c, 
nil)
                        if err != nil {
                                logger.Error("Error creating snapshots", 
log.Ctx{"err": err, "container": c})
                        }
diff --git a/lxd/container_snapshot.go b/lxd/container_snapshot.go
index 8004c1810d..338750d2d9 100644
--- a/lxd/container_snapshot.go
+++ b/lxd/container_snapshot.go
@@ -164,7 +164,7 @@ func containerSnapshotsPost(d *Daemon, r *http.Request) 
response.Response {
                        ExpiryDate:   expiry,
                }
 
-               _, err := instanceCreateAsSnapshot(d.State(), args, inst)
+               _, err := instanceCreateAsSnapshot(d.State(), args, inst, op)
                if err != nil {
                        return err
                }
diff --git a/lxd/storage_migration.go b/lxd/storage_migration.go
index 8391030b63..da6e467d7f 100644
--- a/lxd/storage_migration.go
+++ b/lxd/storage_migration.go
@@ -433,7 +433,7 @@ func rsyncMigrationSink(conn *websocket.Conn, op 
*operations.Operation, args Mig
                                _, err = 
instanceLoadByProjectAndName(args.Instance.DaemonState(),
                                        args.Instance.Project(), snapArgs.Name)
                                if err != nil {
-                                       _, err = 
instanceCreateAsSnapshot(args.Instance.DaemonState(), snapArgs, args.Instance)
+                                       _, err = 
instanceCreateAsSnapshot(args.Instance.DaemonState(), snapArgs, args.Instance, 
op)
                                        if err != nil {
                                                return err
                                        }

From d704ae2b7ce7301fb5b85bbcf1ae023895d82db9 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Mon, 25 Nov 2019 15:17:50 +0000
Subject: [PATCH 09/12] lxd/storage/drivers/load: Adds RunningSnapshotFreeze to
 driver Info struct

- Also adds comments to other Info fields.

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/storage/drivers/load.go | 17 +++++++++--------
 1 file changed, 9 insertions(+), 8 deletions(-)

diff --git a/lxd/storage/drivers/load.go b/lxd/storage/drivers/load.go
index bced6925e7..3f851a90bc 100644
--- a/lxd/storage/drivers/load.go
+++ b/lxd/storage/drivers/load.go
@@ -29,14 +29,15 @@ func Load(state *state.State, driverName string, name 
string, config map[string]
 
 // Info represents information about a storage driver.
 type Info struct {
-       Name               string
-       Version            string
-       Remote             bool
-       OptimizedImages    bool
-       PreservesInodes    bool
-       VolumeTypes        []VolumeType
-       BlockBacking       bool
-       RunningQuotaResize bool
+       Name                  string
+       Version               string
+       VolumeTypes           []VolumeType // Supported volume types.
+       Remote                bool         // Whether the driver uses a remote 
backing store.
+       OptimizedImages       bool         // Whether driver stores images as 
separate volume.
+       PreservesInodes       bool         // Whether driver preserves inodes 
when volumes are moved hosts.
+       BlockBacking          bool         // Whether driver uses block devices 
as backing store.
+       RunningQuotaResize    bool         // Whether quota resize is supported 
whilst instance running.
+       RunningSnapshotFreeze bool         // Whether instance should be frozen 
during snapshot if running.
 }
 
 // SupportedDrivers returns a list of supported storage drivers.

From ad899035e80186ff17d5d82abc6a3d2840a27ca8 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Mon, 25 Nov 2019 15:28:51 +0000
Subject: [PATCH 10/12] lxd/storage/drivers/driver/dir: Defines dir driver
 needs freeze during snapshot

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/storage/drivers/driver_dir.go | 17 +++++++++--------
 1 file changed, 9 insertions(+), 8 deletions(-)

diff --git a/lxd/storage/drivers/driver_dir.go 
b/lxd/storage/drivers/driver_dir.go
index 9072a675a7..cc6da99bc8 100644
--- a/lxd/storage/drivers/driver_dir.go
+++ b/lxd/storage/drivers/driver_dir.go
@@ -28,14 +28,15 @@ type dir struct {
 // Info returns info about the driver and its environment.
 func (d *dir) Info() Info {
        return Info{
-               Name:               "dir",
-               Version:            "1",
-               OptimizedImages:    false,
-               PreservesInodes:    false,
-               Remote:             false,
-               VolumeTypes:        []VolumeType{VolumeTypeCustom, 
VolumeTypeImage, VolumeTypeContainer, VolumeTypeVM},
-               BlockBacking:       false,
-               RunningQuotaResize: true,
+               Name:                  "dir",
+               Version:               "1",
+               OptimizedImages:       false,
+               PreservesInodes:       false,
+               Remote:                false,
+               VolumeTypes:           []VolumeType{VolumeTypeCustom, 
VolumeTypeImage, VolumeTypeContainer, VolumeTypeVM},
+               BlockBacking:          false,
+               RunningQuotaResize:    true,
+               RunningSnapshotFreeze: true,
        }
 }
 

From 616cd570a599956dd6de16ae7712c01225795b20 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Mon, 25 Nov 2019 17:10:31 +0000
Subject: [PATCH 11/12] lxd/storage/backend/lxd: Adds snapshot check to
 ensureInstanceSymlink

To avoid unexpected usage scenarios.

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/storage/backend_lxd.go | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/lxd/storage/backend_lxd.go b/lxd/storage/backend_lxd.go
index ce4b10d20e..479fc9b74c 100644
--- a/lxd/storage/backend_lxd.go
+++ b/lxd/storage/backend_lxd.go
@@ -179,6 +179,10 @@ func (b *lxdBackend) Unmount() (bool, error) {
 // ensureInstanceSymlink creates a symlink in the instance directory to the 
instance's mount path
 // if doesn't exist already.
 func (b *lxdBackend) ensureInstanceSymlink(instanceType instancetype.Type, 
projectName, instanceName, mountPath string) error {
+       if shared.IsSnapshot(instanceName) {
+               return fmt.Errorf("Instance must not be snapshot")
+       }
+
        symlinkPath := InstancePath(instanceType, projectName, instanceName, 
false)
 
        // Remove any old symlinks left over by previous bugs that may point to 
a different pool.

From db57389da8be0ff50f3cf81664ba1b1059fb800b Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Mon, 25 Nov 2019 14:51:16 +0000
Subject: [PATCH 12/12] lxd/storage/backend/lxd: Implements
 CreateInstanceSnapshot

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/storage/backend_lxd.go | 54 ++++++++++++++++++++++++++++++++++++--
 1 file changed, 52 insertions(+), 2 deletions(-)

diff --git a/lxd/storage/backend_lxd.go b/lxd/storage/backend_lxd.go
index 479fc9b74c..76f85c0e90 100644
--- a/lxd/storage/backend_lxd.go
+++ b/lxd/storage/backend_lxd.go
@@ -1049,8 +1049,58 @@ func (b *lxdBackend) GetInstanceDisk(inst Instance) 
(string, error) {
        return diskPath, nil
 }
 
-func (b *lxdBackend) CreateInstanceSnapshot(inst Instance, src Instance, op 
*operations.Operation) error {
-       return ErrNotImplemented
+// CreateInstanceSnapshot creates a snaphot of an instance volume.
+func (b *lxdBackend) CreateInstanceSnapshot(inst instance.Instance, src 
instance.Instance, op *operations.Operation) error {
+       logger := logging.AddContext(b.logger, log.Ctx{"project": 
inst.Project(), "instance": inst.Name(), "src": src.Name()})
+       logger.Debug("CreateInstanceSnapshot started")
+       defer logger.Debug("CreateInstanceSnapshot finished")
+
+       if inst.Type() != src.Type() {
+               return fmt.Errorf("Instance types must match")
+       }
+
+       if !inst.IsSnapshot() {
+               return fmt.Errorf("Instance must be snapshot")
+       }
+
+       if src.IsSnapshot() {
+               return fmt.Errorf("Source instance cannot be snapshot")
+       }
+
+       // Check we can convert the instance to the volume type needed.
+       volType, err := InstanceTypeToVolumeType(inst.Type())
+       if err != nil {
+               return err
+       }
+
+       // Some driver backing stores require that running instances be frozen 
during snapshot.
+       if b.driver.Info().RunningSnapshotFreeze && src.IsRunning() {
+               err = src.Freeze()
+               if err != nil {
+                       return err
+               }
+               defer src.Unfreeze()
+       }
+
+       parentName, snapName, isSnap := 
shared.InstanceGetParentAndSnapshotName(inst.Name())
+       if !isSnap {
+               return fmt.Errorf("Volume name must be a snapshot")
+       }
+
+       // Get the volume name on storage.
+       volStorageName := project.Prefix(inst.Project(), parentName)
+
+       err = b.driver.CreateVolumeSnapshot(volType, volStorageName, snapName, 
op)
+       if err != nil {
+               return err
+       }
+
+       err = b.ensureInstanceSnapshotSymlink(inst.Type(), inst.Project(), 
inst.Name())
+       if err != nil {
+               return err
+       }
+
+       return nil
 }
 
 // RenameInstanceSnapshot renames an instance snapshot.
_______________________________________________
lxc-devel mailing list
lxc-devel@lists.linuxcontainers.org
http://lists.linuxcontainers.org/listinfo/lxc-devel

Reply via email to