func createFromCopy(d *Daemon, req *containerPostReq) Response { if req.Source.Source == "" { return BadRequest(fmt.Errorf("must specify a source container")) } // Make sure the source exists. source, err := newLxdContainer(req.Source.Source, d) if err != nil { return SmartError(err) } if req.Config == nil { config := make(map[string]string) for key, value := range source.config { if key[0:8] == "volatile" { shared.Debugf("skipping: %s\n", key) continue } req.Config[key] = value } req.Config = config } if req.Profiles == nil { req.Profiles = source.profiles } args := DbCreateContainerArgs{ d: d, name: req.Name, ctype: cTypeRegular, config: req.Config, profiles: req.Profiles, ephem: req.Ephemeral, baseImage: req.Source.BaseImage, } _, err = dbCreateContainer(args) if err != nil { return SmartError(err) } var oldPath string if shared.IsSnapshot(req.Source.Source) { snappieces := strings.SplitN(req.Source.Source, "/", 2) oldPath = migration.AddSlash(shared.VarPath("lxc", snappieces[0], "snapshots", snappieces[1], "rootfs")) } else { oldPath = migration.AddSlash(shared.VarPath("lxc", req.Source.Source, "rootfs")) } subvol := strings.TrimSuffix(oldPath, "rootfs/") dpath := shared.VarPath("lxc", req.Name) // Destination path if !btrfsIsSubvolume(subvol) { if err := os.MkdirAll(dpath, 0700); err != nil { removeContainer(d, req.Name) return InternalError(err) } if err := extractShiftIfExists(d, source, req.Source.BaseImage, req.Name); err != nil { removeContainer(d, req.Name) return InternalError(err) } } newPath := fmt.Sprintf("%s/%s", dpath, "rootfs") run := func() shared.OperationResult { if btrfsIsSubvolume(subvol) { /* * Copy by using btrfs snapshot */ output, err := btrfsSnapshot(subvol, dpath, false) if err != nil { shared.Debugf("Failed to create a BTRFS Snapshot of '%s' to '%s'.", subvol, dpath) shared.Debugf(string(output)) return shared.OperationError(err) } } else { /* * Copy by using rsync */ output, err := exec.Command("rsync", "-a", "--devices", oldPath, newPath).CombinedOutput() if err != nil { shared.Debugf("rsync failed:\n%s", output) return shared.OperationError(err) } } if !source.isPrivileged() { err = setUnprivUserAcl(source, dpath) if err != nil { shared.Debugf("Error adding acl for container root: falling back to chmod\n") output, err := exec.Command("chmod", "+x", dpath).CombinedOutput() if err != nil { shared.Debugf("Error chmoding the container root\n") shared.Debugf(string(output)) return shared.OperationError(err) } } } c, err := newLxdContainer(req.Name, d) if err != nil { return shared.OperationError(err) } err = templateApply(c, "copy") if err != nil { return shared.OperationError(err) } return shared.OperationError(nil) } resources := make(map[string][]string) resources["containers"] = []string{req.Name, req.Source.Source} return &asyncResponse{run: run, resources: resources} }
func containerSnapRestore(d *Daemon, name string, snap string) error { // normalize snapshot name if !shared.IsSnapshot(snap) { snap = fmt.Sprintf("%s/%s", name, snap) } shared.Debugf("RESTORE => Restoring snapshot [%s] on container [%s]", snap, name) /* * restore steps: * 1. stop container if already running * 2. overwrite existing config with snapshot config * 3. copy snapshot rootfs to container */ wasRunning := false c, err := newLxdContainer(name, d) if err != nil { shared.Debugf("RESTORE => Error: newLxdContainer() failed for container", err) return err } // 1. stop container // TODO: stateful restore ? if c.c.Running() { wasRunning = true if err = c.Stop(); err != nil { shared.Debugf("RESTORE => Error: could not stop container", err) return err } shared.Debugf("RESTORE => Stopped container %s", name) } // 2, replace config // Make sure the source exists. source, err := newLxdContainer(snap, d) if err != nil { shared.Debugf("RESTORE => Error: newLxdContainer() failed for snapshot", err) return err } newConfig := containerConfigReq{} newConfig.Config = source.config newConfig.Profiles = source.profiles newConfig.Devices = source.devices err = containerReplaceConfig(d, c, name, newConfig) if err != nil { shared.Debugf("RESTORE => err #4", err) return err } // 3. copy rootfs // TODO: btrfs optimizations containerRootPath := shared.VarPath("lxc", name) if !shared.IsDir(path.Dir(containerRootPath)) { shared.Debugf("RESTORE => containerRoot [%s] directory does not exist", containerRootPath) return os.ErrNotExist } var snapshotRootFSPath string snapshotRootFSPath = migration.AddSlash(snapshotRootfsDir(c, strings.SplitN(snap, "/", 2)[1])) containerRootFSPath := migration.AddSlash(fmt.Sprintf("%s/%s", containerRootPath, "rootfs")) shared.Debugf("RESTORE => Copying %s to %s", snapshotRootFSPath, containerRootFSPath) rsyncVerbosity := "-q" if *debug { rsyncVerbosity = "-vi" } output, err := exec.Command("rsync", "-a", "-c", "-HAX", "--devices", "--delete", rsyncVerbosity, snapshotRootFSPath, containerRootFSPath).CombinedOutput() shared.Debugf("RESTORE => rsync output\n%s", output) if err == nil && !source.isPrivileged() { err = setUnprivUserAcl(c, containerRootPath) if err != nil { shared.Debugf("Error adding acl for container root: falling back to chmod\n") output, err := exec.Command("chmod", "+x", containerRootPath).CombinedOutput() if err != nil { shared.Debugf("Error chmoding the container root\n") shared.Debugf(string(output)) return err } } } else { shared.Debugf("rsync failed:\n%s", output) return err } if wasRunning { c.Start() } return nil }