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