func (d *Driver) MountVolume(id string, opts map[string]string) (string, error) { volume := d.blankVolume(id) if err := util.ObjectLoad(volume); err != nil { return "", err } dev, err := d.GetVolumeDevice(id) if err != nil { return "", err } specifiedPoint := opts[convoydriver.OPT_MOUNT_POINT] mountPoint, err := d.getVolumeMountPoint(id, specifiedPoint) if err != nil { return "", err } if volume.MountPoint != "" && volume.MountPoint != mountPoint { return "", fmt.Errorf("volume %v already mounted at %v, but asked to mount at %v", id, volume.MountPoint, mountPoint) } if !mounted(dev, mountPoint) { log.Debugf("Volume %v is not mounted, mount it now to %v", id, mountPoint) _, err = util.Execute(MOUNT_BINARY, []string{dev, mountPoint}) if err != nil { return "", err } } volume.MountPoint = mountPoint if err := util.ObjectSave(volume); err != nil { return "", err } return mountPoint, nil }
func (d *Driver) doCreateVolume(volume *Volume, stack *Stack, id string, opts map[string]string) error { // Doing find just to see if we are creating versus using an existing stack env, err := stack.Find() if err != nil { return err } // Always run create because it also ensures that things are active if _, err := stack.Create(); err != nil { return err } // If env was nil then we created stack so we need to format if env == nil { dev, _ := volume.GetDevice() err := Backoff(5*time.Minute, fmt.Sprintf("Failed to find %s", dev), func() (bool, error) { if _, err := os.Stat(dev); err == nil { return true, nil } return false, nil }) if err != nil { return err } log.Infof("Formatting %s", dev) if _, err := util.Execute("mkfs.ext4", []string{dev}); err != nil { return err } } return util.ObjectSave(volume) }
func (d *Driver) CompareSnapshot(id, compareID, volumeID string) (*metadata.Mappings, error) { includeSame := false if compareID == "" || compareID == id { compareID = id includeSame = true } snap1, _, err := d.getSnapshotAndVolume(id, volumeID) if err != nil { return nil, err } snap2, _, err := d.getSnapshotAndVolume(compareID, volumeID) if err != nil { return nil, err } dev := d.MetadataDevice out, err := util.Execute(THIN_PROVISION_TOOLS_BINARY, []string{"thin_delta", "--snap1", strconv.Itoa(snap1.DevID), "--snap2", strconv.Itoa(snap2.DevID), dev}) if err != nil { return nil, err } mapping, err := metadata.DeviceMapperThinDeltaParser([]byte(out), d.ThinpoolBlockSize*SECTOR_SIZE, includeSame) if err != nil { return nil, err } return mapping, err }
func (v *VfsObjectStoreDriver) Download(src, dst string) error { _, err := util.Execute("cp", []string{v.updatePath(src), dst}) if err != nil { return err } return nil }
func (v *VfsObjectStoreDriver) Upload(src, dst string) error { tmpDst := dst + ".tmp" if v.FileExists(tmpDst) { v.Remove(tmpDst) } if err := v.preparePath(dst); err != nil { return err } _, err := util.Execute("cp", []string{src, v.updatePath(tmpDst)}) if err != nil { return err } _, err = util.Execute("mv", []string{v.updatePath(tmpDst), v.updatePath(dst)}) if err != nil { return err } return nil }
func (v *VfsObjectStoreDriver) List(path string) ([]string, error) { out, err := util.Execute("ls", []string{"-1", v.updatePath(path)}) if err != nil { return nil, err } var result []string if len(out) == 0 { return result, nil } result = strings.Split(strings.TrimSpace(string(out)), "\n") return result, nil }
func mounted(dev, mountPoint string) bool { output, err := util.Execute(MOUNT_BINARY, []string{}) if err != nil { return false } lines := strings.Split(output, "\n") for _, line := range lines { if strings.Contains(line, dev) && strings.Contains(line, mountPoint) { return true } } return false }
func (d *Driver) UmountVolume(id string) error { volume := d.blankVolume(id) if err := util.ObjectLoad(volume); err != nil { return err } if volume.MountPoint == "" { log.Debugf("Umount a umounted volume %v", id) return nil } if _, err := util.Execute(UMOUNT_BINARY, []string{volume.MountPoint}); err != nil { return err } d.putVolumeMountPoint(volume.MountPoint) volume.MountPoint = "" if err := util.ObjectSave(volume); err != nil { return err } return nil }
func (d *Driver) DeleteVolume(id string, opts map[string]string) error { d.mutex.Lock() defer d.mutex.Unlock() volume := d.blankVolume(id) if err := util.ObjectLoad(volume); err != nil { return err } if volume.MountPoint != "" { return fmt.Errorf("Cannot delete volume %v. It is still mounted", id) } referenceOnly, _ := strconv.ParseBool(opts[convoydriver.OPT_REFERENCE_ONLY]) if !referenceOnly { log.Debugf("Cleaning up %v for volume %v", volume.Path, id) if out, err := util.Execute("rm", []string{"-rf", volume.Path}); err != nil { return fmt.Errorf("Fail to cleanup the volume, output: %v, error: %v", out, err.Error()) } } return util.ObjectDelete(volume) }
func (d *Driver) CreateVolume(id string, opts map[string]string) error { var ( size int64 err error ) backupURL := opts[convoydriver.OPT_BACKUP_URL] if backupURL != "" { objVolume, err := objectstore.LoadVolume(backupURL) if err != nil { return err } if objVolume.Driver != d.Name() { return fmt.Errorf("Cannot restore backup of %v to %v", objVolume.Driver, d.Name()) } size, err = d.getSize(opts, objVolume.Size) if err != nil { return err } if size != objVolume.Size { return fmt.Errorf("Volume size must match with backup's size") } } else { size, err = d.getSize(opts, d.DefaultVolumeSize) if err != nil { return err } } if size%(d.ThinpoolBlockSize*SECTOR_SIZE) != 0 { return fmt.Errorf("Size must be multiple of block size") } volume := d.blankVolume(id) exists, err := util.ObjectExists(volume) if err != nil { return err } if exists { return generateError(logrus.Fields{ LOG_FIELD_VOLUME: id, }, "Already has volume with specific uuid") } devID, err := d.allocateDevID() if err != nil { return err } log.WithFields(logrus.Fields{ LOG_FIELD_REASON: LOG_REASON_START, LOG_FIELD_EVENT: LOG_EVENT_CREATE, LOG_FIELD_OBJECT: LOG_OBJECT_VOLUME, LOG_FIELD_VOLUME: id, DM_LOG_FIELD_VOLUME_DEVID: devID, }).Debugf("Creating volume") err = devicemapper.CreateDevice(d.ThinpoolDevice, devID) if err != nil { return err } log.WithFields(logrus.Fields{ LOG_FIELD_REASON: LOG_REASON_START, LOG_FIELD_EVENT: LOG_EVENT_ACTIVATE, LOG_FIELD_OBJECT: LOG_OBJECT_VOLUME, LOG_FIELD_VOLUME: id, DM_LOG_FIELD_VOLUME_DEVID: devID, }).Debugf("Activating device for volume") err = devicemapper.ActivateDevice(d.ThinpoolDevice, id, devID, uint64(size)) if err != nil { log.WithFields(logrus.Fields{ LOG_FIELD_REASON: LOG_REASON_ROLLBACK, LOG_FIELD_EVENT: LOG_EVENT_REMOVE, LOG_FIELD_OBJECT: LOG_OBJECT_VOLUME, LOG_FIELD_VOLUME: id, DM_LOG_FIELD_VOLUME_DEVID: devID, }).Debugf("Removing device for volume due to fail to activate") if err := devicemapper.DeleteDevice(d.ThinpoolDevice, devID); err != nil { log.WithFields(logrus.Fields{ LOG_FIELD_REASON: LOG_REASON_FAILURE, LOG_FIELD_EVENT: LOG_EVENT_REMOVE, LOG_FIELD_OBJECT: LOG_OBJECT_VOLUME, LOG_FIELD_VOLUME: id, DM_LOG_FIELD_VOLUME_DEVID: devID, }).Debugf("Failed to remove device") } return err } volume.DevID = devID volume.Size = size volume.Snapshots = make(map[string]Snapshot) if err := util.ObjectSave(volume); err != nil { return err } dev, err := d.GetVolumeDevice(id) if err != nil { return err } if backupURL == "" { // format the device if _, err := util.Execute("mkfs", []string{"-t", "ext4", dev}); err != nil { return err } } else { if err := objectstore.RestoreDeltaBlockBackup(backupURL, dev); err != nil { return err } } return nil }
func (d *Driver) CreateVolume(id string, opts map[string]string) error { var ( err error volumeSize int64 format bool snapshot *Snapshot ) d.mutex.Lock() defer d.mutex.Unlock() volume := d.blankVolume(id) exists, err := util.ObjectExists(volume) if err != nil { return err } if exists { return fmt.Errorf("Volume %v already exists", id) } //EBS volume ID volumeID := opts[convoydriver.OPT_VOLUME_ID] backupURL := opts[convoydriver.OPT_BACKUP_URL] if backupURL != "" && volumeID != "" { return fmt.Errorf("Cannot specify both backup and EBS volume ID") } newTags := map[string]string{ "Name": opts[convoydriver.OPT_VOLUME_NAME], "ConvoyVolumeUUID": id, } if volumeID != "" { ebsVolume, err := d.ebsService.GetVolume(volumeID) if err != nil { return err } volumeSize = *ebsVolume.Size * GB log.Debugf("Found EBS volume %v for volume %v, update tags", volumeID, id) if err := d.ebsService.AddTags(volumeID, newTags); err != nil { log.Debugf("Failed to update tags for volume %v, but continue", volumeID) } } else if backupURL != "" { region, ebsSnapshotID, err := decodeURL(backupURL) if err != nil { return err } if region != d.ebsService.Region { // We don't want to automatically copy snapshot here // because it's way too time consuming. return fmt.Errorf("Snapshot %v is at %v rather than current region %v. Copy snapshot is needed", ebsSnapshotID, region, d.ebsService.Region) } if err := d.ebsService.WaitForSnapshotComplete(ebsSnapshotID); err != nil { return err } log.Debugf("Snapshot %v is ready", ebsSnapshotID) ebsSnapshot, err := d.ebsService.GetSnapshot(ebsSnapshotID) if err != nil { return err } snapshot = &Snapshot{ UUID: uuid.New(), VolumeUUID: id, EBSID: ebsSnapshotID, } snapshotVolumeSize := *ebsSnapshot.VolumeSize * GB volumeSize, err = d.getSize(opts, snapshotVolumeSize) if err != nil { return err } if volumeSize < snapshotVolumeSize { return fmt.Errorf("Volume size cannot be less than snapshot size %v", snapshotVolumeSize) } volumeType, iops, err := d.getTypeAndIOPS(opts) if err != nil { return err } r := &CreateEBSVolumeRequest{ Size: volumeSize, SnapshotID: ebsSnapshotID, VolumeType: volumeType, IOPS: iops, Tags: newTags, } volumeID, err = d.ebsService.CreateVolume(r) if err != nil { return err } log.Debugf("Created volume %v from EBS snapshot %v", id, ebsSnapshotID) } else { // Create a new EBS volume volumeSize, err = d.getSize(opts, d.DefaultVolumeSize) if err != nil { return err } volumeType, iops, err := d.getTypeAndIOPS(opts) if err != nil { return err } r := &CreateEBSVolumeRequest{ Size: volumeSize, VolumeType: volumeType, IOPS: iops, Tags: newTags, } volumeID, err = d.ebsService.CreateVolume(r) if err != nil { return err } log.Debugf("Created volume %v from EBS volume %v", id, volumeID) format = true } dev, err := d.ebsService.AttachVolume(volumeID, volumeSize) if err != nil { return err } log.Debugf("Attached EBS volume %v to %v", volumeID, dev) volume.EBSID = volumeID volume.Device = dev volume.Snapshots = make(map[string]Snapshot) if snapshot != nil { volume.Snapshots[snapshot.UUID] = *snapshot } // We don't format existing or snapshot restored volume if format { if _, err := util.Execute("mkfs", []string{"-t", "ext4", dev}); err != nil { return err } } return util.ObjectSave(volume) }