diff --git a/pkg/daemon/ceph/client/erasure-code-profile.go b/pkg/daemon/ceph/client/erasure-code-profile.go index 16579af81..115e65c22 100644 --- a/pkg/daemon/ceph/client/erasure-code-profile.go +++ b/pkg/daemon/ceph/client/erasure-code-profile.go @@ -95,7 +95,9 @@ func CreateErasureCodeProfile(context *clusterd.Context, clusterInfo *ClusterInf args := []string{"osd", "erasure-code-profile", "set", profileName, "--force"} args = append(args, profilePairs...) - _, err = NewCephCommand(context, clusterInfo, args).Run() + cmd := NewCephCommand(context, clusterInfo, args) + cmd.JsonOutput = false + _, err = cmd.Run() if err != nil { return errors.Wrap(err, "failed to set ec-profile") } diff --git a/pkg/operator/ceph/object/objectstore.go b/pkg/operator/ceph/object/objectstore.go index 2c12fa48b..f404e3281 100644 --- a/pkg/operator/ceph/object/objectstore.go +++ b/pkg/operator/ceph/object/objectstore.go @@ -725,7 +725,9 @@ func DeletePools(ctx *Context, lastStore bool, poolPrefix string) error { return errors.Wrapf(err, "failed to list erasure code profiles for cluster %s", ctx.clusterInfo.Namespace) } // cleans up the EC profile for the data pool only. Metadata pools don't support EC (only replication is supported). - ecProfileName := cephclient.GetErasureCodeProfileForPool(ctx.Name) + // The profile name must match the full pool name used during creation (e.g., ".rgw.buckets.data_ecprofile"). + dataPool := poolName(poolPrefix, dataPoolName) + ecProfileName := cephclient.GetErasureCodeProfileForPool(dataPool) for i := range erasureCodes { if erasureCodes[i] == ecProfileName { if err := cephclient.DeleteErasureCodeProfile(ctx.Context, ctx.clusterInfo, ecProfileName); err != nil { diff --git a/pkg/operator/ceph/object/objectstore_test.go b/pkg/operator/ceph/object/objectstore_test.go index 3c3b97330..9d86b7f71 100644 --- a/pkg/operator/ceph/object/objectstore_test.go +++ b/pkg/operator/ceph/object/objectstore_test.go @@ -431,13 +431,13 @@ func deleteStore(t *testing.T, name string, existingStores string, expectedDelet } if args[1] == "erasure-code-profile" { if args[2] == "ls" { - return `["default","myobj_ecprofile"]`, nil + return `["default","myobj.rgw.buckets.data_ecprofile"]`, nil } if args[2] == "rm" { - if args[3] == "myobj_ecprofile" { + if args[3] == "myobj.rgw.buckets.data_ecprofile" { deletedErasureCodeProfile = true } else { - assert.Fail(t, fmt.Sprintf("the erasure code profile to be deleted should be myobj_ecprofile. Actual: %s ", args[3])) + assert.Fail(t, fmt.Sprintf("the erasure code profile to be deleted should be myobj.rgw.buckets.data_ecprofile. Actual: %s ", args[3])) } return "", nil } diff --git a/pkg/operator/ceph/pool/controller.go b/pkg/operator/ceph/pool/controller.go index 89320a4e7..31d4a5fbf 100644 --- a/pkg/operator/ceph/pool/controller.go +++ b/pkg/operator/ceph/pool/controller.go @@ -589,6 +589,16 @@ func deletePool(context *clusterd.Context, clusterInfo *cephclient.ClusterInfo, return errors.Wrapf(err, "failed to delete pool %q", p.Name) } } + + // Clean up the erasure code profile for EC pools so that recreating with + // different settings does not fail + if p.IsErasureCoded() { + ecProfileName := cephclient.GetErasureCodeProfileForPool(p.Name) + if err := cephclient.DeleteErasureCodeProfile(context, clusterInfo, ecProfileName); err != nil { + logger.Warningf("failed to delete erasure code profile %q for pool %q: %v", ecProfileName, p.Name, err) + } + } + return nil } diff --git a/pkg/operator/ceph/pool/controller_test.go b/pkg/operator/ceph/pool/controller_test.go index 1cb132945..b7b671327 100644 --- a/pkg/operator/ceph/pool/controller_test.go +++ b/pkg/operator/ceph/pool/controller_test.go @@ -200,6 +200,45 @@ func TestDeletePool(t *testing.T) { p = &cephv1.NamedPoolSpec{Name: "mypool"} err = deletePool(context, clusterInfo, p) assert.Error(t, err) + + // delete an erasure coded pool should also delete the EC profile + ecProfileDeleted := false + failOnDelete = false + ecExecutor := &exectest.MockExecutor{ + MockExecuteCommandWithOutput: func(command string, args ...string) (string, error) { + emptyPool := "{\"images\":{\"count\":0,\"provisioned_bytes\":0,\"snap_count\":0},\"trash\":{\"count\":1,\"provisioned_bytes\":2048,\"snap_count\":0}}" + if command == "ceph" && args[1] == "lspools" { + return `[{"poolnum":1,"poolname":"ecpool"}]`, nil + } else if command == "ceph" && args[1] == "pool" && args[2] == "get" { + return `{"pool": "ecpool","pool_id": 1,"size":1}`, nil + } else if command == "ceph" && args[1] == "pool" && args[2] == "delete" { + return "", nil + } else if command == "ceph" && args[1] == "erasure-code-profile" && args[2] == "rm" { + assert.Equal(t, "ecpool_ecprofile", args[3]) + ecProfileDeleted = true + return "", nil + } else if args[0] == "pool" { + if args[1] == "stats" { + return emptyPool, nil + } + return "", errors.Errorf("rbd: error opening pool %q: (2) No such file or directory", args[3]) + } + return "", errors.Errorf("unexpected rbd command %q", args) + }, + } + ecContext := &clusterd.Context{Executor: ecExecutor} + ecPool := &cephv1.NamedPoolSpec{ + Name: "ecpool", + PoolSpec: cephv1.PoolSpec{ + ErasureCoded: cephv1.ErasureCodedSpec{ + DataChunks: 2, + CodingChunks: 1, + }, + }, + } + err = deletePool(ecContext, clusterInfo, ecPool) + assert.NoError(t, err) + assert.True(t, ecProfileDeleted) } // TestCephBlockPoolController runs ReconcileCephBlockPool.Reconcile() against a