// Rollback rolls back the volume to the given snapshot func (c *BtrfsConn) Rollback(label string) error { if exists, err := c.snapshotExists(label); err != nil || !exists { if err != nil { return err } else { return fmt.Errorf("snapshot %s does not exist", label) } } c.Lock() defer c.Unlock() vd := path.Join(c.root, c.name) dirp, err := volume.IsDir(vd) if err != nil { return err } glog.Infof("starting rollback of snapshot %s", label) start := time.Now() if dirp { timeout := getEnvMinDuration("SERVICED_BTRFS_ROLLBACK_TIMEOUT", 300, 120) glog.Infof("rollback using env var SERVICED_BTRFS_ROLLBACK_TIMEOUT:%s", timeout) for { cmd := []string{"subvolume", "delete", vd} output, deleteError := runcmd(c.sudoer, cmd...) if deleteError == nil { break } now := time.Now() if now.Sub(start) > timeout { glog.Errorf("rollback of snapshot %s failed - btrfs subvolume deletes took %s for cmd:%s", label, timeout, cmd) return deleteError } else if strings.Contains(string(output), "Device or resource busy") { waitTime := time.Duration(5 * time.Second) glog.Warningf("retrying rollback subvolume delete in %s - unable to run cmd:%s output:%s error:%s", waitTime, cmd, string(output), deleteError) time.Sleep(waitTime) } else { return deleteError } } } cmd := []string{"subvolume", "snapshot", c.SnapshotPath(label), vd} _, err = runcmd(c.sudoer, cmd...) if err != nil { glog.Errorf("rollback of snapshot %s failed for cmd:%s", label, cmd) } else { duration := time.Now().Sub(start) glog.Infof("rollback of snapshot %s took %s", label, duration) } return err }
// Snapshot performs a writable snapshot on the subvolume func (c *RsyncConn) Snapshot(label string) (err error) { c.Lock() defer c.Unlock() dest := c.SnapshotPath(label) if exists, err := volume.IsDir(dest); exists || err != nil { if exists { return fmt.Errorf("snapshot %s already exists", label) } return err } exe, err := exec.LookPath("rsync") if err != nil { return err } argv := []string{"-a", c.Path() + "/", dest + "/"} glog.Infof("Performing snapshot rsync command: %s %s", exe, argv) var output []byte for i := 0; i < 3; i++ { rsync := exec.Command(exe, argv...) done := make(chan interface{}) go func() { defer close(done) output, err = rsync.CombinedOutput() }() select { case <-time.After(c.timeout): glog.V(2).Infof("Received signal to kill rsync") rsync.Process.Kill() <-done case <-done: } if err == nil { return nil } if exitStatus, ok := utils.GetExitStatus(err); !ok || exitStatus != 24 { glog.Errorf("Could not perform rsync: %s", string(output)) return err } glog.Infof("trying snapshot again: %s", label) } if exitStatus, _ := utils.GetExitStatus(err); exitStatus == 24 { glog.Warningf("snapshot completed with errors: Partial transfer due to vanished source files") return nil } glog.Errorf("Could not perform rsync: %s", string(output)) return err }
// Import imports a snapshot func (c *RsyncConn) Import(label, indir string) (err error) { c.Lock() defer c.Unlock() if exists, err := volume.IsDir(c.SnapshotPath(label)); err != nil { return err } else if exists { return fmt.Errorf("snapshot %s exists", label) } rsync := exec.Command("rsync", "-azh", filepath.Join(indir, label), c.root) glog.V(4).Infof("About ro execute %s", rsync) if output, err := rsync.CombinedOutput(); err != nil { glog.V(2).Infof("Could not perform rsync: %s", string(output)) return err } return nil }
// Export copys a snapshot func (c *RsyncConn) Export(label, parent, outdir string) (err error) { c.Lock() defer c.Unlock() src := c.SnapshotPath(label) if exists, err := volume.IsDir(src); err != nil { return err } else if !exists { return fmt.Errorf("snapshot %s does not exist", label) } rsync := exec.Command("rsync", "-azh", src, outdir) glog.V(4).Infof("About ro execute %s", rsync) if output, err := rsync.CombinedOutput(); err != nil { glog.V(2).Infof("Could not perform rsync: %s", string(output)) return err } return nil }
// Rollback rolls back the volume to the given snapshot func (c *RsyncConn) Rollback(label string) (err error) { c.Lock() defer c.Unlock() src := c.SnapshotPath(label) if exists, err := volume.IsDir(src); !exists || err != nil { if !exists { return fmt.Errorf("snapshot %s does not exist", label) } return err } rsync := exec.Command("rsync", "-a", "--del", "--force", src+"/", c.Path()+"/") glog.V(4).Infof("About to execute: %s", rsync) if output, err := rsync.CombinedOutput(); err != nil { glog.V(2).Infof("Could not perform rsync: %s", string(output)) return err } return nil }