Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

pool: Allow configuration of built-in pools with non-k8s names #9363

Merged
merged 2 commits into from Dec 15, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 0 additions & 5 deletions .github/workflows/canary-integration-test.yml
Expand Up @@ -59,11 +59,6 @@ jobs:
kubectl -n rook-ceph cp deploy/examples/create-external-cluster-resources.py $toolbox:/etc/ceph
timeout 10 sh -c "until kubectl -n rook-ceph exec $toolbox -- python3 /etc/ceph/create-external-cluster-resources.py --rbd-data-pool-name replicapool; do echo 'waiting for script to succeed' && sleep 1; done"

- name: test external script for erasure coded rbd pool
run: |
toolbox=$(kubectl get pod -l app=rook-ceph-tools -n rook-ceph -o jsonpath='{.items[*].metadata.name}')
timeout 10 sh -c "until kubectl -n rook-ceph exec $toolbox -- python3 /etc/ceph/create-external-cluster-resources.py --rbd-data-pool-name=ec-pool --rbd-metadata-ec-pool-name=replicapool; do echo 'waiting for script to succeed' && sleep 1; done"

- name: run external script create-external-cluster-resources.py unit tests
run: |
kubectl -n rook-ceph exec $(kubectl get pod -l app=rook-ceph-tools -n rook-ceph -o jsonpath='{.items[0].metadata.name}') -- python3 -m unittest /etc/ceph/create-external-cluster-resources.py
Expand Down
5 changes: 5 additions & 0 deletions Documentation/ceph-pool-crd.md
Expand Up @@ -197,6 +197,11 @@ stretched) then you will have 2 replicas per datacenter where each replica ends
* `deviceClass`: Sets up the CRUSH rule for the pool to distribute data only on the specified device class. If left empty or unspecified, the pool will use the cluster's default CRUSH root, which usually distributes data over all OSDs, regardless of their class.
* `crushRoot`: The root in the crush map to be used by the pool. If left empty or unspecified, the default root will be used. Creating a crush hierarchy for the OSDs currently requires the Rook toolbox to run the Ceph tools described [here](http://docs.ceph.com/docs/master/rados/operations/crush-map/#modifying-the-crush-map).
* `enableRBDStats`: Enables collecting RBD per-image IO statistics by enabling dynamic OSD performance counters. Defaults to false. For more info see the [ceph documentation](https://docs.ceph.com/docs/master/mgr/prometheus/#rbd-io-statistics).
* `name`: The name of Ceph pools is based on the `metadata.name` of the CephBlockPool CR. Some built-in Ceph pools
require names that are incompatible with K8s resource names. These special pools can be configured
by setting this `name` to override the name of the Ceph pool that is created instead of using the `metadata.name` for the pool.
Two pool names are supported: `device_health_metrics` and `.nfs`. See the example
[device health metrics pool](https://github.com/rook/rook/blob/{{ branchName }}/deploy/examples/pool-device-health-metrics.yaml).

* `parameters`: Sets any [parameters](https://docs.ceph.com/docs/master/rados/operations/pools/#set-pool-values) listed to the given pool
* `target_size_ratio:` gives a hint (%) to Ceph in terms of expected consumption of the total cluster capacity of a given pool, for more info see the [ceph documentation](https://docs.ceph.com/docs/master/rados/operations/placement-groups/#specifying-expected-pool-size)
Expand Down
8 changes: 7 additions & 1 deletion deploy/charts/rook-ceph/templates/resources.yaml
Expand Up @@ -30,7 +30,7 @@ spec:
metadata:
type: object
spec:
description: PoolSpec represents the spec of ceph pool
description: NamedBlockPoolSpec allows a block pool to be created with a non-default name. This is more specific than the NamedPoolSpec so we get schema validation on the allowed pool names that can be specified.
properties:
compressionMode:
description: 'DEPRECATED: use Parameters instead, e.g., Parameters["compression_mode"] = "force" The inline compression mode in Bluestore OSD to set to (options are: none, passive, aggressive, force) Do NOT set a default value for kubebuilder as this will override the Parameters'
Expand Down Expand Up @@ -110,6 +110,12 @@ spec:
type: object
type: array
type: object
name:
description: The desired name of the pool if different from the CephBlockPool CR name.
enum:
- device_health_metrics
- .nfs
type: string
parameters:
additionalProperties:
type: string
Expand Down
12 changes: 12 additions & 0 deletions deploy/examples/cluster-test.yaml
Expand Up @@ -51,3 +51,15 @@ spec:
timeout: 600s
disruptionManagement:
managePodBudgets: true
---
apiVersion: ceph.rook.io/v1
kind: CephBlockPool
metadata:
name: device-health-metrics
namespace: rook-ceph # namespace:cluster
spec:
name: device_health_metrics
failureDomain: host
replicated:
size: 1
requireSafeReplicaSize: false
8 changes: 7 additions & 1 deletion deploy/examples/crds.yaml
Expand Up @@ -33,7 +33,7 @@ spec:
metadata:
type: object
spec:
description: PoolSpec represents the spec of ceph pool
description: NamedBlockPoolSpec allows a block pool to be created with a non-default name. This is more specific than the NamedPoolSpec so we get schema validation on the allowed pool names that can be specified.
properties:
compressionMode:
description: 'DEPRECATED: use Parameters instead, e.g., Parameters["compression_mode"] = "force" The inline compression mode in Bluestore OSD to set to (options are: none, passive, aggressive, force) Do NOT set a default value for kubebuilder as this will override the Parameters'
Expand Down Expand Up @@ -113,6 +113,12 @@ spec:
type: object
type: array
type: object
name:
description: The desired name of the pool if different from the CephBlockPool CR name.
enum:
- device_health_metrics
- .nfs
type: string
parameters:
additionalProperties:
type: string
Expand Down
12 changes: 12 additions & 0 deletions deploy/examples/nfs-test.yaml
Expand Up @@ -14,3 +14,15 @@ spec:
active: 1
# The logging levels: NIV_NULL | NIV_FATAL | NIV_MAJ | NIV_CRIT | NIV_WARN | NIV_EVENT | NIV_INFO | NIV_DEBUG | NIV_MID_DEBUG |NIV_FULL_DEBUG |NB_LOG_LEVEL
logLevel: NIV_INFO
---
apiVersion: ceph.rook.io/v1
kind: CephBlockPool
metadata:
name: builtin-nfs
namespace: rook-ceph # namespace:cluster
spec:
name: .nfs
failureDomain: host
replicated:
size: 1
requireSafeReplicaSize: false
20 changes: 20 additions & 0 deletions deploy/examples/nfs.yaml
@@ -1,3 +1,9 @@
#################################################################################################################
# Create a Ceph pool with settings for replication in production environments. A minimum of 3 OSDs on
# different hosts are required in this example.
# kubectl create -f nfs.yaml
#################################################################################################################

apiVersion: ceph.rook.io/v1
kind: CephNFS
metadata:
Expand Down Expand Up @@ -49,3 +55,17 @@ spec:
#priorityClassName:
# The logging levels: NIV_NULL | NIV_FATAL | NIV_MAJ | NIV_CRIT | NIV_WARN | NIV_EVENT | NIV_INFO | NIV_DEBUG | NIV_MID_DEBUG |NIV_FULL_DEBUG |NB_LOG_LEVEL
logLevel: NIV_INFO
---
apiVersion: ceph.rook.io/v1
kind: CephBlockPool
metadata:
name: builtin-nfs
namespace: rook-ceph # namespace:cluster
spec:
# The required pool name ".nfs" cannot be specified as a K8s resource name, thus we override
# the pool name created in Ceph with this name property
name: .nfs
failureDomain: host
replicated:
size: 3
requireSafeReplicaSize: true
21 changes: 21 additions & 0 deletions deploy/examples/pool-device-health-metrics.yaml
@@ -0,0 +1,21 @@
apiVersion: ceph.rook.io/v1
kind: CephBlockPool
metadata:
# If the built-in Ceph pool for health metrics needs to be configured with alternate
# settings, create this pool with any of the pool properties. Create this pool immediately
# with the cluster CR, or else some properties may not be applied when Ceph creates the
# pool by default.
name: device-health-metrics
travisn marked this conversation as resolved.
Show resolved Hide resolved
namespace: rook-ceph # namespace:cluster
spec:
# The required pool name with underscores cannot be specified as a K8s resource name, thus we override
# the pool name created in Ceph with this name property.
name: device_health_metrics
failureDomain: host
replicated:
size: 3
requireSafeReplicaSize: true
parameters:
compression_mode: none
mirroring:
enabled: false
13 changes: 10 additions & 3 deletions pkg/apis/ceph.rook.io/v1/pool.go
Expand Up @@ -53,14 +53,14 @@ func (p *ReplicatedSpec) IsTargetRatioEnabled() bool {
func (p *CephBlockPool) ValidateCreate() error {
logger.Infof("validate create cephblockpool %v", p)

err := validatePoolSpec(p.Spec)
err := validatePoolSpec(p.Spec.ToNamedPoolSpec())
if err != nil {
return err
}
return nil
}

func validatePoolSpec(ps PoolSpec) error {
func validatePoolSpec(ps NamedPoolSpec) error {
// Checks if either ErasureCoded or Replicated fields are set
if ps.ErasureCoded.CodingChunks <= 0 && ps.ErasureCoded.DataChunks <= 0 && ps.Replicated.TargetSizeRatio <= 0 && ps.Replicated.Size <= 0 {
return errors.New("invalid create: either of erasurecoded or replicated fields should be set")
Expand All @@ -86,10 +86,17 @@ func validatePoolSpec(ps PoolSpec) error {
return nil
}

func (p *NamedBlockPoolSpec) ToNamedPoolSpec() NamedPoolSpec {
return NamedPoolSpec{
Name: p.Name,
PoolSpec: p.PoolSpec,
}
}

func (p *CephBlockPool) ValidateUpdate(old runtime.Object) error {
logger.Info("validate update cephblockpool")
ocbp := old.(*CephBlockPool)
err := validatePoolSpec(p.Spec)
err := validatePoolSpec(p.Spec.ToNamedPoolSpec())
if err != nil {
return err
}
Expand Down
20 changes: 12 additions & 8 deletions pkg/apis/ceph.rook.io/v1/pool_test.go
Expand Up @@ -28,18 +28,20 @@ func TestValidatePoolSpec(t *testing.T) {
ObjectMeta: metav1.ObjectMeta{
Name: "ec-pool",
},
Spec: PoolSpec{
ErasureCoded: ErasureCodedSpec{
CodingChunks: 1,
DataChunks: 2,
Spec: NamedBlockPoolSpec{
PoolSpec: PoolSpec{
ErasureCoded: ErasureCodedSpec{
CodingChunks: 1,
DataChunks: 2,
},
},
},
}
err := validatePoolSpec(p.Spec)
err := validatePoolSpec(p.Spec.ToNamedPoolSpec())
assert.NoError(t, err)

p.Spec.ErasureCoded.DataChunks = 1
err = validatePoolSpec(p.Spec)
err = validatePoolSpec(p.Spec.ToNamedPoolSpec())
assert.Error(t, err)
}

Expand All @@ -48,8 +50,10 @@ func TestCephBlockPoolValidateUpdate(t *testing.T) {
ObjectMeta: metav1.ObjectMeta{
Name: "ec-pool",
},
Spec: PoolSpec{
Replicated: ReplicatedSpec{RequireSafeReplicaSize: true, Size: 3},
Spec: NamedBlockPoolSpec{
PoolSpec: PoolSpec{
Replicated: ReplicatedSpec{RequireSafeReplicaSize: true, Size: 3},
},
},
}
up := p.DeepCopy()
Expand Down
14 changes: 13 additions & 1 deletion pkg/apis/ceph.rook.io/v1/types.go
Expand Up @@ -565,7 +565,7 @@ type CrashCollectorSpec struct {
type CephBlockPool struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata"`
Spec PoolSpec `json:"spec"`
Spec NamedBlockPoolSpec `json:"spec"`
// +kubebuilder:pruning:PreserveUnknownFields
Status *CephBlockPoolStatus `json:"status,omitempty"`
}
Expand Down Expand Up @@ -639,6 +639,18 @@ type PoolSpec struct {
Quotas QuotaSpec `json:"quotas,omitempty"`
}

// NamedBlockPoolSpec allows a block pool to be created with a non-default name.
// This is more specific than the NamedPoolSpec so we get schema validation on the
// allowed pool names that can be specified.
type NamedBlockPoolSpec struct {
// The desired name of the pool if different from the CephBlockPool CR name.
// +kubebuilder:validation:Enum=device_health_metrics;.nfs
// +optional
Name string `json:"name,omitempty"`
// The core pool configuration
PoolSpec `json:",inline"`
}

// NamedPoolSpec represents the named ceph pool spec
type NamedPoolSpec struct {
// Name of the pool
Expand Down
17 changes: 17 additions & 0 deletions pkg/apis/ceph.rook.io/v1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions pkg/daemon/ceph/client/crush_rule_test.go
Expand Up @@ -32,27 +32,27 @@ func TestBuildStretchClusterCrushRule(t *testing.T) {
err := json.Unmarshal([]byte(testCrushMap), &crushMap)
assert.NoError(t, err)

pool := &cephv1.PoolSpec{
pool := cephv1.PoolSpec{
FailureDomain: "datacenter",
CrushRoot: cephv1.DefaultCRUSHRoot,
Replicated: cephv1.ReplicatedSpec{
ReplicasPerFailureDomain: 2,
},
}

rule := buildTwoStepCrushRule(crushMap, "stretched", *pool)
rule := buildTwoStepCrushRule(crushMap, "stretched", pool)
assert.Equal(t, 2, rule.ID)
}

func TestBuildCrushSteps(t *testing.T) {
pool := &cephv1.PoolSpec{
pool := cephv1.PoolSpec{
FailureDomain: "datacenter",
CrushRoot: cephv1.DefaultCRUSHRoot,
Replicated: cephv1.ReplicatedSpec{
ReplicasPerFailureDomain: 2,
},
}
steps := buildTwoStepCrushSteps(*pool)
steps := buildTwoStepCrushSteps(pool)
assert.Equal(t, 4, len(steps))
assert.Equal(t, cephv1.DefaultCRUSHRoot, steps[0].ItemName)
assert.Equal(t, "datacenter", steps[1].Type)
Expand Down
22 changes: 11 additions & 11 deletions pkg/daemon/ceph/client/mirror.go
Expand Up @@ -104,17 +104,17 @@ func CreateRBDMirrorBootstrapPeer(context *clusterd.Context, clusterInfo *Cluste
}

// enablePoolMirroring turns on mirroring on that pool by specifying the mirroring type
func enablePoolMirroring(context *clusterd.Context, clusterInfo *ClusterInfo, pool cephv1.PoolSpec, poolName string) error {
logger.Infof("enabling mirroring type %q for pool %q", pool.Mirroring.Mode, poolName)
func enablePoolMirroring(context *clusterd.Context, clusterInfo *ClusterInfo, pool cephv1.NamedPoolSpec) error {
logger.Infof("enabling mirroring type %q for pool %q", pool.Mirroring.Mode, pool.Name)

// Build command
args := []string{"mirror", "pool", "enable", poolName, pool.Mirroring.Mode}
args := []string{"mirror", "pool", "enable", pool.Name, pool.Mirroring.Mode}
cmd := NewRBDCommand(context, clusterInfo, args)

// Run command
output, err := cmd.Run()
if err != nil {
return errors.Wrapf(err, "failed to enable mirroring type %q for pool %q. %s", pool.Mirroring.Mode, poolName, output)
return errors.Wrapf(err, "failed to enable mirroring type %q for pool %q. %s", pool.Mirroring.Mode, pool.Name, output)
}

return nil
Expand Down Expand Up @@ -246,17 +246,17 @@ func removeSnapshotSchedule(context *clusterd.Context, clusterInfo *ClusterInfo,
return nil
}

func enableSnapshotSchedules(context *clusterd.Context, clusterInfo *ClusterInfo, poolSpec cephv1.PoolSpec, poolName string) error {
func enableSnapshotSchedules(context *clusterd.Context, clusterInfo *ClusterInfo, pool cephv1.NamedPoolSpec) error {
logger.Info("resetting current snapshot schedules")
// Reset any existing schedules
err := removeSnapshotSchedules(context, clusterInfo, poolSpec, poolName)
err := removeSnapshotSchedules(context, clusterInfo, pool)
if err != nil {
logger.Errorf("failed to remove snapshot schedules. %v", err)
}

// Enable all the snap schedules
for _, snapSchedule := range poolSpec.Mirroring.SnapshotSchedules {
err := enableSnapshotSchedule(context, clusterInfo, snapSchedule, poolName)
for _, snapSchedule := range pool.Mirroring.SnapshotSchedules {
err := enableSnapshotSchedule(context, clusterInfo, snapSchedule, pool.Name)
if err != nil {
return errors.Wrap(err, "failed to enable snapshot schedule")
}
Expand All @@ -266,16 +266,16 @@ func enableSnapshotSchedules(context *clusterd.Context, clusterInfo *ClusterInfo
}

// removeSnapshotSchedules removes all the existing snapshot schedules
func removeSnapshotSchedules(context *clusterd.Context, clusterInfo *ClusterInfo, poolSpec cephv1.PoolSpec, poolName string) error {
func removeSnapshotSchedules(context *clusterd.Context, clusterInfo *ClusterInfo, pool cephv1.NamedPoolSpec) error {
// Get the list of existing snapshot schedule
existingSnapshotSchedules, err := listSnapshotSchedules(context, clusterInfo, poolName)
existingSnapshotSchedules, err := listSnapshotSchedules(context, clusterInfo, pool.Name)
if err != nil {
return errors.Wrap(err, "failed to list snapshot schedule(s)")
}

// Remove each schedule
for _, existingSnapshotSchedule := range existingSnapshotSchedules {
err := removeSnapshotSchedule(context, clusterInfo, existingSnapshotSchedule, poolName)
err := removeSnapshotSchedule(context, clusterInfo, existingSnapshotSchedule, pool.Name)
if err != nil {
return errors.Wrapf(err, "failed to remove snapshot schedule %v", existingSnapshotSchedule)
}
Expand Down