diff --git a/internal/server/storage/drivers/driver_ceph_utils.go b/internal/server/storage/drivers/driver_ceph_utils.go index 419c85b698..1c78b15ccc 100644 --- a/internal/server/storage/drivers/driver_ceph_utils.go +++ b/internal/server/storage/drivers/driver_ceph_utils.go @@ -7,7 +7,6 @@ import ( "net/http" "os" "os/exec" - "regexp" "slices" "strconv" "strings" @@ -134,7 +133,7 @@ func (d *ceph) rbdCreateVolume(vol Volume, size string) error { cmd = append(cmd, "--size", fmt.Sprintf("%dB", sizeBytes), "create", - d.getRBDVolumeName(vol, "", false, false)) + d.getRBDVolumeName(vol, "", false)) _, err = subprocess.RunCommand("rbd", cmd...) return err @@ -152,7 +151,7 @@ func (d *ceph) rbdDeleteVolume(vol Volume) error { "--cluster", d.config["ceph.cluster_name"], "--pool", d.config["ceph.osd.pool_name"], "rm", - d.getRBDVolumeName(vol, "", false, false)) + d.getRBDVolumeName(vol, "", false)) if err != nil { return err } @@ -164,7 +163,7 @@ func (d *ceph) rbdDeleteVolume(vol Volume) error { // This will ensure that the RBD storage volume is accessible as a block device // in the /dev directory and is therefore necessary in order to mount it. func (d *ceph) rbdMapVolume(vol Volume) (string, error) { - rbdName := d.getRBDVolumeName(vol, "", false, false) + rbdName := d.getRBDVolumeName(vol, "", false) devPath, err := subprocess.RunCommand( "rbd", "--id", d.config["ceph.user.name"], @@ -191,7 +190,7 @@ func (d *ceph) rbdMapVolume(vol Volume) (string, error) { // This is a precondition in order to delete an RBD storage volume can. func (d *ceph) rbdUnmapVolume(vol Volume, unmapUntilEINVAL bool) error { busyCount := 0 - rbdVol := d.getRBDVolumeName(vol, "", false, false) + rbdVol := d.getRBDVolumeName(vol, "", false) ourDeactivate := false @@ -254,7 +253,7 @@ again: "--cluster", d.config["ceph.cluster_name"], "--pool", d.config["ceph.osd.pool_name"], "unmap", - d.getRBDVolumeName(vol, snapshotName, false, false)) + d.getRBDVolumeName(vol, snapshotName, false)) if err != nil { runError, ok := err.(subprocess.RunError) if ok { @@ -287,7 +286,7 @@ func (d *ceph) rbdCreateVolumeSnapshot(vol Volume, snapshotName string) error { "snap", "create", "--snap", snapshotName, - d.getRBDVolumeName(vol, "", false, false)) + d.getRBDVolumeName(vol, "", false)) if err != nil { return err } @@ -306,7 +305,7 @@ func (d *ceph) rbdProtectVolumeSnapshot(vol Volume, snapshotName string) error { "snap", "protect", "--snap", snapshotName, - d.getRBDVolumeName(vol, "", false, false)) + d.getRBDVolumeName(vol, "", false)) if err != nil { runError, ok := err.(subprocess.RunError) if ok { @@ -337,7 +336,7 @@ func (d *ceph) rbdUnprotectVolumeSnapshot(vol Volume, snapshotName string) error "snap", "unprotect", "--snap", snapshotName, - d.getRBDVolumeName(vol, "", false, false)) + d.getRBDVolumeName(vol, "", false)) if err != nil { runError, ok := err.(subprocess.RunError) if ok { @@ -377,8 +376,8 @@ func (d *ceph) rbdCreateClone(sourceVol Volume, sourceSnapshotName string, targe cmd = append(cmd, "clone", - d.getRBDVolumeName(sourceVol, sourceSnapshotName, false, true), - d.getRBDVolumeName(targetVol, "", false, true)) + d.getRBDVolumeName(sourceVol, sourceSnapshotName, true), + d.getRBDVolumeName(targetVol, "", true)) _, err := subprocess.RunCommand("rbd", cmd...) if err != nil { @@ -396,7 +395,7 @@ func (d *ceph) rbdListSnapshotClones(vol Volume, snapshotName string) ([]string, "--cluster", d.config["ceph.cluster_name"], "--pool", d.config["ceph.osd.pool_name"], "children", - "--image", d.getRBDVolumeName(vol, "", false, false), + "--image", d.getRBDVolumeName(vol, "", false), "--snap", snapshotName) if err != nil { return nil, err @@ -422,14 +421,15 @@ func (d *ceph) rbdMarkVolumeDeleted(vol Volume, newVolumeName string) error { // Ensure that new volume contains the config from the source volume to maintain filesystem suffix on // new volume name generated in getRBDVolumeName. newVol := NewVolume(d, d.name, vol.volType, vol.contentType, newVolumeName, vol.config, vol.poolConfig) - deletedName := d.getRBDVolumeName(newVol, "", true, true) + newVol.isDeleted = true + deletedName := d.getRBDVolumeName(newVol, "", true) _, err := subprocess.RunCommand( "rbd", "--id", d.config["ceph.user.name"], "--cluster", d.config["ceph.cluster_name"], "mv", - d.getRBDVolumeName(vol, "", false, true), + d.getRBDVolumeName(vol, "", true), deletedName, ) if err != nil { @@ -454,8 +454,8 @@ func (d *ceph) rbdRenameVolume(vol Volume, newVolumeName string) error { "--id", d.config["ceph.user.name"], "--cluster", d.config["ceph.cluster_name"], "mv", - d.getRBDVolumeName(vol, "", false, true), - d.getRBDVolumeName(newVol, "", false, true), + d.getRBDVolumeName(vol, "", true), + d.getRBDVolumeName(newVol, "", true), ) if err != nil { return err @@ -477,8 +477,8 @@ func (d *ceph) rbdRenameVolumeSnapshot(vol Volume, oldSnapshotName string, newSn "--cluster", d.config["ceph.cluster_name"], "snap", "rename", - d.getRBDVolumeName(vol, oldSnapshotName, false, true), - d.getRBDVolumeName(vol, newSnapshotName, false, true)) + d.getRBDVolumeName(vol, oldSnapshotName, true), + d.getRBDVolumeName(vol, newSnapshotName, true)) if err != nil { return err } @@ -500,7 +500,7 @@ func (d *ceph) rbdGetVolumeParent(vol Volume) (string, error) { "--cluster", d.config["ceph.cluster_name"], "--pool", d.config["ceph.osd.pool_name"], "info", - d.getRBDVolumeName(vol, "", false, false)) + d.getRBDVolumeName(vol, "", false)) if err != nil { return "", err } @@ -535,7 +535,7 @@ func (d *ceph) rbdDeleteVolumeSnapshot(vol Volume, snapshotName string) error { "--pool", d.config["ceph.osd.pool_name"], "snap", "rm", - d.getRBDVolumeName(vol, snapshotName, false, false)) + d.getRBDVolumeName(vol, snapshotName, false)) if err != nil { return err } @@ -558,7 +558,7 @@ func (d *ceph) rbdListVolumeSnapshots(vol Volume) ([]string, error) { "--format", "json", "snap", "ls", - d.getRBDVolumeName(vol, "", false, false)) + d.getRBDVolumeName(vol, "", false)) if err != nil { return []string{}, err } @@ -678,7 +678,7 @@ func (d *ceph) deleteVolume(vol Volume) (int, error) { return -1, err } - if strings.HasPrefix(vol.name, "zombie_") || strings.HasPrefix(string(vol.volType), "zombie_") { + if vol.isDeleted { return 1, nil } @@ -723,7 +723,7 @@ func (d *ceph) deleteVolume(vol Volume) (int, error) { // Only delete the parent snapshot of the instance if it is a zombie. // This includes both if the parent volume itself is a zombie, or if the just the snapshot // is a zombie. If it is not we know that Incus is still using it. - if strings.HasPrefix(string(parentVol.volType), "zombie_") || strings.HasPrefix(parentSnapshotName, "zombie_") { + if parentVol.isDeleted || strings.HasPrefix(parentSnapshotName, "zombie_") { ret, err := d.deleteVolumeSnapshot(parentVol, parentSnapshotName) if ret < 0 { return -1, err @@ -794,7 +794,7 @@ func (d *ceph) deleteVolumeSnapshot(vol Volume, snapshotName string) (int, error } // Only delete the parent image if it is a zombie. If it is not we know that Incus is still using it. - if strings.HasPrefix(string(vol.volType), "zombie_") { + if vol.isDeleted { ret, err := d.deleteVolume(vol) if ret < 0 { return -1, err @@ -806,17 +806,18 @@ func (d *ceph) deleteVolumeSnapshot(vol Volume, snapshotName string) (int, error canDelete := true for _, clone := range clones { - _, cloneType, cloneName, err := d.parseClone(clone) + _, cloneType, cloneName, isDeleted, err := d.parseClone(clone) if err != nil { return -1, err } - if !strings.HasPrefix(cloneType, "zombie_") { + if !isDeleted { canDelete = false continue } cloneVol := NewVolume(d, d.name, VolumeType(cloneType), vol.contentType, cloneName, nil, nil) + cloneVol.isDeleted = isDeleted ret, err := d.deleteVolume(cloneVol) if ret < 0 { @@ -848,7 +849,7 @@ func (d *ceph) deleteVolumeSnapshot(vol Volume, snapshotName string) (int, error // Only delete the parent image if it is a zombie. If it // is not we know that Incus is still using it. - if strings.HasPrefix(string(vol.volType), "zombie_") { + if vol.isDeleted { ret, err := d.deleteVolume(vol) if ret < 0 { return -1, err @@ -880,68 +881,154 @@ func (d *ceph) deleteVolumeSnapshot(vol Volume, snapshotName string) (int, error func (d *ceph) parseParent(parent string) (Volume, string, error) { vol := Volume{} - idx := strings.Index(parent, "/") - if idx == -1 { + fields := strings.SplitN(parent, "/", 2) + if len(fields) != 2 { return vol, "", fmt.Errorf("Pool delimiter not found") } - slider := parent[(idx + 1):] - poolName := parent[:idx] + parentName := fields[1] + vol.pool = fields[0] + vol.isDeleted = strings.HasPrefix(parentName, "zombie_") - // Match image volumes and extract their various parts into a Volume struct. - // Looks for volumes like: - // pool/zombie_image_9e90b7b9ccdd7a671a987fadcf07ab92363be57e7f056d18d42af452cdaf95bb_ext4.block@readonly - // pool/image_9e90b7b9ccdd7a671a987fadcf07ab92363be57e7f056d18d42af452cdaf95bb_xfs - reImage, err := regexp.Compile(`^((?:zombie_)?image)_([A-Za-z0-9]+)_([A-Za-z0-9]+)\.?(block)?@?([-\w]+)?$`) - if err != nil { - return vol, "", err + // Handle images. + if strings.HasPrefix(parentName, "image_") || strings.HasPrefix(parentName, "zombie_image_") { + vol.volType = VolumeTypeImage + + // Split snapshot name. + s := strings.Split(parentName, "@") + var name string + var snapName string + + if len(s) > 1 { + snapName = s[len(s)-1] + name = strings.TrimSuffix(parentName, "@"+snapName) + } else { + name = parentName + } + + // Remove prefix from name. + name = strings.SplitN(name, "image_", 2)[1] + + // Check for block indicator. + if strings.HasSuffix(name, ".block") { + name = strings.TrimSuffix(name, ".block") + vol.contentType = ContentTypeBlock + } else { + vol.contentType = ContentTypeFS + } + + // Check for filesystem indicator. + if strings.Contains(name, "_") { + s := strings.Split(name, "_") + filesystem := s[len(s)-1] + + name = strings.TrimSuffix(name, "_"+filesystem) + vol.config = map[string]string{ + "block.filesystem": filesystem, + } + } + + vol.name = name + return vol, snapName, nil } - imageRes := reImage.FindStringSubmatch(slider) - if imageRes != nil { - vol.volType = VolumeType(imageRes[1]) - vol.pool = poolName - vol.name = imageRes[2] - vol.config = map[string]string{ - "block.filesystem": imageRes[3], + // Handle custom volumes. + if strings.HasPrefix(parentName, "custom_") || strings.HasPrefix(parentName, "zombie_custom_") { + vol.volType = VolumeTypeCustom + + // Split snapshot name. + s := strings.Split(parentName, "@") + var name string + var snapName string + + if len(s) > 1 { + snapName = s[len(s)-1] + name = strings.TrimSuffix(parentName, "@"+snapName) + } else { + name = parentName } - if imageRes[4] == "block" { + // Remove prefix from name. + name = strings.SplitN(name, "custom_", 2)[1] + + // Check for block or ISO indicator. + if strings.HasSuffix(name, ".block") { + name = strings.TrimSuffix(name, ".block") vol.contentType = ContentTypeBlock + } else if strings.HasSuffix(name, ".iso") { + name = strings.TrimSuffix(name, ".iso") + vol.contentType = ContentTypeISO } else { vol.contentType = ContentTypeFS } - return vol, imageRes[5], nil + vol.name = name + return vol, snapName, nil } - // Match normal instance volumes. - // Looks for volumes like: - // pool/container_bar@zombie_snapshot_ce77e971-6c1b-45c0-b193-dba9ec5e7d82 - reInst, err := regexp.Compile(`^((?:zombie_)?[a-z-]+)_([\w-]+)\.?(block|iso)?@?([-\w]+)?$`) - if err != nil { - return vol, "", err + // Handle container volumes. + if strings.HasPrefix(parentName, "container_") || strings.HasPrefix(parentName, "zombie_container_") { + vol.volType = VolumeTypeContainer + + // Split snapshot name. + s := strings.Split(parentName, "@") + var name string + var snapName string + + if len(s) > 1 { + snapName = s[len(s)-1] + name = strings.TrimSuffix(parentName, "@"+snapName) + } else { + name = parentName + } + + // Remove prefix from name. + name = strings.SplitN(name, "container_", 2)[1] + + // Check for block indicator. + if strings.HasSuffix(name, ".block") { + name = strings.TrimSuffix(name, ".block") + vol.contentType = ContentTypeBlock + } else { + vol.contentType = ContentTypeFS + } + + vol.name = name + return vol, snapName, nil } - instRes := reInst.FindStringSubmatch(slider) - if instRes != nil { - vol.volType = VolumeType(instRes[1]) - vol.pool = poolName - vol.name = instRes[2] + // Handle virtual-machines volumes. + if strings.HasPrefix(parentName, "virtual_machine_") || strings.HasPrefix(parentName, "zombie_virtual_machine_") { + vol.volType = VolumeTypeVM + + // Split snapshot name. + s := strings.Split(parentName, "@") + var name string + var snapName string + + if len(s) > 1 { + snapName = s[len(s)-1] + name = strings.TrimSuffix(parentName, "@"+snapName) + } else { + name = parentName + } + + // Remove prefix from name. + name = strings.SplitN(name, "virtual_machine_", 2)[1] - switch instRes[3] { - case "block": + // Check for block indicator. + if strings.HasSuffix(name, ".block") { + name = strings.TrimSuffix(name, ".block") vol.contentType = ContentTypeBlock - case "iso": - vol.contentType = ContentTypeISO - default: + } else { vol.contentType = ContentTypeFS } - return vol, instRes[4], nil + vol.name = name + return vol, snapName, nil } - return vol, "", fmt.Errorf("Unrecognised parent: %q", parent) + return vol, "", fmt.Errorf("Unrecognized parent: %q", parent) } // parseClone splits a strings describing an RBD storage volume. @@ -949,48 +1036,28 @@ func (d *ceph) parseParent(parent string) (Volume, string, error) { // /_ // will be split into // , , . -func (d *ceph) parseClone(clone string) (string, string, string, error) { - idx := strings.Index(clone, "/") - if idx == -1 { - return "", "", "", fmt.Errorf("Unexpected parsing error") +func (d *ceph) parseClone(clone string) (string, string, string, bool, error) { + fields := strings.SplitN(clone, "/", 2) + if len(fields) != 2 { + return "", "", "", false, fmt.Errorf("Pool delimiter not found") } - slider := clone[(idx + 1):] - poolName := clone[:idx] + volumeName := fields[1] + poolName := fields[0] + volumeDeleted := strings.HasPrefix(volumeName, "zombie_") - volumeType := slider - idx = strings.Index(slider, "zombie_") - if idx == 0 { - idx += len("zombie_") - volumeType = slider - slider = slider[idx:] - } + // Strip zombie prefix. + volumeName = strings.TrimPrefix(volumeName, "zombie_") - idxType := strings.Index(slider, "_") - if idxType == -1 { - return "", "", "", fmt.Errorf("Unexpected parsing error") - } - - if idx == len("zombie_") { - idxType += idx - } - - volumeType = volumeType[:idxType] - - idx = strings.Index(slider, "_") - if idx == -1 { - return "", "", "", fmt.Errorf("Unexpected parsing error") - } - - volumeName := slider - idx = strings.Index(volumeName, "_") - if idx == -1 { - return "", "", "", fmt.Errorf("Unexpected parsing error") + f := strings.SplitN(volumeName, "_", 2) + if len(f) != 2 { + return "", "", "", false, fmt.Errorf("Unexpected parsing error") } - volumeName = volumeName[(idx + 1):] + volumeType := f[0] + volumeName = f[1] - return poolName, volumeType, volumeName, nil + return poolName, volumeType, volumeName, volumeDeleted, nil } // getRBDMappedDevPath looks at sysfs to retrieve the device path. If it doesn't find it it will map it if told to @@ -1044,7 +1111,7 @@ func (d *ceph) getRBDMappedDevPath(vol Volume, mapIfMissing bool) (bool, string, return false, "", err } - rbdName := d.getRBDVolumeName(vol, "", false, false) + rbdName := d.getRBDVolumeName(vol, "", false) // Split RBD name into volume name and snapshot name parts. rbdNameParts := strings.SplitN(rbdName, "@", 2) @@ -1104,8 +1171,8 @@ func (d *ceph) generateUUID(fsType string, devPath string) error { return nil } -func (d *ceph) getRBDVolumeName(vol Volume, snapName string, zombie bool, withPoolName bool) string { - out := CephGetRBDImageName(vol, snapName, zombie) +func (d *ceph) getRBDVolumeName(vol Volume, snapName string, withPoolName bool) string { + out := CephGetRBDImageName(vol, snapName, vol.isDeleted) // If needed, the output will be prefixed with the pool name, e.g. // /_@. @@ -1259,7 +1326,7 @@ func (d *ceph) resizeVolume(vol Volume, sizeBytes int64, allowShrink bool) error "--cluster", d.config["ceph.cluster_name"], "--pool", d.config["ceph.osd.pool_name"], "--size", fmt.Sprintf("%dB", sizeBytes), - d.getRBDVolumeName(vol, "", false, false), + d.getRBDVolumeName(vol, "", false), ) // Resize the block device. diff --git a/internal/server/storage/drivers/driver_ceph_utils_test.go b/internal/server/storage/drivers/driver_ceph_utils_test.go index 0b23a54faa..343d579ceb 100644 --- a/internal/server/storage/drivers/driver_ceph_utils_test.go +++ b/internal/server/storage/drivers/driver_ceph_utils_test.go @@ -9,7 +9,6 @@ func Test_ceph_getRBDVolumeName(t *testing.T) { type args struct { vol Volume snapName string - zombie bool withPoolName bool } @@ -23,7 +22,6 @@ func Test_ceph_getRBDVolumeName(t *testing.T) { args{ vol: NewVolume(nil, "testpool", VolumeTypeContainer, ContentTypeFS, "testvol", nil, nil), snapName: "", - zombie: false, withPoolName: false, }, "container_testvol", @@ -33,7 +31,6 @@ func Test_ceph_getRBDVolumeName(t *testing.T) { args{ vol: NewVolume(nil, "testpool", VolumeType("unknown"), ContentTypeFS, "testvol", nil, nil), snapName: "", - zombie: false, withPoolName: false, }, "unknown_testvol", @@ -41,9 +38,12 @@ func Test_ceph_getRBDVolumeName(t *testing.T) { { "Volume without pool name in zombie mode", args{ - vol: NewVolume(nil, "testpool", VolumeTypeContainer, ContentTypeFS, "testvol", nil, nil), + vol: func() Volume { + vol := NewVolume(nil, "testpool", VolumeTypeContainer, ContentTypeFS, "testvol", nil, nil) + vol.isDeleted = true + return vol + }(), snapName: "", - zombie: true, withPoolName: false, }, "zombie_container_testvol", @@ -51,9 +51,12 @@ func Test_ceph_getRBDVolumeName(t *testing.T) { { "Volume with pool name in zombie mode", args{ - vol: NewVolume(nil, "testpool", VolumeTypeContainer, ContentTypeFS, "testvol", nil, nil), + vol: func() Volume { + vol := NewVolume(nil, "testpool", VolumeTypeContainer, ContentTypeFS, "testvol", nil, nil) + vol.isDeleted = true + return vol + }(), snapName: "", - zombie: true, withPoolName: true, }, "testosdpool/zombie_container_testvol", @@ -63,7 +66,6 @@ func Test_ceph_getRBDVolumeName(t *testing.T) { args{ vol: NewVolume(nil, "testpool", VolumeTypeContainer, ContentTypeFS, "testvol", nil, nil), snapName: "snapshot_testsnap", - zombie: false, withPoolName: false, }, "container_testvol@snapshot_testsnap", @@ -73,7 +75,6 @@ func Test_ceph_getRBDVolumeName(t *testing.T) { args{ vol: NewVolume(nil, "testpool", VolumeTypeContainer, ContentTypeFS, "testvol", nil, nil), snapName: "snapshot_testsnap", - zombie: false, withPoolName: true, }, "testosdpool/container_testvol@snapshot_testsnap", @@ -83,7 +84,6 @@ func Test_ceph_getRBDVolumeName(t *testing.T) { args{ vol: NewVolume(nil, "testpool", VolumeTypeContainer, ContentTypeFS, "testvol/testsnap", nil, nil), snapName: "", - zombie: false, withPoolName: true, }, "testosdpool/container_testvol@snapshot_testsnap", @@ -93,7 +93,6 @@ func Test_ceph_getRBDVolumeName(t *testing.T) { args{ vol: NewVolume(nil, "testpool", VolumeTypeContainer, ContentTypeFS, "testvol/testsnap", nil, nil), snapName: "testsnap1", - zombie: false, withPoolName: true, }, "testosdpool/container_testvol@testsnap1", @@ -110,13 +109,14 @@ func Test_ceph_getRBDVolumeName(t *testing.T) { }, } - got := d.getRBDVolumeName(tt.args.vol, tt.args.snapName, tt.args.zombie, tt.args.withPoolName) + got := d.getRBDVolumeName(tt.args.vol, tt.args.snapName, tt.args.withPoolName) if got != tt.want { t.Errorf("ceph.getRBDVolumeName() = %v, want %v", got, tt.want) } }) } } + func Example_ceph_parseParent() { d := &ceph{} @@ -131,21 +131,98 @@ func Example_ceph_parseParent() { "pool/container_bar@zombie_snapshot_ce77e971-6c1b-45c0-b193-dba9ec5e7d82", "pool/container_test-project_c4.block", "pool/zombie_container_test-project_c1_28e7a7ab-740a-490c-8118-7caf7810f83b@zombie_snapshot_1027f4ab-de11-4cee-8015-bd532a1fed76", + "pool/custom_default_foo.vol@zombie_snapshot_2e8112b2-91ca-4fc2-a546-c7723395fdbd", } for _, parent := range parents { vol, snapName, err := d.parseParent(parent) - fmt.Println(vol.pool, vol.volType, vol.name, vol.config["block.filesystem"], vol.contentType, snapName, err) + fmt.Printf("%s: %v\n", parent, err) + if err == nil { + fmt.Printf(" pool: %s\n", vol.pool) + fmt.Printf(" name: %s\n", vol.name) + + if snapName != "" { + fmt.Printf(" snapName: %s\n", snapName) + } + + fmt.Printf(" volType: %s\n", vol.volType) + fmt.Printf(" contentType: %s\n", vol.contentType) + fmt.Printf(" config: %+v\n", vol.config) + } } - // Output: pool zombie_image 9e90b7b9ccdd7a671a987fadcf07ab92363be57e7f056d18d42af452cdaf95bb ext4 block readonly - // pool zombie_image 9e90b7b9ccdd7a671a987fadcf07ab92363be57e7f056d18d42af452cdaf95bb ext4 block - // pool image 9e90b7b9ccdd7a671a987fadcf07ab92363be57e7f056d18d42af452cdaf95bb ext4 block readonly - // pool zombie_image 9e90b7b9ccdd7a671a987fadcf07ab92363be57e7f056d18d42af452cdaf95bb ext4 filesystem readonly - // pool zombie_image 9e90b7b9ccdd7a671a987fadcf07ab92363be57e7f056d18d42af452cdaf95bb ext4 filesystem - // pool image 9e90b7b9ccdd7a671a987fadcf07ab92363be57e7f056d18d42af452cdaf95bb ext4 filesystem readonly - // pool zombie_image 2cfc5a5567b8d74c0986f3d8a77a2a78e58fe22ea9abd2693112031f85afa1a1 xfs filesystem zombie_snapshot_7f6d679b-ee25-419e-af49-bb805cb32088 - // pool container bar filesystem zombie_snapshot_ce77e971-6c1b-45c0-b193-dba9ec5e7d82 - // pool container test-project_c4 block - // pool zombie_container test-project_c1_28e7a7ab-740a-490c-8118-7caf7810f83b filesystem zombie_snapshot_1027f4ab-de11-4cee-8015-bd532a1fed76 + // Output: pool/zombie_image_9e90b7b9ccdd7a671a987fadcf07ab92363be57e7f056d18d42af452cdaf95bb_ext4.block@readonly: + // pool: pool + // name: 9e90b7b9ccdd7a671a987fadcf07ab92363be57e7f056d18d42af452cdaf95bb + // snapName: readonly + // volType: images + // contentType: block + // config: map[block.filesystem:ext4] + // pool/zombie_image_9e90b7b9ccdd7a671a987fadcf07ab92363be57e7f056d18d42af452cdaf95bb_ext4.block: + // pool: pool + // name: 9e90b7b9ccdd7a671a987fadcf07ab92363be57e7f056d18d42af452cdaf95bb + // volType: images + // contentType: block + // config: map[block.filesystem:ext4] + // pool/image_9e90b7b9ccdd7a671a987fadcf07ab92363be57e7f056d18d42af452cdaf95bb_ext4.block@readonly: + // pool: pool + // name: 9e90b7b9ccdd7a671a987fadcf07ab92363be57e7f056d18d42af452cdaf95bb + // snapName: readonly + // volType: images + // contentType: block + // config: map[block.filesystem:ext4] + // pool/zombie_image_9e90b7b9ccdd7a671a987fadcf07ab92363be57e7f056d18d42af452cdaf95bb_ext4@readonly: + // pool: pool + // name: 9e90b7b9ccdd7a671a987fadcf07ab92363be57e7f056d18d42af452cdaf95bb + // snapName: readonly + // volType: images + // contentType: filesystem + // config: map[block.filesystem:ext4] + // pool/zombie_image_9e90b7b9ccdd7a671a987fadcf07ab92363be57e7f056d18d42af452cdaf95bb_ext4: + // pool: pool + // name: 9e90b7b9ccdd7a671a987fadcf07ab92363be57e7f056d18d42af452cdaf95bb + // volType: images + // contentType: filesystem + // config: map[block.filesystem:ext4] + // pool/image_9e90b7b9ccdd7a671a987fadcf07ab92363be57e7f056d18d42af452cdaf95bb_ext4@readonly: + // pool: pool + // name: 9e90b7b9ccdd7a671a987fadcf07ab92363be57e7f056d18d42af452cdaf95bb + // snapName: readonly + // volType: images + // contentType: filesystem + // config: map[block.filesystem:ext4] + // pool/zombie_image_2cfc5a5567b8d74c0986f3d8a77a2a78e58fe22ea9abd2693112031f85afa1a1_xfs@zombie_snapshot_7f6d679b-ee25-419e-af49-bb805cb32088: + // pool: pool + // name: 2cfc5a5567b8d74c0986f3d8a77a2a78e58fe22ea9abd2693112031f85afa1a1 + // snapName: zombie_snapshot_7f6d679b-ee25-419e-af49-bb805cb32088 + // volType: images + // contentType: filesystem + // config: map[block.filesystem:xfs] + // pool/container_bar@zombie_snapshot_ce77e971-6c1b-45c0-b193-dba9ec5e7d82: + // pool: pool + // name: bar + // snapName: zombie_snapshot_ce77e971-6c1b-45c0-b193-dba9ec5e7d82 + // volType: containers + // contentType: filesystem + // config: map[] + // pool/container_test-project_c4.block: + // pool: pool + // name: test-project_c4 + // volType: containers + // contentType: block + // config: map[] + // pool/zombie_container_test-project_c1_28e7a7ab-740a-490c-8118-7caf7810f83b@zombie_snapshot_1027f4ab-de11-4cee-8015-bd532a1fed76: + // pool: pool + // name: test-project_c1_28e7a7ab-740a-490c-8118-7caf7810f83b + // snapName: zombie_snapshot_1027f4ab-de11-4cee-8015-bd532a1fed76 + // volType: containers + // contentType: filesystem + // config: map[] + // pool/custom_default_foo.vol@zombie_snapshot_2e8112b2-91ca-4fc2-a546-c7723395fdbd: + // pool: pool + // name: default_foo.vol + // snapName: zombie_snapshot_2e8112b2-91ca-4fc2-a546-c7723395fdbd + // volType: custom + // contentType: filesystem + // config: map[] } diff --git a/internal/server/storage/drivers/driver_ceph_volumes.go b/internal/server/storage/drivers/driver_ceph_volumes.go index 520aa95aba..e1b38fa00a 100644 --- a/internal/server/storage/drivers/driver_ceph_volumes.go +++ b/internal/server/storage/drivers/driver_ceph_volumes.go @@ -76,7 +76,7 @@ func (d *ceph) CreateVolume(vol Volume, filler *VolumeFiller, op *operations.Ope // Check if the cached image volume is larger than the current pool volume.size setting // (if so we won't be able to resize the snapshot to that the smaller size later). - volSizeBytes, err := d.getVolumeSize(d.getRBDVolumeName(deletedVol, "", false, true)) + volSizeBytes, err := d.getVolumeSize(d.getRBDVolumeName(deletedVol, "", true)) if err != nil { return err } @@ -97,7 +97,7 @@ func (d *ceph) CreateVolume(vol Volume, filler *VolumeFiller, op *operations.Ope if volSizeBytes != poolVolSizeBytes { d.logger.Debug("Renaming deleted cached image volume so that regeneration is used", logger.Ctx{"fingerprint": vol.Name()}) randomVol := NewVolume(d, d.name, deletedVol.volType, deletedVol.contentType, strings.Replace(uuid.New().String(), "-", "", -1), deletedVol.config, deletedVol.poolConfig) - err = renameVolume(d.getRBDVolumeName(deletedVol, "", false, true), d.getRBDVolumeName(randomVol, "", false, true)) + err = renameVolume(d.getRBDVolumeName(deletedVol, "", true), d.getRBDVolumeName(randomVol, "", true)) if err != nil { return err } @@ -106,7 +106,7 @@ func (d *ceph) CreateVolume(vol Volume, filler *VolumeFiller, op *operations.Ope fsDeletedVol := deletedVol.NewVMBlockFilesystemVolume() fsRandomVol := randomVol.NewVMBlockFilesystemVolume() - err = renameVolume(d.getRBDVolumeName(fsDeletedVol, "", false, true), d.getRBDVolumeName(fsRandomVol, "", false, true)) + err = renameVolume(d.getRBDVolumeName(fsDeletedVol, "", true), d.getRBDVolumeName(fsRandomVol, "", true)) if err != nil { return err } @@ -118,7 +118,7 @@ func (d *ceph) CreateVolume(vol Volume, filler *VolumeFiller, op *operations.Ope // Restore the image. if canRestore { d.logger.Debug("Restoring previously deleted cached image volume", logger.Ctx{"fingerprint": vol.Name()}) - err = renameVolume(d.getRBDVolumeName(deletedVol, "", false, true), d.getRBDVolumeName(vol, "", false, true)) + err = renameVolume(d.getRBDVolumeName(deletedVol, "", true), d.getRBDVolumeName(vol, "", true)) if err != nil { return err } @@ -127,7 +127,7 @@ func (d *ceph) CreateVolume(vol Volume, filler *VolumeFiller, op *operations.Ope fsDeletedVol := deletedVol.NewVMBlockFilesystemVolume() fsVol := vol.NewVMBlockFilesystemVolume() - err = renameVolume(d.getRBDVolumeName(fsDeletedVol, "", false, true), d.getRBDVolumeName(fsVol, "", false, true)) + err = renameVolume(d.getRBDVolumeName(fsDeletedVol, "", true), d.getRBDVolumeName(fsVol, "", true)) if err != nil { return err } @@ -395,8 +395,8 @@ func (d *ceph) CreateVolumeFromCopy(vol Volume, srcVol Volume, copySnapshots boo "--id", d.config["ceph.user.name"], "--cluster", d.config["ceph.cluster_name"], "cp", - d.getRBDVolumeName(srcVol, "", false, true), - d.getRBDVolumeName(vol, "", false, true), + d.getRBDVolumeName(srcVol, "", true), + d.getRBDVolumeName(vol, "", true), ) if err != nil { return err @@ -466,7 +466,7 @@ func (d *ceph) CreateVolumeFromCopy(vol Volume, srcVol Volume, copySnapshots boo revert.Add(func() { _ = d.rbdDeleteVolume(vol) }) // Receive over the placeholder volume we created above. - targetVolumeName := d.getRBDVolumeName(vol, "", false, true) + targetVolumeName := d.getRBDVolumeName(vol, "", true) lastSnap := "" @@ -484,7 +484,7 @@ func (d *ceph) CreateVolumeFromCopy(vol Volume, srcVol Volume, copySnapshots boo } lastSnap = fmt.Sprintf("snapshot_%s", snap) - sourceVolumeName := d.getRBDVolumeName(srcVol, lastSnap, false, true) + sourceVolumeName := d.getRBDVolumeName(srcVol, lastSnap, true) err = d.copyWithSnapshots(sourceVolumeName, targetVolumeName, prev) if err != nil { return err @@ -504,7 +504,7 @@ func (d *ceph) CreateVolumeFromCopy(vol Volume, srcVol Volume, copySnapshots boo } // Copy snapshot. - sourceVolumeName := d.getRBDVolumeName(srcVol, "", false, true) + sourceVolumeName := d.getRBDVolumeName(srcVol, "", true) err = d.copyWithSnapshots(sourceVolumeName, targetVolumeName, lastSnap) if err != nil { @@ -554,7 +554,7 @@ func (d *ceph) CreateVolumeFromMigration(vol Volume, conn io.ReadWriteCloser, vo } } - recvName := d.getRBDVolumeName(vol, "", false, true) + recvName := d.getRBDVolumeName(vol, "", true) volExists, err := d.HasVolume(vol) if err != nil { @@ -583,7 +583,7 @@ func (d *ceph) CreateVolumeFromMigration(vol Volume, conn io.ReadWriteCloser, vo // Transfer the snapshots. for _, snapName := range volTargetArgs.Snapshots { - fullSnapshotName := d.getRBDVolumeName(vol, snapName, false, true) + fullSnapshotName := d.getRBDVolumeName(vol, snapName, true) wrapper := localMigration.ProgressWriter(op, "fs_progress", fullSnapshotName) err = d.receiveVolume(recvName, conn, wrapper) @@ -667,7 +667,7 @@ func (d *ceph) DeleteVolume(vol Volume, op *operations.Operation) error { return err } - hasReadonlySnapshot, err := d.hasVolume(d.getRBDVolumeName(vol, "readonly", false, false)) + hasReadonlySnapshot, err := d.hasVolume(d.getRBDVolumeName(vol, "readonly", false)) if err != nil { return err } @@ -707,7 +707,7 @@ func (d *ceph) DeleteVolume(vol Volume, op *operations.Operation) error { "--pool", d.config["ceph.osd.pool_name"], "snap", "purge", - d.getRBDVolumeName(vol, "", false, false)) + d.getRBDVolumeName(vol, "", false)) if err != nil { return err } @@ -790,7 +790,7 @@ func (d *ceph) hasVolume(rbdVolumeName string) (bool, error) { // HasVolume indicates whether a specific volume exists on the storage pool. func (d *ceph) HasVolume(vol Volume) (bool, error) { - return d.hasVolume(d.getRBDVolumeName(vol, "", false, false)) + return d.hasVolume(d.getRBDVolumeName(vol, "", false)) } // FillVolumeConfig populate volume with default config. @@ -918,7 +918,7 @@ func (d *ceph) GetVolumeUsage(vol Volume) (int64, error) { "--id", d.config["ceph.user.name"], "--cluster", d.config["ceph.cluster_name"], "--pool", d.config["ceph.osd.pool_name"], - d.getRBDVolumeName(vol, "", false, false), + d.getRBDVolumeName(vol, "", false), ) if err != nil { return -1, err @@ -1428,7 +1428,7 @@ func (d *ceph) MigrateVolume(vol Volume, conn io.ReadWriteCloser, volSrcArgs *lo } lastSnap = fmt.Sprintf("snapshot_%s", snapName) - sendSnapName := d.getRBDVolumeName(vol, lastSnap, false, true) + sendSnapName := d.getRBDVolumeName(vol, lastSnap, true) // Setup progress tracking. var wrapper *ioprogress.ProgressTracker @@ -1458,7 +1458,7 @@ func (d *ceph) MigrateVolume(vol Volume, conn io.ReadWriteCloser, volSrcArgs *lo defer func() { _ = d.rbdDeleteVolumeSnapshot(vol, runningSnapName) }() - cur := d.getRBDVolumeName(vol, runningSnapName, false, true) + cur := d.getRBDVolumeName(vol, runningSnapName, true) err = d.sendVolume(conn, cur, lastSnap, wrapper) if err != nil { @@ -1537,7 +1537,7 @@ func (d *ceph) DeleteVolumeSnapshot(snapVol Volume, op *operations.Operation) er "--cluster", d.config["ceph.cluster_name"], "--pool", d.config["ceph.osd.pool_name"], "info", - d.getRBDVolumeName(snapVol, "", false, false)) + d.getRBDVolumeName(snapVol, "", false)) if err != nil { return nil } @@ -1806,7 +1806,7 @@ func (d *ceph) RestoreVolume(vol Volume, snapshotName string, op *operations.Ope "snap", "rollback", "--snap", fmt.Sprintf("snapshot_%s", snapshotName), - d.getRBDVolumeName(vol, "", false, false)) + d.getRBDVolumeName(vol, "", false)) if err != nil { return err } diff --git a/internal/server/storage/drivers/volume.go b/internal/server/storage/drivers/volume.go index f96ddbcd79..e1f60b3e5c 100644 --- a/internal/server/storage/drivers/volume.go +++ b/internal/server/storage/drivers/volume.go @@ -105,6 +105,7 @@ type Volume struct { mountCustomPath string // Mount the filesystem volume at a custom location. mountFilesystemProbe bool // Probe filesystem type when mounting volume (when needed). hasSource bool // Whether the volume is created from a source volume. + isDeleted bool // Whether we're dealing with a hidden volume (kept until all references are gone). } // NewVolume instantiates a new Volume struct.