Repository: rook/rook
Pull request #14951 (2024-11-01 13:51:34)

rgw: keep default-placement in zone config
Based on discussion in #14937. Contains following fixes for RGW target placements feature:
- `placement.default` flag was not marked as optional in CephObjectStore CRD
- `radosgw-admin` CLI always recreates `default-placement` in zone and zonegroup json when it is updated with --infile option. I've created corresponding issue in ceph bugtracker https://tracker.ceph.com/issues/68775. Now rook also always keeps `default-placement`. In case if user marked other placement as default, rook will duplicate this new default placement under `default-placement` name.
- i've observed new issue with `radosgw-admin zone/zonegroup set --infile` - it sorts json arrays alphabetically for pool placements and targets. To comply with `radosgw-admin` rook now also sorts placements array from CRD.

BlaineEXE: > In case if user marked other placement as default, rook will duplicate this new default placement under default-placement name.

I have concerns about this. If `default-placement` is pre-existing from a configuration that used shared pools but not pool placements, this will already be configured and have data stored.

When `default-placement` is changed, that existing data will I think be made inaccessible to the end user.

Is it possible to just not touch `default-placement` at all and still mark another placement as default, or is that not possible?
arttor: > Is it possible to just not touch `default-placement` at all and still mark another placement as default, or is that not possible?

Yes it is possible. So we will keep  `default-placement`  like it is if `sharedPools` are set and copy values from `default` placement otherwise. correct?
BlaineEXE: I'm curious why we need to copy values at all. What happens if we just never touch it unless the higher-level sharedPools are set?
arttor: > I'm curious why we need to copy values at all. What happens if we just never touch it unless the higher-level sharedPools are set?

I am actually worry only about a use-case where user marks one of the placements in CR as a `default` and not using `sharedPools`. I think it will be main configuration for someone setting poolPlacements from scratch. In this case `radosgw-admin` will add `default-placement` routing to probably non-existing pools like `<instance-name>.rgw.buckets.index".
BlaineEXE: > I am actually worry only about a use-case where user marks one of the placements in CR as a default and not using sharedPools. I think it will be main configuration for someone setting poolPlacements from scratch. In this case radosgw-admin will add default-placement routing to probably non-existing pools like <instance-name>.rgw.buckets.index.

That seems like a reasonable concern. That would result in having extra pools around also, which we don't want.

> Yes it is possible. So we will keep default-placement like it is if sharedPools are set and copy values from default placement otherwise. correct?

This seems like a reasonable way to continue for now. Let's continue with this, and we can adjust in the future if needed.

Patch:
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 974e9a53f..2e9922c5a 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -21,11 +21,11 @@ jobs:
     if: "!contains(github.event.pull_request.labels.*.name, 'skip-ci')"
     steps:
       - name: checkout
-        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+        uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
         with:
           fetch-depth: 0
 
-      - uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed # v5.1.0
+      - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2
         with:
           go-version: "1.22"
 
@@ -74,12 +74,12 @@ jobs:
         go-version: ["1.22", "1.23"]
     steps:
       - name: checkout
-        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+        uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
         with:
           fetch-depth: 0
 
       - name: setup golang ${{ matrix.go-version }}
-        uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed # v5.1.0
+        uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2
         with:
           go-version: ${{ matrix.go-version }}
 
diff --git a/.github/workflows/codegen.yml b/.github/workflows/codegen.yml
index b4f24a465..74fb05769 100644
--- a/.github/workflows/codegen.yml
+++ b/.github/workflows/codegen.yml
@@ -30,11 +30,11 @@ jobs:
     if: "!contains(github.event.pull_request.labels.*.name, 'skip-ci')"
     steps:
       - name: checkout
-        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+        uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
         with:
           fetch-depth: 0
 
-      - uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed # v5.1.0
+      - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2
         with:
           go-version: "1.22"
 
diff --git a/.github/workflows/codespell.yaml b/.github/workflows/codespell.yaml
index e5aaebd88..57ebcf8fd 100644
--- a/.github/workflows/codespell.yaml
+++ b/.github/workflows/codespell.yaml
@@ -24,7 +24,7 @@ jobs:
     name: codespell
     runs-on: ubuntu-22.04
     steps:
-      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+      - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
         with:
           fetch-depth: 0
       - name: codespell
@@ -53,7 +53,7 @@ jobs:
     name: misspell
     runs-on: ubuntu-22.04
     steps:
-      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+      - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
         with:
           fetch-depth: 0
       - name: misspell
diff --git a/.github/workflows/commitlint.yml b/.github/workflows/commitlint.yml
index 1f7cb2f09..e388f2485 100644
--- a/.github/workflows/commitlint.yml
+++ b/.github/workflows/commitlint.yml
@@ -28,7 +28,7 @@ jobs:
     env:
       GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
     steps:
-      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+      - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
         with:
           fetch-depth: 0
       - uses: wagoid/commitlint-github-action@3d28780bbf0365e29b144e272b2121204d5be5f3 # v6.1.2
diff --git a/.github/workflows/crds-gen.yml b/.github/workflows/crds-gen.yml
index 55c43dcb0..8916627bf 100644
--- a/.github/workflows/crds-gen.yml
+++ b/.github/workflows/crds-gen.yml
@@ -29,11 +29,11 @@ jobs:
     runs-on: ubuntu-22.04
     steps:
       - name: checkout
-        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+        uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
         with:
           fetch-depth: 0
 
-      - uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed # v5.1.0
+      - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2
         with:
           go-version: "1.22"
 
diff --git a/.github/workflows/daily-nightly-jobs.yml b/.github/workflows/daily-nightly-jobs.yml
index d211e03a1..a5bf2f89f 100644
--- a/.github/workflows/daily-nightly-jobs.yml
+++ b/.github/workflows/daily-nightly-jobs.yml
@@ -19,7 +19,7 @@ jobs:
 
     steps:
       - name: checkout
-        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+        uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
         with:
           fetch-depth: 0
 
@@ -29,7 +29,7 @@ jobs:
           use-tmate: ${{ secrets.USE_TMATE }}
 
       - name: setup golang
-        uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed # v5.1.0
+        uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2
         with:
           go-version: "1.22"
 
@@ -116,7 +116,7 @@ jobs:
     runs-on: ubuntu-22.04
     steps:
       - name: checkout
-        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+        uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
         with:
           fetch-depth: 0
 
@@ -156,7 +156,7 @@ jobs:
     runs-on: ubuntu-22.04
     steps:
       - name: checkout
-        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+        uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
         with:
           fetch-depth: 0
 
@@ -196,7 +196,7 @@ jobs:
     runs-on: ubuntu-22.04
     steps:
       - name: checkout
-        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+        uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
         with:
           fetch-depth: 0
 
@@ -236,7 +236,7 @@ jobs:
     runs-on: ubuntu-22.04
     steps:
       - name: checkout
-        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+        uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
         with:
           fetch-depth: 0
 
@@ -276,7 +276,7 @@ jobs:
     runs-on: ubuntu-22.04
     steps:
       - name: checkout
-        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+        uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
         with:
           fetch-depth: 0
 
@@ -317,7 +317,7 @@ jobs:
     runs-on: ubuntu-22.04
     steps:
       - name: checkout
-        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+        uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
         with:
           fetch-depth: 0
 
diff --git a/.github/workflows/docs-check.yml b/.github/workflows/docs-check.yml
index 41b25dfd4..265ffff83 100644
--- a/.github/workflows/docs-check.yml
+++ b/.github/workflows/docs-check.yml
@@ -24,15 +24,15 @@ jobs:
     name: docs-check
     runs-on: ubuntu-22.04
     steps:
-      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+      - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
         with:
           fetch-depth: 0
 
-      - uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed # v5.1.0
+      - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2
         with:
           go-version: "1.22"
 
-      - uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0
+      - uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 # v5.2.0
         with:
           python-version: 3.9
 
diff --git a/.github/workflows/encryption-pvc-kms-ibm-kp/action.yml b/.github/workflows/encryption-pvc-kms-ibm-kp/action.yml
index 8dd63ee86..4e9975eab 100644
--- a/.github/workflows/encryption-pvc-kms-ibm-kp/action.yml
+++ b/.github/workflows/encryption-pvc-kms-ibm-kp/action.yml
@@ -10,9 +10,6 @@ inputs:
   artifact-name:
     description: the name of the artifact where logs will be stored
     required: true
-  ceph-image:
-    description: the name of ceph image
-    required: true
 
 runs:
   using: "composite"
@@ -30,7 +27,7 @@ runs:
 
     - name: set Ceph version in CephCluster manifest
       shell: bash --noprofile --norc -eo pipefail -x {0}
-      run: tests/scripts/github-action-helper.sh replace_ceph_image  "deploy/examples/cluster-test.yaml" "${{ inputs.ceph-image }}"
+      run: tests/scripts/github-action-helper.sh replace_ceph_image  "deploy/examples/cluster-test.yaml" "${{ github.event.inputs.ceph-image }}"
 
     - name: use local disk and create partitions for osds
       shell: bash --noprofile --norc -eo pipefail -x {0}
diff --git a/.github/workflows/golangci-lint.yaml b/.github/workflows/golangci-lint.yaml
index 0d7198753..89df3980a 100644
--- a/.github/workflows/golangci-lint.yaml
+++ b/.github/workflows/golangci-lint.yaml
@@ -24,10 +24,10 @@ jobs:
     name: golangci-lint
     runs-on: ubuntu-latest
     steps:
-      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+      - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
         with:
           fetch-depth: 0
-      - uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed # v5.1.0
+      - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2
         with:
           go-version: "1.22"
       - name: golangci-lint
@@ -52,7 +52,7 @@ jobs:
     name: govulncheck
     runs-on: ubuntu-latest
     steps:
-      - uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed # v5.1.0
+      - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2
         with:
          go-version: "1.22.5"
          check-latest: true
diff --git a/.github/workflows/helm-lint.yaml b/.github/workflows/helm-lint.yaml
index b274a0103..8a57bf634 100644
--- a/.github/workflows/helm-lint.yaml
+++ b/.github/workflows/helm-lint.yaml
@@ -26,7 +26,7 @@ jobs:
     runs-on: ubuntu-latest
     steps:
       - name: Checkout
-        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+        uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
         with:
           fetch-depth: 0
 
@@ -35,7 +35,7 @@ jobs:
         with:
           version: v3.6.2
 
-      - uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0
+      - uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 # v5.2.0
         with:
           python-version: 3.9
 
diff --git a/.github/workflows/linters.yaml b/.github/workflows/linters.yaml
index 59d8dba4e..810eb2417 100644
--- a/.github/workflows/linters.yaml
+++ b/.github/workflows/linters.yaml
@@ -23,12 +23,12 @@ jobs:
   yaml-linter:
     runs-on: ubuntu-latest
     steps:
-      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+      - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
         with:
           fetch-depth: 0
 
       - name: Set up Python
-        uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0
+        uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 # v5.2.0
         with:
           python-version: 3.9
 
@@ -41,12 +41,12 @@ jobs:
   pylint:
     runs-on: ubuntu-latest
     steps:
-      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+      - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
         with:
           fetch-depth: 0
 
       - name: Set up Python
-        uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0
+        uses: actions/setup-python@f677139bbe7f9c59b41e40162b753c062f5d49a3 # v5.2.0
         with:
           python-version: 3.9
 
diff --git a/.github/workflows/mod-check.yml b/.github/workflows/mod-check.yml
index 5ff39fa02..108eac712 100644
--- a/.github/workflows/mod-check.yml
+++ b/.github/workflows/mod-check.yml
@@ -29,11 +29,11 @@ jobs:
     runs-on: ubuntu-22.04
     steps:
       - name: checkout
-        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+        uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
         with:
           fetch-depth: 0
 
-      - uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed # v5.1.0
+      - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2
         with:
           go-version: "1.22"
 
diff --git a/.github/workflows/multus.yaml b/.github/workflows/multus.yaml
index 0be92c9e8..21c172ebf 100644
--- a/.github/workflows/multus.yaml
+++ b/.github/workflows/multus.yaml
@@ -36,12 +36,12 @@ jobs:
       NUMBER_OF_COMPUTE_NODES: 5
     steps:
       - name: checkout
-        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+        uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
         with:
           fetch-depth: 0
 
       - name: Set up Go version
-        uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed # v5.1.0
+        uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2
         with:
           go-version: "1.22"
 
@@ -55,7 +55,6 @@ jobs:
         uses: ./.github/workflows/tmate_debug
         with:
           use-tmate: ${{ secrets.USE_TMATE }}
-          debug-ci: ${{ contains(github.event.pull_request.labels.*.name, 'debug-ci') }}
 
       - name: Setup multus
         run: ./tests/scripts/multus/setup-multus.sh
diff --git a/.github/workflows/push-build.yaml b/.github/workflows/push-build.yaml
index adae7dd72..8f5c08302 100644
--- a/.github/workflows/push-build.yaml
+++ b/.github/workflows/push-build.yaml
@@ -21,11 +21,11 @@ jobs:
     if: github.repository == 'rook/rook'
     steps:
       - name: checkout
-        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+        uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
         with:
           fetch-depth: 0
 
-      - uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed # v5.1.0
+      - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2
         with:
           go-version: "1.22"
 
diff --git a/.github/workflows/rbac-gen.yaml b/.github/workflows/rbac-gen.yaml
index b4887cb5f..be342bf72 100644
--- a/.github/workflows/rbac-gen.yaml
+++ b/.github/workflows/rbac-gen.yaml
@@ -29,11 +29,11 @@ jobs:
     runs-on: ubuntu-22.04
     steps:
       - name: checkout
-        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+        uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
         with:
           fetch-depth: 0
 
-      - uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed # v5.1.0
+      - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2
         with:
           go-version: "1.22"
 
diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml
index 0674e4318..3cebd2816 100644
--- a/.github/workflows/scorecards.yml
+++ b/.github/workflows/scorecards.yml
@@ -28,7 +28,7 @@ jobs:
 
     steps:
       - name: "Checkout code"
-        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+        uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
         with:
           persist-credentials: false
 
@@ -64,6 +64,6 @@ jobs:
       # Upload the results to GitHub's code scanning dashboard (optional).
       # Commenting out will disable upload of results to your repo's Code Scanning dashboard
       - name: "Upload to code-scanning"
-        uses: github/codeql-action/upload-sarif@662472033e021d55d94146f66f6058822b0b39fd # v3.27.0
+        uses: github/codeql-action/upload-sarif@f779452ac5af1c261dce0346a8f964149f49322b # v3.26.13
         with:
           sarif_file: results.sarif
diff --git a/.github/workflows/shellcheck.yaml b/.github/workflows/shellcheck.yaml
index bd8d999e8..4fdca1376 100644
--- a/.github/workflows/shellcheck.yaml
+++ b/.github/workflows/shellcheck.yaml
@@ -24,7 +24,7 @@ jobs:
     name: Shellcheck
     runs-on: ubuntu-latest
     steps:
-    - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+    - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
     - name: Run ShellCheck
       uses: ludeeus/action-shellcheck@00b27aa7cb85167568cb48a3838b75f4265f2bca # master
       with:
diff --git a/.github/workflows/snyk.yaml b/.github/workflows/snyk.yaml
index aee260d93..193e78e53 100644
--- a/.github/workflows/snyk.yaml
+++ b/.github/workflows/snyk.yaml
@@ -16,7 +16,7 @@ jobs:
     runs-on: ubuntu-latest
     steps:
       - name: checkout
-        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+        uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
         with:
           fetch-depth: 0
 
diff --git a/.github/workflows/tmate_debug/action.yml b/.github/workflows/tmate_debug/action.yml
index 93c2d5772..b6a32e5f5 100644
--- a/.github/workflows/tmate_debug/action.yml
+++ b/.github/workflows/tmate_debug/action.yml
@@ -4,15 +4,12 @@ inputs:
   use-tmate:
     description: "boolean for enabling TMATE"
     required: true
-  debug-ci:
-    description: "boolean for debug-ci label in PR"
-    requnred: false
 runs:
   using: "composite"
   steps:
     - name: consider debugging
       shell: bash --noprofile --norc -eo pipefail -x {0}
-      if: runner.debug || inputs.debug-ci == 'true'
+      if: runner.debug || contains(github.event.pull_request.labels.*.name, 'debug-ci')
       run: |
         # Enable tmate only in the Rook fork, where the USE_TMATE secret is set in the repo, or if the action is re-run
         if [ "$GITHUB_REPOSITORY_OWNER" = "rook" ] || [ -n "${{ inputs.use-tmate }}" ] || [ "$GITHUB_RUN_ATTEMPT" -gt 1 ]; then
diff --git a/Documentation/CRDs/Cluster/ceph-cluster-crd.md b/Documentation/CRDs/Cluster/ceph-cluster-crd.md
index fa449dd25..fce660364 100755
--- a/Documentation/CRDs/Cluster/ceph-cluster-crd.md
+++ b/Documentation/CRDs/Cluster/ceph-cluster-crd.md
@@ -33,7 +33,7 @@ Settings can be specified at the global level to apply to the cluster as a whole
         Using the general `v19` tag is not recommended in production because it may lead to inconsistent versions of the image running across different nodes in the cluster.
     * `allowUnsupported`: If `true`, allow an unsupported major version of the Ceph release. Currently Reef and Squid are supported. Future versions such as Tentacle (v20) would require this to be set to `true`. Should be set to `false` in production.
     * `imagePullPolicy`: The image pull policy for the ceph daemon pods. Possible values are `Always`, `IfNotPresent`, and `Never`. The default is `IfNotPresent`.
-* `dataDirHostPath`: The path on the host ([hostPath](https://kubernetes.io/docs/concepts/storage/volumes/#hostpath)) where config and data should be stored for each of the services. If there are multiple clusters, the directory must be unique for each cluster. If the directory does not exist, it will be created. Because this directory persists on the host, it will remain after pods are deleted. Following paths and any of their subpaths **must not be used**: `/etc/ceph`, `/rook` or `/var/log/ceph`.
+* `dataDirHostPath`: The path on the host ([hostPath](https://kubernetes.io/docs/concepts/storage/volumes/#hostpath)) where config and data should be stored for each of the services. If the directory does not exist, it will be created. Because this directory persists on the host, it will remain after pods are deleted. Following paths and any of their subpaths **must not be used**: `/etc/ceph`, `/rook` or `/var/log/ceph`.
     * **WARNING**: For test scenarios, if you delete a cluster and start a new cluster on the same hosts, the path used by `dataDirHostPath` must be deleted. Otherwise, stale keys and other config will remain from the previous cluster and the new mons will fail to start.
 If this value is empty, each pod will get an ephemeral directory to store their config files that is tied to the lifetime of the pod running on that node. More details can be found in the Kubernetes [empty dir docs](https://kubernetes.io/docs/concepts/storage/volumes/#emptydir).
 * `skipUpgradeChecks`: if set to true Rook won't perform any upgrade checks on Ceph daemons during an upgrade. Use this at **YOUR OWN RISK**, only if you know what you're doing. To understand Rook's upgrade process of Ceph, read the [upgrade doc](../../Upgrade/rook-upgrade.md#ceph-version-upgrades).
@@ -94,8 +94,6 @@ For more details on the mons and when to choose a number other than `3`, see the
         * For non-PVCs: `placement.all` and `placement.osd`
         * For PVCs: `placement.all` and inside the storageClassDeviceSets from the `placement` or `preparePlacement`
     * `flappingRestartIntervalHours`: Defines the time for which an OSD pod will sleep before restarting, if it stopped due to flapping. Flapping occurs where OSDs are marked `down` by Ceph more than 5 times in 600 seconds. The OSDs will stay down when flapping since they likely have a bad disk or other issue that needs investigation. If the issue with the OSD is fixed manually, the OSD pod can be manually restarted. The sleep is disabled if this interval is set to 0.
-    * `scheduleAlways`: Whether to always schedule OSD pods on nodes declared explicitly in the "nodes" section, even if they are
-        temporarily not schedulable. If set to true, consider adding placement tolerations for unschedulable nodes.
     * `fullRatio`: The ratio at which Ceph should block IO if the OSDs are too full. The default is 0.95.
     * `backfillFullRatio`: The ratio at which Ceph should stop backfilling data if the OSDs are too full. The default is 0.90.
     * `nearFullRatio`: The ratio at which Ceph should raise a health warning if the cluster is almost full. The default is 0.85.
@@ -487,7 +485,6 @@ You can set resource requests/limits for Rook components through the [Resource R
 It scrapes for Ceph daemon core dumps and sends them to the Ceph manager crash module so that core dumps are centralized and can be easily listed/accessed.
 You can read more about the [Ceph Crash module](https://docs.ceph.com/docs/master/mgr/crash/).
 * `logcollector`: Set resource requests/limits for the log collector. When enabled, this container runs as side-car to each Ceph daemons.
-* `cmd-reporter`: Set resource requests/limits for the jobs that detect the ceph version and collect network info.
 * `cleanup`: Set resource requests/limits for cleanup job, responsible for wiping cluster's data after uninstall
 * `exporter`: Set resource requests/limits for Ceph exporter.
 
diff --git a/Documentation/CRDs/Cluster/external-cluster/advance-external.md b/Documentation/CRDs/Cluster/external-cluster/advance-external.md
index 4a273fc49..5034b0da9 100644
--- a/Documentation/CRDs/Cluster/external-cluster/advance-external.md
+++ b/Documentation/CRDs/Cluster/external-cluster/advance-external.md
@@ -16,7 +16,7 @@ you can export the settings from this cluster with the following steps.
     kubectl -n rook-ceph cp deploy/examples/external/create-external-cluster-resources.py $toolbox:/etc/ceph
     ```
 
-2. Exec to the toolbox pod and execute create-external-cluster-resources.py with needed options to create required [users and keys](provider-export.md#1-create-all-users-and-keys).
+2. Exec to the toolbox pod and execute create-external-cluster-resources.py with needed options to create required [users and keys](/Documentation/CRDs/Cluster/external-cluster/provider-export.md#1-create-all-users-and-keys).
 
 !!! important
     For other clusters to connect to storage in this cluster, Rook must be configured with a networking configuration that is accessible from other clusters. Most commonly this is done by enabling host networking in the CephCluster CR so the Ceph daemons will be addressable by their host IPs.
diff --git a/Documentation/CRDs/Cluster/external-cluster/external-cluster.md b/Documentation/CRDs/Cluster/external-cluster/external-cluster.md
index c788c508c..68b965fce 100644
--- a/Documentation/CRDs/Cluster/external-cluster/external-cluster.md
+++ b/Documentation/CRDs/Cluster/external-cluster/external-cluster.md
@@ -21,22 +21,22 @@ Create the desired types of storage in the provider Ceph cluster:
 
 ## Connect the external Ceph Provider cluster to the Rook consumer cluster
 
-1) [Export config from the Provider Ceph cluster](provider-export.md). Configuration must be exported by the Ceph admin, such as a Ceph keyring and mon endpoints, that allows connection to the Ceph cluster.
+1) [Export config from the Provider Ceph cluster](/Documentation/CRDs/Cluster/external-cluster/provider-export.md). Configuration must be exported by the Ceph admin, such as a Ceph keyring and mon endpoints, that allows connection to the Ceph cluster.
 
-2) [Import config to the Rook consumer cluster](consumer-import.md). The configuration exported from the Ceph cluster is imported to Rook to provide the needed connection details.
+2) [Import config to the Rook consumer cluster](/Documentation/CRDs/Cluster/external-cluster/consumer-import.md). The configuration exported from the Ceph cluster is imported to Rook to provide the needed connection details.
 
 ## Advance Options
 
-* [NFS storage](advance-external.md#nfs-storage)
+* [NFS storage](/Documentation/CRDs/Cluster/external-cluster/advance-external.md#nfs-storage)
 
-* [Exporting Rook to another cluster](advance-external.md#exporting-rook-to-another-cluster)
+* [Exporting Rook to another cluster](/Documentation/CRDs/Cluster/external-cluster/advance-external.md#exporting-rook-to-another-cluster)
 
-* [Run consumer Rook cluster with Admin privileges](advance-external.md#admin-privileges)
+* [Run consumer Rook cluster with Admin privileges](/Documentation/CRDs/Cluster/external-cluster/advance-external.md#admin-privileges)
 
-* [Connect to an External Object Store](advance-external.md#connect-to-an-external-object-store)
+* [Connect to an External Object Store](/Documentation/CRDs/Cluster/external-cluster/advance-external.md#connect-to-an-external-object-store)
 
 ## Upgrades
 
-* [Upgrade external cluster](upgrade-external.md#upgrade-external-cluster)
+* [Upgrade external cluster](/Documentation/CRDs/Cluster/external-cluster/upgrade-external.md#upgrade-external-cluster)
 
-* [Utilize new features in upgrade](upgrade-external.md#upgrade-cluster-to-utilize-new-feature)
+* [Utilize new features in upgrade](/Documentation/CRDs/Cluster/external-cluster/upgrade-external.md#upgrade-cluster-to-utilize-new-feature)
diff --git a/Documentation/CRDs/Cluster/external-cluster/upgrade-external.md b/Documentation/CRDs/Cluster/external-cluster/upgrade-external.md
index eddcb8283..7ee3110ca 100644
--- a/Documentation/CRDs/Cluster/external-cluster/upgrade-external.md
+++ b/Documentation/CRDs/Cluster/external-cluster/upgrade-external.md
@@ -47,11 +47,11 @@ Argument that are decided using the Priority: command-line-args has more priorit
 
 3. Make any desired modifications and additions to `config.ini``
 
-4. Run the python script again using the [config file](provider-export.md#config-file)
+4. Run the python script again using the [config file](/Documentation/CRDs/Cluster/external-cluster/provider-export.md#config-file)
 
-5. [Copy the bash output](provider-export.md#2-copy-the-bash-output)
+5. [Copy the bash output](/Documentation/CRDs/Cluster/external-cluster/provider-export.md#2-copy-the-bash-output)
 
-6. [Import-the-provider-data](consumer-import.md#import-the-provider-data)
+6. [Import-the-provider-data](/Documentation/CRDs/Cluster/external-cluster/consumer-import.md#import-the-provider-data)
 
 !!! warning
     If the last-applied config is unavailable, run the current version of the script again using previously-applied config and CLI flags.
diff --git a/Documentation/CRDs/specification.md b/Documentation/CRDs/specification.md
index 89f570a27..df8166674 100644
--- a/Documentation/CRDs/specification.md
+++ b/Documentation/CRDs/specification.md
@@ -10965,7 +10965,9 @@ bool
 </em>
 </td>
 <td>
-<p>Sets given placement as default. Only one placement in the list can be marked as default.</p>
+<em>(Optional)</em>
+<p>Sets given placement as default. Only one placement in the list can be marked as default.
+Default is false.</p>
 </td>
 </tr>
 <tr>
@@ -12821,18 +12823,6 @@ bool
 </tr>
 <tr>
 <td>
-<code>scheduleAlways</code><br/>
-<em>
-bool
-</em>
-</td>
-<td>
-<em>(Optional)</em>
-<p>Whether to always schedule OSDs on a node even if the node is not currently scheduleable or ready</p>
-</td>
-</tr>
-<tr>
-<td>
 <code>onlyApplyOSDPlacement</code><br/>
 <em>
 bool
diff --git a/Documentation/Helm-Charts/operator-chart.md b/Documentation/Helm-Charts/operator-chart.md
index 7fc7ec887..63ec4f542 100644
--- a/Documentation/Helm-Charts/operator-chart.md
+++ b/Documentation/Helm-Charts/operator-chart.md
@@ -52,6 +52,7 @@ The following table lists the configurable parameters of the rook-operator chart
 | `cephCommandsTimeoutSeconds` | The timeout for ceph commands in seconds | `"15"` |
 | `containerSecurityContext` | Set the container security context for the operator | `{"capabilities":{"drop":["ALL"]},"runAsGroup":2016,"runAsNonRoot":true,"runAsUser":2016}` |
 | `crds.enabled` | Whether the helm chart should create and update the CRDs. If false, the CRDs must be managed independently with deploy/examples/crds.yaml. **WARNING** Only set during first deployment. If later disabled the cluster may be DESTROYED. If the CRDs are deleted in this case, see [the disaster recovery guide](https://rook.io/docs/rook/latest/Troubleshooting/disaster-recovery/#restoring-crds-after-deletion) to restore them. | `true` |
+| `csi.allowUnsupportedVersion` | Allow starting an unsupported ceph-csi image | `false` |
 | `csi.attacher.repository` | Kubernetes CSI Attacher image repository | `"registry.k8s.io/sig-storage/csi-attacher"` |
 | `csi.attacher.tag` | Attacher image tag | `"v4.6.1"` |
 | `csi.cephFSAttachRequired` | Whether to skip any attach operation altogether for CephFS PVCs. See more details [here](https://kubernetes-csi.github.io/docs/skip-attach.html#skip-attach-with-csi-driver-object). If cephFSAttachRequired is set to false it skips the volume attachments and makes the creation of pods using the CephFS PVC fast. **WARNING** It's highly discouraged to use this for CephFS RWO volumes. Refer to this [issue](https://github.com/kubernetes/kubernetes/issues/103305) for more details. | `true` |
diff --git a/Documentation/Storage-Configuration/ceph-teardown.md b/Documentation/Storage-Configuration/ceph-teardown.md
index 702417841..6196854ed 100644
--- a/Documentation/Storage-Configuration/ceph-teardown.md
+++ b/Documentation/Storage-Configuration/ceph-teardown.md
@@ -12,7 +12,7 @@ Rook provides the following clean up options:
 To tear down the cluster, the following resources need to be cleaned up:
 
 * The resources created under Rook's namespace (default `rook-ceph`) such as the Rook operator created by `operator.yaml` and the cluster CR `cluster.yaml`.
-* All files under `dataDirHostPath` (default `/var/lib/rook`): Path on each host in the cluster where configuration is stored by ceph daemons.
+* `/var/lib/rook/rook-ceph`: Path on each host in the cluster where configuration is stored by the ceph mons and osds
 * Devices used by the OSDs
 
 If the default namespaces or paths such as `dataDirHostPath` are changed in the example yaml files, these namespaces and paths will need to be changed throughout these instructions.
@@ -68,7 +68,7 @@ kubectl delete storageclass csi-cephfs
 
     These jobs will perform the following operations:
 
-    * Delete the all files under `dataDirHostPath` on all the nodes
+    * Delete the namespace directory under `dataDirHostPath`, for example `/var/lib/rook/rook-ceph`, on all the nodes
     * Wipe the data on the drives on all the nodes where OSDs were running in this cluster
 
 !!! note
@@ -92,7 +92,7 @@ kubectl delete -f crds.yaml
 
 If the `cleanupPolicy` was not added to the CephCluster CR before deleting the cluster, these manual steps are required to tear down the cluster.
 
-Connect to each machine and delete all files under `dataDirHostPath`.
+Connect to each machine and delete the namespace directory under `dataDirHostPath`, for example `/var/lib/rook/rook-ceph`.
 
 #### Zapping Devices
 
diff --git a/Makefile b/Makefile
index 413f33f61..6fef6948b 100644
--- a/Makefile
+++ b/Makefile
@@ -161,7 +161,6 @@ fmt: ## Check formatting of go sources.
 	@$(MAKE) go.init
 	@$(MAKE) go.fmt
 
-gen.codegen: codegen
 codegen: ${CODE_GENERATOR} ## Run code generators.
 	@build/codegen/codegen.sh
 
@@ -179,21 +178,19 @@ distclean: clean ## Remove all files that are created by building or configuring
 prune: ## Prune cached artifacts.
 	@$(MAKE) -C images prune
 
-gen.crds: crds
+docs: helm-docs
+
 crds: $(CONTROLLER_GEN) $(YQ)
 	@echo Updating CRD manifests
 	@build/crds/build-crds.sh $(CONTROLLER_GEN) $(YQ)
 	@GOBIN=$(GOBIN) build/crds/generate-crd-docs.sh
 
-gen.rbac: gen-rbac
 gen-rbac: $(HELM) $(YQ) ## Generate RBAC from Helm charts
 	@# output only stdout to the file; stderr for debugging should keep going to stderr
 	HELM=$(HELM) ./build/rbac/gen-common.sh
 	HELM=$(HELM) ./build/rbac/gen-nfs-rbac.sh
 	HELM=$(HELM) ./build/rbac/gen-psp.sh
 
-gen.docs: docs
-docs: helm-docs
 helm-docs: $(HELM_DOCS) ## Use helm-docs to generate documentation from helm charts
 	$(HELM_DOCS) -c deploy/charts/rook-ceph \
 		-o ../../../Documentation/Helm-Charts/operator-chart.md \
@@ -221,15 +218,11 @@ docs-preview: ## Preview the documentation through mkdocs
 docs-build:  ## Build the documentation to the `site/` directory
 	mkdocs build --strict
 
-gen.crd-docs: generate-docs-crds
 generate-docs-crds: ## Build the documentation for CRD
 	@GOBIN=$(GOBIN) build/crds/generate-crd-docs.sh
 
-generate: gen.codegen gen.crds gen.rbac gen.docs gen.crd-docs ## Update all generated files (code, manifests, charts, and docs).
-
-
 .PHONY: all build.common
-.PHONY: build build.all install test check vet fmt codegen gen.codegen gen.rbac gen.crds gen.crd-docs gen.docs generate mod.check clean distclean prune
+.PHONY: build build.all install test check vet fmt codegen mod.check clean distclean prune
 
 # ====================================================================================
 # Help
diff --git a/ROADMAP.md b/ROADMAP.md
index ec480f678..c365a2aac 100644
--- a/ROADMAP.md
+++ b/ROADMAP.md
@@ -8,21 +8,20 @@ We hope that the items listed below will inspire further engagement from the com
 Any dates listed below and the specific issues that will ship in a given milestone are subject to change but should give a general idea of what we are planning.
 See the [GitHub project boards](https://github.com/rook/rook/projects) for the most up-to-date issues and their status.
 
-## Rook Ceph 1.16
+## Rook Ceph 1.15
 
-The following high level features are targeted for Rook v1.16 (December 2024). For more detailed project tracking see the [v1.16 board](https://github.com/orgs/rook/projects/6).
+The following high level features are targeted for Rook v1.15 (July 2024). For more detailed project tracking see the [v1.15 board](https://github.com/rook/rook/projects/32).
 
-* Removed support for Ceph Quincy since at end of life [#14795](https://github.com/rook/rook/pull/14795)
-* Enable mirroring for RADOS namespaces [#14701](https://github.com/rook/rook/pull/14701)
 * Replace a single OSD when a metadataDevice is configured with multiple OSDs [#13240](https://github.com/rook/rook/issues/13240)
-* Remove multus-enabled "holder" pods [#14289](https://github.com/rook/rook/issues/14289)
-* OSD migration to enable encryption as a day 2 operation [#14719](https://github.com/rook/rook/pull/14719)
+* Multus-enabled clusters will potentially remove "holder" pods [#14289](https://github.com/rook/rook/issues/14289)
 * Key rotation for Ceph object store users [#11563](https://github.com/rook/rook/issues/11563)
 * CSI Driver
-  * Continue integration of the new Ceph-CSI operator, targeted for stable in v1.17 [#14766](https://github.com/rook/rook/pull/14766)
-  * Ceph-CSI [v3.13](https://github.com/ceph/ceph-csi/issues?q=is%3Aopen+is%3Aissue+milestone%3Arelease-v3.13.0)
+  * Integrate the new Ceph-CSI operator [#14260](https://github.com/rook/rook/issues/14260)
+  * Ceph-CSI [v3.12](https://github.com/ceph/ceph-csi/issues?q=is%3Aopen+is%3Aissue+milestone%3Arelease-v3.12.0)
+  * Support log rotation for the Ceph-CSI pods [#12809](https://github.com/rook/rook/issues/12809)
 
 ## Kubectl Plugin
 
 Features are planned for the [Kubectl Plugin](https://github.com/rook/kubectl-rook-ceph), though without a committed timeline.
 * Collect details to help troubleshoot the csi driver [#69](https://github.com/rook/kubectl-rook-ceph/issues/69)
+* Support `radosgw-admin` commands from the plugin [#253](https://github.com/rook/kubectl-rook-ceph/issues/253)
diff --git a/deploy/charts/rook-ceph-cluster/values.yaml b/deploy/charts/rook-ceph-cluster/values.yaml
index 47297069d..7de9fdebb 100644
--- a/deploy/charts/rook-ceph-cluster/values.yaml
+++ b/deploy/charts/rook-ceph-cluster/values.yaml
@@ -100,7 +100,7 @@ cephClusterSpec:
     # Do not set to true in production.
     allowUnsupported: false
 
-  # The path on the host where configuration files will be persisted. Must be specified. If there are multiple clusters, the directory must be unique for each cluster.
+  # The path on the host where configuration files will be persisted. Must be specified.
   # Important: if you reinstall the cluster, make sure you delete this directory from each host or else the mons will fail to start on the new cluster.
   # In Minikube, the '/data' directory is configured to persist across reboots. Use "/data/rook" in Minikube environment.
   dataDirHostPath: /var/lib/rook
diff --git a/deploy/charts/rook-ceph/templates/configmap.yaml b/deploy/charts/rook-ceph/templates/configmap.yaml
index 1acc2c5e8..98b99741d 100644
--- a/deploy/charts/rook-ceph/templates/configmap.yaml
+++ b/deploy/charts/rook-ceph/templates/configmap.yaml
@@ -169,6 +169,9 @@ data:
 {{- if .Values.csi.nfsProvisionerNodeAffinity }}
   CSI_NFS_PROVISIONER_NODE_AFFINITY: {{ .Values.csi.nfsProvisionerNodeAffinity }}
 {{- end }}
+{{- if .Values.csi.allowUnsupportedVersion }}
+  ROOK_CSI_ALLOW_UNSUPPORTED_VERSION: {{ .Values.csi.allowUnsupportedVersion | quote }}
+{{- end }}
 {{- if .Values.csi.pluginTolerations }}
   CSI_PLUGIN_TOLERATIONS: {{ toYaml .Values.csi.pluginTolerations | quote }}
 {{- end }}
diff --git a/deploy/charts/rook-ceph/templates/resources.yaml b/deploy/charts/rook-ceph/templates/resources.yaml
index 9f0e70332..f4d0dc5d8 100644
--- a/deploy/charts/rook-ceph/templates/resources.yaml
+++ b/deploy/charts/rook-ceph/templates/resources.yaml
@@ -3645,9 +3645,6 @@ spec:
                       type: array
                     onlyApplyOSDPlacement:
                       type: boolean
-                    scheduleAlways:
-                      description: Whether to always schedule OSDs on a node even if the node is not currently scheduleable or ready
-                      type: boolean
                     storageClassDeviceSets:
                       items:
                         description: StorageClassDeviceSet is a storage class device set
@@ -12387,7 +12384,9 @@ spec:
                             minLength: 1
                             type: string
                           default:
-                            description: Sets given placement as default. Only one placement in the list can be marked as default.
+                            description: |-
+                              Sets given placement as default. Only one placement in the list can be marked as default.
+                              Default is false.
                             type: boolean
                           metadataPoolName:
                             description: The metadata pool used to store ObjectStore bucket index.
@@ -12426,7 +12425,6 @@ spec:
                             type: array
                         required:
                           - dataPoolName
-                          - default
                           - metadataPoolName
                           - name
                         type: object
@@ -13313,7 +13311,9 @@ spec:
                             minLength: 1
                             type: string
                           default:
-                            description: Sets given placement as default. Only one placement in the list can be marked as default.
+                            description: |-
+                              Sets given placement as default. Only one placement in the list can be marked as default.
+                              Default is false.
                             type: boolean
                           metadataPoolName:
                             description: The metadata pool used to store ObjectStore bucket index.
@@ -13352,7 +13352,6 @@ spec:
                             type: array
                         required:
                           - dataPoolName
-                          - default
                           - metadataPoolName
                           - name
                         type: object
diff --git a/deploy/charts/rook-ceph/values.yaml b/deploy/charts/rook-ceph/values.yaml
index 8b5ceb82f..5b48fc868 100644
--- a/deploy/charts/rook-ceph/values.yaml
+++ b/deploy/charts/rook-ceph/values.yaml
@@ -179,6 +179,9 @@ csi:
   # -- Set GRPC timeout for csi containers (in seconds). It should be >= 120. If this value is not set or is invalid, it defaults to 150
   grpcTimeoutInSeconds: 150
 
+  # -- Allow starting an unsupported ceph-csi image
+  allowUnsupportedVersion: false
+
   # -- Burst to use while communicating with the kubernetes apiserver.
   kubeApiBurst:
 
diff --git a/deploy/examples/cluster-external-management.yaml b/deploy/examples/cluster-external-management.yaml
index f1ef32560..156e968df 100644
--- a/deploy/examples/cluster-external-management.yaml
+++ b/deploy/examples/cluster-external-management.yaml
@@ -16,7 +16,6 @@ metadata:
 spec:
   external:
     enable: true
-  # If there are multiple clusters, the directory must be unique for each cluster.
   dataDirHostPath: /var/lib/rook
   # providing an image is required, if you want to create other CRs (rgw, mds, nfs)
   cephVersion:
diff --git a/deploy/examples/cluster-on-local-pvc.yaml b/deploy/examples/cluster-on-local-pvc.yaml
index c93b52af8..0d92793f4 100644
--- a/deploy/examples/cluster-on-local-pvc.yaml
+++ b/deploy/examples/cluster-on-local-pvc.yaml
@@ -162,7 +162,6 @@ metadata:
   name: rook-ceph
   namespace: rook-ceph # namespace:cluster
 spec:
-  # If there are multiple clusters, the directory must be unique for each cluster.
   dataDirHostPath: /var/lib/rook
   mon:
     count: 3
diff --git a/deploy/examples/cluster-on-pvc-minikube.yaml b/deploy/examples/cluster-on-pvc-minikube.yaml
index 00f56a726..3900fc3ef 100644
--- a/deploy/examples/cluster-on-pvc-minikube.yaml
+++ b/deploy/examples/cluster-on-pvc-minikube.yaml
@@ -128,7 +128,6 @@ metadata:
   name: my-cluster
   namespace: rook-ceph # namespace:cluster
 spec:
-  # If there are multiple clusters, the directory must be unique for each cluster.
   dataDirHostPath: /var/lib/rook
   mon:
     count: 1
diff --git a/deploy/examples/cluster-on-pvc.yaml b/deploy/examples/cluster-on-pvc.yaml
index 398de65cf..01017800c 100644
--- a/deploy/examples/cluster-on-pvc.yaml
+++ b/deploy/examples/cluster-on-pvc.yaml
@@ -12,7 +12,6 @@ metadata:
   name: rook-ceph
   namespace: rook-ceph # namespace:cluster
 spec:
-  # If there are multiple clusters, the directory must be unique for each cluster.
   dataDirHostPath: /var/lib/rook
   mon:
     # Set the number of mons to be started. Generally recommended to be 3.
diff --git a/deploy/examples/cluster-stretched-aws.yaml b/deploy/examples/cluster-stretched-aws.yaml
index 48796dfe2..c286e7dcc 100644
--- a/deploy/examples/cluster-stretched-aws.yaml
+++ b/deploy/examples/cluster-stretched-aws.yaml
@@ -14,7 +14,6 @@ metadata:
   name: rook-ceph
   namespace: rook-ceph # namespace:cluster
 spec:
-  # If there are multiple clusters, the directory must be unique for each cluster.
   dataDirHostPath: /var/lib/rook
   mon:
     # Five mons must be created for stretch mode
diff --git a/deploy/examples/cluster-stretched.yaml b/deploy/examples/cluster-stretched.yaml
index 5d595f3ca..7f582754a 100644
--- a/deploy/examples/cluster-stretched.yaml
+++ b/deploy/examples/cluster-stretched.yaml
@@ -14,7 +14,6 @@ metadata:
   name: rook-ceph
   namespace: rook-ceph # namespace:cluster
 spec:
-  # If there are multiple clusters, the directory must be unique for each cluster.
   dataDirHostPath: /var/lib/rook
   mon:
     # Five mons must be created for stretch mode
diff --git a/deploy/examples/cluster.yaml b/deploy/examples/cluster.yaml
index 70b406875..345897fb5 100644
--- a/deploy/examples/cluster.yaml
+++ b/deploy/examples/cluster.yaml
@@ -26,7 +26,7 @@ spec:
     # Future versions such as Tentacle (v20) would require this to be set to `true`.
     # Do not set to true in production.
     allowUnsupported: false
-  # The path on the host where configuration files will be persisted. Must be specified. If there are multiple clusters, the directory must be unique for each cluster.
+  # The path on the host where configuration files will be persisted. Must be specified.
   # Important: if you reinstall the cluster, make sure you delete this directory from each host or else the mons will fail to start on the new cluster.
   # In Minikube, the '/data' directory is configured to persist across reboots. Use "/data/rook" in Minikube environment.
   dataDirHostPath: /var/lib/rook
@@ -251,7 +251,6 @@ spec:
   #   logcollector:
   #   cleanup:
   #   exporter:
-  #   cmd-reporter:
   # The option to automatically remove OSDs that are out and are safe to destroy.
   removeOSDsIfOutAndSafeToRemove: false
   priorityClassNames:
@@ -286,9 +285,6 @@ spec:
     #     config: # configuration can be specified at the node level which overrides the cluster level config
     #   - name: "172.17.4.301"
     #     deviceFilter: "^sd."
-    # Whether to always schedule OSD pods on nodes declared explicitly in the "nodes" section, even if they are
-    # temporarily not schedulable. If set to true, consider adding placement tolerations for unschedulable nodes.
-    scheduleAlways: false
     # when onlyApplyOSDPlacement is false, will merge both placement.All() and placement.osd
     onlyApplyOSDPlacement: false
     # Time for which an OSD pod will sleep before restarting, if it stopped due to flapping
diff --git a/deploy/examples/crds.yaml b/deploy/examples/crds.yaml
index 6cbfc7321..db2c73513 100644
--- a/deploy/examples/crds.yaml
+++ b/deploy/examples/crds.yaml
@@ -3643,9 +3643,6 @@ spec:
                       type: array
                     onlyApplyOSDPlacement:
                       type: boolean
-                    scheduleAlways:
-                      description: Whether to always schedule OSDs on a node even if the node is not currently scheduleable or ready
-                      type: boolean
                     storageClassDeviceSets:
                       items:
                         description: StorageClassDeviceSet is a storage class device set
@@ -12378,7 +12375,9 @@ spec:
                             minLength: 1
                             type: string
                           default:
-                            description: Sets given placement as default. Only one placement in the list can be marked as default.
+                            description: |-
+                              Sets given placement as default. Only one placement in the list can be marked as default.
+                              Default is false.
                             type: boolean
                           metadataPoolName:
                             description: The metadata pool used to store ObjectStore bucket index.
@@ -12417,7 +12416,6 @@ spec:
                             type: array
                         required:
                           - dataPoolName
-                          - default
                           - metadataPoolName
                           - name
                         type: object
@@ -13301,7 +13299,9 @@ spec:
                             minLength: 1
                             type: string
                           default:
-                            description: Sets given placement as default. Only one placement in the list can be marked as default.
+                            description: |-
+                              Sets given placement as default. Only one placement in the list can be marked as default.
+                              Default is false.
                             type: boolean
                           metadataPoolName:
                             description: The metadata pool used to store ObjectStore bucket index.
@@ -13340,7 +13340,6 @@ spec:
                             type: array
                         required:
                           - dataPoolName
-                          - default
                           - metadataPoolName
                           - name
                         type: object
diff --git a/deploy/examples/operator-openshift.yaml b/deploy/examples/operator-openshift.yaml
index 1186fd184..239b75b16 100644
--- a/deploy/examples/operator-openshift.yaml
+++ b/deploy/examples/operator-openshift.yaml
@@ -183,6 +183,9 @@ data:
   # supported values are documented at https://kubernetes-csi.github.io/docs/support-fsgroup.html
   CSI_NFS_FSGROUPPOLICY: "File"
 
+  # (Optional) Allow starting unsupported ceph-csi image
+  ROOK_CSI_ALLOW_UNSUPPORTED_VERSION: "false"
+
   # (Optional) control the host mount of /etc/selinux for csi plugin pods.
   CSI_PLUGIN_ENABLE_SELINUX_HOST_MOUNT: "false"
 
diff --git a/deploy/examples/operator.yaml b/deploy/examples/operator.yaml
index b11549be5..1a90a5193 100644
--- a/deploy/examples/operator.yaml
+++ b/deploy/examples/operator.yaml
@@ -113,6 +113,9 @@ data:
   # supported values are documented at https://kubernetes-csi.github.io/docs/support-fsgroup.html
   CSI_NFS_FSGROUPPOLICY: "File"
 
+  # (Optional) Allow starting unsupported ceph-csi image
+  ROOK_CSI_ALLOW_UNSUPPORTED_VERSION: "false"
+
   # (Optional) control the host mount of /etc/selinux for csi plugin pods.
   CSI_PLUGIN_ENABLE_SELINUX_HOST_MOUNT: "false"
 
diff --git a/go.mod b/go.mod
index 2eeb5de12..db922fc34 100644
--- a/go.mod
+++ b/go.mod
@@ -45,14 +45,14 @@ require (
 	golang.org/x/sync v0.8.0
 	gopkg.in/ini.v1 v1.67.0
 	gopkg.in/yaml.v2 v2.4.0
-	k8s.io/api v0.31.2
-	k8s.io/apiextensions-apiserver v0.31.2
-	k8s.io/apimachinery v0.31.2
-	k8s.io/cli-runtime v0.31.2
-	k8s.io/client-go v0.31.2
-	k8s.io/cloud-provider v0.31.2
+	k8s.io/api v0.31.1
+	k8s.io/apiextensions-apiserver v0.31.1
+	k8s.io/apimachinery v0.31.1
+	k8s.io/cli-runtime v0.31.1
+	k8s.io/client-go v0.31.1
+	k8s.io/cloud-provider v0.31.1
 	k8s.io/utils v0.0.0-20240711033017-18e509b52bc8
-	sigs.k8s.io/controller-runtime v0.19.1
+	sigs.k8s.io/controller-runtime v0.19.0
 	sigs.k8s.io/mcs-api v0.1.0
 	sigs.k8s.io/yaml v1.4.0
 )
diff --git a/go.sum b/go.sum
index 5a29b130a..a23d2eeb7 100644
--- a/go.sum
+++ b/go.sum
@@ -1605,15 +1605,15 @@ k8s.io/api v0.20.1/go.mod h1:KqwcCVogGxQY3nBlRpwt+wpAMF/KjaCc7RpywacvqUo=
 k8s.io/api v0.20.4/go.mod h1:++lNL1AJMkDymriNniQsWRkMDzRaX2Y/POTUi8yvqYQ=
 k8s.io/api v0.23.5/go.mod h1:Na4XuKng8PXJ2JsploYYrivXrINeTaycCGcYgF91Xm8=
 k8s.io/api v0.26.0/go.mod h1:k6HDTaIFC8yn1i6pSClSqIwLABIcLV9l5Q4EcngKnQg=
-k8s.io/api v0.31.2 h1:3wLBbL5Uom/8Zy98GRPXpJ254nEFpl+hwndmk9RwmL0=
-k8s.io/api v0.31.2/go.mod h1:bWmGvrGPssSK1ljmLzd3pwCQ9MgoTsRCuK35u6SygUk=
+k8s.io/api v0.31.1 h1:Xe1hX/fPW3PXYYv8BlozYqw63ytA92snr96zMW9gWTU=
+k8s.io/api v0.31.1/go.mod h1:sbN1g6eY6XVLeqNsZGLnI5FwVseTrZX7Fv3O26rhAaI=
 k8s.io/apiextensions-apiserver v0.0.0-20190409022649-727a075fdec8/go.mod h1:IxkesAMoaCRoLrPJdZNZUQp9NfZnzqaVzLhb2VEQzXE=
 k8s.io/apiextensions-apiserver v0.18.2/go.mod h1:q3faSnRGmYimiocj6cHQ1I3WpLqmDgJFlKL37fC4ZvY=
 k8s.io/apiextensions-apiserver v0.18.3/go.mod h1:TMsNGs7DYpMXd+8MOCX8KzPOCx8fnZMoIGB24m03+JE=
 k8s.io/apiextensions-apiserver v0.18.4/go.mod h1:NYeyeYq4SIpFlPxSAB6jHPIdvu3hL0pc36wuRChybio=
 k8s.io/apiextensions-apiserver v0.20.1/go.mod h1:ntnrZV+6a3dB504qwC5PN/Yg9PBiDNt1EVqbW2kORVk=
-k8s.io/apiextensions-apiserver v0.31.2 h1:W8EwUb8+WXBLu56ser5IudT2cOho0gAKeTOnywBLxd0=
-k8s.io/apiextensions-apiserver v0.31.2/go.mod h1:i+Geh+nGCJEGiCGR3MlBDkS7koHIIKWVfWeRFiOsUcM=
+k8s.io/apiextensions-apiserver v0.31.1 h1:L+hwULvXx+nvTYX/MKM3kKMZyei+UiSXQWciX/N6E40=
+k8s.io/apiextensions-apiserver v0.31.1/go.mod h1:tWMPR3sgW+jsl2xm9v7lAyRF1rYEK71i9G5dRtkknoQ=
 k8s.io/apimachinery v0.0.0-20190404173353-6a84e37a896d/go.mod h1:ccL7Eh7zubPUSh9A3USN90/OzHNSVN6zxzde07TDCL0=
 k8s.io/apimachinery v0.18.2/go.mod h1:9SnR/e11v5IbyPCGbvJViimtJ0SwHG4nfZFjU77ftcA=
 k8s.io/apimachinery v0.18.3/go.mod h1:OaXp26zu/5J7p0f92ASynJa1pZo06YlV9fG7BoWbCko=
@@ -1625,14 +1625,14 @@ k8s.io/apimachinery v0.20.1/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRp
 k8s.io/apimachinery v0.20.4/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU=
 k8s.io/apimachinery v0.23.5/go.mod h1:BEuFMMBaIbcOqVIJqNZJXGFTP4W6AycEpb5+m/97hrM=
 k8s.io/apimachinery v0.26.0/go.mod h1:tnPmbONNJ7ByJNz9+n9kMjNP8ON+1qoAIIC70lztu74=
-k8s.io/apimachinery v0.31.2 h1:i4vUt2hPK56W6mlT7Ry+AO8eEsyxMD1U44NR22CLTYw=
-k8s.io/apimachinery v0.31.2/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo=
+k8s.io/apimachinery v0.31.1 h1:mhcUBbj7KUjaVhyXILglcVjuS4nYXiwC+KKFBgIVy7U=
+k8s.io/apimachinery v0.31.1/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo=
 k8s.io/apiserver v0.18.2/go.mod h1:Xbh066NqrZO8cbsoenCwyDJ1OSi8Ag8I2lezeHxzwzw=
 k8s.io/apiserver v0.18.3/go.mod h1:tHQRmthRPLUtwqsOnJJMoI8SW3lnoReZeE861lH8vUw=
 k8s.io/apiserver v0.18.4/go.mod h1:q+zoFct5ABNnYkGIaGQ3bcbUNdmPyOCoEBcg51LChY8=
 k8s.io/apiserver v0.20.1/go.mod h1:ro5QHeQkgMS7ZGpvf4tSMx6bBOgPfE+f52KwvXfScaU=
-k8s.io/cli-runtime v0.31.2 h1:7FQt4C4Xnqx8V1GJqymInK0FFsoC+fAZtbLqgXYVOLQ=
-k8s.io/cli-runtime v0.31.2/go.mod h1:XROyicf+G7rQ6FQJMbeDV9jqxzkWXTYD6Uxd15noe0Q=
+k8s.io/cli-runtime v0.31.1 h1:/ZmKhmZ6hNqDM+yf9s3Y4KEYakNXUn5sod2LWGGwCuk=
+k8s.io/cli-runtime v0.31.1/go.mod h1:pKv1cDIaq7ehWGuXQ+A//1OIF+7DI+xudXtExMCbe9U=
 k8s.io/client-go v0.18.2/go.mod h1:Xcm5wVGXX9HAA2JJ2sSBUn3tCJ+4SVlCbl2MNNv+CIU=
 k8s.io/client-go v0.18.3/go.mod h1:4a/dpQEvzAhT1BbuWW09qvIaGw6Gbu1gZYiQZIi1DMw=
 k8s.io/client-go v0.18.4/go.mod h1:f5sXwL4yAZRkAtzOxRWUhA/N8XzGCb+nPZI8PfobZ9g=
@@ -1641,10 +1641,10 @@ k8s.io/client-go v0.19.2/go.mod h1:S5wPhCqyDNAlzM9CnEdgTGV4OqhsW3jGO1UM1epwfJA=
 k8s.io/client-go v0.20.0/go.mod h1:4KWh/g+Ocd8KkCwKF8vUNnmqgv+EVnQDK4MBF4oB5tY=
 k8s.io/client-go v0.20.1/go.mod h1:/zcHdt1TeWSd5HoUe6elJmHSQ6uLLgp4bIJHVEuy+/Y=
 k8s.io/client-go v0.23.5/go.mod h1:flkeinTO1CirYgzMPRWxUCnV0G4Fbu2vLhYCObnt/r4=
-k8s.io/client-go v0.31.2 h1:Y2F4dxU5d3AQj+ybwSMqQnpZH9F30//1ObxOKlTI9yc=
-k8s.io/client-go v0.31.2/go.mod h1:NPa74jSVR/+eez2dFsEIHNa+3o09vtNaWwWwb1qSxSs=
-k8s.io/cloud-provider v0.31.2 h1:ZdstxJOcHzP45j8giE79VJmgNckBbw1ldtIqnhZvKIA=
-k8s.io/cloud-provider v0.31.2/go.mod h1:+4L8YpYCEx1Ko5EKS/PzXyBY5YREwDKChsJjAxHRf08=
+k8s.io/client-go v0.31.1 h1:f0ugtWSbWpxHR7sjVpQwuvw9a3ZKLXX0u0itkFXufb0=
+k8s.io/client-go v0.31.1/go.mod h1:sKI8871MJN2OyeqRlmA4W4KM9KBdBUpDLu/43eGemCg=
+k8s.io/cloud-provider v0.31.1 h1:40b6AgDizwm5eWratZbqubTHMob25VWr6NX2Ei5TwZA=
+k8s.io/cloud-provider v0.31.1/go.mod h1:xAdkE7fdZdu9rKLuOZUMBfagu7bM+bas3iPux/2nLGg=
 k8s.io/code-generator v0.18.2/go.mod h1:+UHX5rSbxmR8kzS+FAv7um6dtYrZokQvjHpDSYRVkTc=
 k8s.io/code-generator v0.18.3/go.mod h1:TgNEVx9hCyPGpdtCWA34olQYLkh3ok9ar7XfSsr8b6c=
 k8s.io/code-generator v0.18.4/go.mod h1:TgNEVx9hCyPGpdtCWA34olQYLkh3ok9ar7XfSsr8b6c=
@@ -1699,8 +1699,8 @@ sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.7/go.mod h1:PHgbrJT
 sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.14/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg=
 sigs.k8s.io/controller-runtime v0.2.2/go.mod h1:9dyohw3ZtoXQuV1e766PHUn+cmrRCIcBh6XIMFNMZ+I=
 sigs.k8s.io/controller-runtime v0.6.1/go.mod h1:XRYBPdbf5XJu9kpS84VJiZ7h/u1hF3gEORz0efEja7A=
-sigs.k8s.io/controller-runtime v0.19.1 h1:Son+Q40+Be3QWb+niBXAg2vFiYWolDjjRfO8hn/cxOk=
-sigs.k8s.io/controller-runtime v0.19.1/go.mod h1:iRmWllt8IlaLjvTTDLhRBXIEtkCK6hwVBJJsYS9Ajf4=
+sigs.k8s.io/controller-runtime v0.19.0 h1:nWVM7aq+Il2ABxwiCizrVDSlmDcshi9llbaFbC0ji/Q=
+sigs.k8s.io/controller-runtime v0.19.0/go.mod h1:iRmWllt8IlaLjvTTDLhRBXIEtkCK6hwVBJJsYS9Ajf4=
 sigs.k8s.io/controller-tools v0.3.0/go.mod h1:enhtKGfxZD1GFEoMgP8Fdbu+uKQ/cq1/WGJhdVChfvI=
 sigs.k8s.io/json v0.0.0-20211020170558-c049b76a60c6/go.mod h1:p4QtZmO4uMYipTQNzagwnNoseA6OxSUutVw05NhYDRs=
 sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
diff --git a/pkg/apis/ceph.rook.io/v1/resources.go b/pkg/apis/ceph.rook.io/v1/resources.go
index daebe4aaf..b87089fde 100644
--- a/pkg/apis/ceph.rook.io/v1/resources.go
+++ b/pkg/apis/ceph.rook.io/v1/resources.go
@@ -31,8 +31,6 @@ const (
 	ResourcesKeyOSD = "osd"
 	// ResourcesKeyPrepareOSD represents the name of resource in the CR for the osd prepare job
 	ResourcesKeyPrepareOSD = "prepareosd"
-	// ResourcesKeyCmdReporter represents the name of resource in the CR for the detect version and network jobs
-	ResourcesKeyCmdReporter = "cmd-reporter"
 	// ResourcesKeyMDS represents the name of resource in the CR for the mds
 	ResourcesKeyMDS = "mds"
 	// ResourcesKeyCrashCollector represents the name of resource in the CR for the crash
@@ -49,22 +47,22 @@ const (
 	ResourcesKeyCephExporter = "exporter"
 )
 
-// GetMgrResources returns the resources for the MGR service
+// GetMgrResources returns the placement for the MGR service
 func GetMgrResources(p ResourceSpec) v1.ResourceRequirements {
 	return p[ResourcesKeyMgr]
 }
 
-// GetMgrSidecarResources returns the resources for the MGR sidecar container
+// GetMgrSidecarResources returns the placement for the MGR sidecar container
 func GetMgrSidecarResources(p ResourceSpec) v1.ResourceRequirements {
 	return p[ResourcesKeyMgrSidecar]
 }
 
-// GetMonResources returns the resources for the monitors
+// GetMonResources returns the placement for the monitors
 func GetMonResources(p ResourceSpec) v1.ResourceRequirements {
 	return p[ResourcesKeyMon]
 }
 
-// GetOSDResources returns the resources for all OSDs or for OSDs of specified device class (hdd, nvme, ssd)
+// GetOSDResources returns the placement for all OSDs or for OSDs of specified device class (hdd, nvme, ssd)
 func GetOSDResources(p ResourceSpec, deviceClass string) v1.ResourceRequirements {
 	if deviceClass == "" {
 		return p[ResourcesKeyOSD]
@@ -82,32 +80,27 @@ func getOSDResourceKeyForDeviceClass(deviceClass string) string {
 	return ResourcesKeyOSD + "-" + deviceClass
 }
 
-// GetPrepareOSDResources returns the resources for the OSDs prepare job
+// GetPrepareOSDResources returns the placement for the OSDs prepare job
 func GetPrepareOSDResources(p ResourceSpec) v1.ResourceRequirements {
 	return p[ResourcesKeyPrepareOSD]
 }
 
-// GetCmdReporterResources returns the resources for the detect version job
-func GetCmdReporterResources(p ResourceSpec) v1.ResourceRequirements {
-	return p[ResourcesKeyCmdReporter]
-}
-
-// GetCrashCollectorResources returns the resources for the crash daemon
+// GetCrashCollectorResources returns the placement for the crash daemon
 func GetCrashCollectorResources(p ResourceSpec) v1.ResourceRequirements {
 	return p[ResourcesKeyCrashCollector]
 }
 
-// GetLogCollectorResources returns the resources for the logo collector
+// GetLogCollectorResources returns the placement for the crash daemon
 func GetLogCollectorResources(p ResourceSpec) v1.ResourceRequirements {
 	return p[ResourcesKeyLogCollector]
 }
 
-// GetCleanupResources returns the resources for the cleanup job
+// GetCleanupResources returns the placement for the cleanup job
 func GetCleanupResources(p ResourceSpec) v1.ResourceRequirements {
 	return p[ResourcesKeyCleanup]
 }
 
-// GetCephExporterResources returns the resources for the cleanup job
+// GetCephExporterResources returns the placement for the cleanup job
 func GetCephExporterResources(p ResourceSpec) v1.ResourceRequirements {
 	return p[ResourcesKeyCephExporter]
 }
diff --git a/pkg/apis/ceph.rook.io/v1/types.go b/pkg/apis/ceph.rook.io/v1/types.go
index da0ef9b58..26af881c7 100755
--- a/pkg/apis/ceph.rook.io/v1/types.go
+++ b/pkg/apis/ceph.rook.io/v1/types.go
@@ -1564,6 +1564,8 @@ type PoolPlacementSpec struct {
 	Name string `json:"name"`
 
 	// Sets given placement as default. Only one placement in the list can be marked as default.
+	// Default is false.
+	// +optional
 	Default bool `json:"default"`
 
 	// The metadata pool used to store ObjectStore bucket index.
@@ -3035,9 +3037,6 @@ type StorageScopeSpec struct {
 	// +optional
 	UseAllNodes bool `json:"useAllNodes,omitempty"`
 	// +optional
-	// Whether to always schedule OSDs on a node even if the node is not currently scheduleable or ready
-	ScheduleAlways bool `json:"scheduleAlways,omitempty"`
-	// +optional
 	OnlyApplyOSDPlacement bool `json:"onlyApplyOSDPlacement,omitempty"`
 	// +kubebuilder:pruning:PreserveUnknownFields
 	// +nullable
diff --git a/pkg/apis/go.mod b/pkg/apis/go.mod
index cbaa66097..2626abd58 100644
--- a/pkg/apis/go.mod
+++ b/pkg/apis/go.mod
@@ -21,8 +21,8 @@ require (
 	github.com/libopenstorage/secrets v0.0.0-20240416031220-a17cf7f72c6c
 	github.com/pkg/errors v0.9.1
 	github.com/stretchr/testify v1.9.0
-	k8s.io/api v0.31.2
-	k8s.io/apimachinery v0.31.2
+	k8s.io/api v0.31.1
+	k8s.io/apimachinery v0.31.1
 )
 
 require (
@@ -36,7 +36,7 @@ require (
 	github.com/onsi/gomega v1.34.2 // indirect
 	github.com/x448/float16 v0.8.4 // indirect
 	gopkg.in/yaml.v2 v2.4.0 // indirect
-	k8s.io/client-go v0.31.2 // indirect
+	k8s.io/client-go v0.31.1 // indirect
 	k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 // indirect
 	sigs.k8s.io/yaml v1.4.0 // indirect
 )
diff --git a/pkg/apis/go.sum b/pkg/apis/go.sum
index 1bd4a99ba..f0be261e2 100644
--- a/pkg/apis/go.sum
+++ b/pkg/apis/go.sum
@@ -1408,8 +1408,8 @@ k8s.io/api v0.20.1/go.mod h1:KqwcCVogGxQY3nBlRpwt+wpAMF/KjaCc7RpywacvqUo=
 k8s.io/api v0.20.4/go.mod h1:++lNL1AJMkDymriNniQsWRkMDzRaX2Y/POTUi8yvqYQ=
 k8s.io/api v0.23.5/go.mod h1:Na4XuKng8PXJ2JsploYYrivXrINeTaycCGcYgF91Xm8=
 k8s.io/api v0.26.0/go.mod h1:k6HDTaIFC8yn1i6pSClSqIwLABIcLV9l5Q4EcngKnQg=
-k8s.io/api v0.31.2 h1:3wLBbL5Uom/8Zy98GRPXpJ254nEFpl+hwndmk9RwmL0=
-k8s.io/api v0.31.2/go.mod h1:bWmGvrGPssSK1ljmLzd3pwCQ9MgoTsRCuK35u6SygUk=
+k8s.io/api v0.31.1 h1:Xe1hX/fPW3PXYYv8BlozYqw63ytA92snr96zMW9gWTU=
+k8s.io/api v0.31.1/go.mod h1:sbN1g6eY6XVLeqNsZGLnI5FwVseTrZX7Fv3O26rhAaI=
 k8s.io/apiextensions-apiserver v0.0.0-20190409022649-727a075fdec8/go.mod h1:IxkesAMoaCRoLrPJdZNZUQp9NfZnzqaVzLhb2VEQzXE=
 k8s.io/apiextensions-apiserver v0.18.3/go.mod h1:TMsNGs7DYpMXd+8MOCX8KzPOCx8fnZMoIGB24m03+JE=
 k8s.io/apiextensions-apiserver v0.20.1/go.mod h1:ntnrZV+6a3dB504qwC5PN/Yg9PBiDNt1EVqbW2kORVk=
@@ -1422,8 +1422,8 @@ k8s.io/apimachinery v0.20.1/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRp
 k8s.io/apimachinery v0.20.4/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU=
 k8s.io/apimachinery v0.23.5/go.mod h1:BEuFMMBaIbcOqVIJqNZJXGFTP4W6AycEpb5+m/97hrM=
 k8s.io/apimachinery v0.26.0/go.mod h1:tnPmbONNJ7ByJNz9+n9kMjNP8ON+1qoAIIC70lztu74=
-k8s.io/apimachinery v0.31.2 h1:i4vUt2hPK56W6mlT7Ry+AO8eEsyxMD1U44NR22CLTYw=
-k8s.io/apimachinery v0.31.2/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo=
+k8s.io/apimachinery v0.31.1 h1:mhcUBbj7KUjaVhyXILglcVjuS4nYXiwC+KKFBgIVy7U=
+k8s.io/apimachinery v0.31.1/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo=
 k8s.io/apiserver v0.18.3/go.mod h1:tHQRmthRPLUtwqsOnJJMoI8SW3lnoReZeE861lH8vUw=
 k8s.io/apiserver v0.20.1/go.mod h1:ro5QHeQkgMS7ZGpvf4tSMx6bBOgPfE+f52KwvXfScaU=
 k8s.io/client-go v0.18.3/go.mod h1:4a/dpQEvzAhT1BbuWW09qvIaGw6Gbu1gZYiQZIi1DMw=
@@ -1432,8 +1432,8 @@ k8s.io/client-go v0.19.2/go.mod h1:S5wPhCqyDNAlzM9CnEdgTGV4OqhsW3jGO1UM1epwfJA=
 k8s.io/client-go v0.20.0/go.mod h1:4KWh/g+Ocd8KkCwKF8vUNnmqgv+EVnQDK4MBF4oB5tY=
 k8s.io/client-go v0.20.1/go.mod h1:/zcHdt1TeWSd5HoUe6elJmHSQ6uLLgp4bIJHVEuy+/Y=
 k8s.io/client-go v0.23.5/go.mod h1:flkeinTO1CirYgzMPRWxUCnV0G4Fbu2vLhYCObnt/r4=
-k8s.io/client-go v0.31.2 h1:Y2F4dxU5d3AQj+ybwSMqQnpZH9F30//1ObxOKlTI9yc=
-k8s.io/client-go v0.31.2/go.mod h1:NPa74jSVR/+eez2dFsEIHNa+3o09vtNaWwWwb1qSxSs=
+k8s.io/client-go v0.31.1 h1:f0ugtWSbWpxHR7sjVpQwuvw9a3ZKLXX0u0itkFXufb0=
+k8s.io/client-go v0.31.1/go.mod h1:sKI8871MJN2OyeqRlmA4W4KM9KBdBUpDLu/43eGemCg=
 k8s.io/code-generator v0.18.3/go.mod h1:TgNEVx9hCyPGpdtCWA34olQYLkh3ok9ar7XfSsr8b6c=
 k8s.io/code-generator v0.19.0/go.mod h1:moqLn7w0t9cMs4+5CQyxnfA/HV8MF6aAVENF+WZZhgk=
 k8s.io/code-generator v0.20.0/go.mod h1:UsqdF+VX4PU2g46NC2JRs4gc+IfrctnwHb76RNbWHJg=
diff --git a/pkg/daemon/ceph/client/filesystem.go b/pkg/daemon/ceph/client/filesystem.go
index 1798a9411..048f7ce1b 100644
--- a/pkg/daemon/ceph/client/filesystem.go
+++ b/pkg/daemon/ceph/client/filesystem.go
@@ -20,7 +20,6 @@ import (
 	ctx "context"
 	"encoding/json"
 	"fmt"
-	"regexp"
 	"strconv"
 	"syscall"
 	"time"
@@ -363,14 +362,14 @@ func deleteFSPool(context *clusterd.Context, clusterInfo *ClusterInfo, poolNames
 }
 
 // WaitForNoStandbys waits for all standbys go away
-func WaitForNoStandbys(context *clusterd.Context, clusterInfo *ClusterInfo, fsName string, retryInterval, timeout time.Duration) error {
+func WaitForNoStandbys(context *clusterd.Context, clusterInfo *ClusterInfo, retryInterval, timeout time.Duration) error {
 	err := wait.PollUntilContextTimeout(clusterInfo.Context, retryInterval, timeout, true, func(ctx ctx.Context) (bool, error) {
 		mdsDump, err := GetMDSDump(context, clusterInfo)
 		if err != nil {
 			logger.Errorf("failed to get fs dump. %v", err)
 			return false, nil
 		}
-		return !filesystemHasStandby(mdsDump, fsName), nil
+		return len(mdsDump.Standbys) == 0, nil
 	})
 
 	if err != nil {
@@ -379,21 +378,6 @@ func WaitForNoStandbys(context *clusterd.Context, clusterInfo *ClusterInfo, fsNa
 	return nil
 }
 
-func filesystemHasStandby(dump *MDSDump, fsName string) bool {
-	for _, standby := range dump.Standbys {
-		// The mds dump does not explicitly return the name of the filesystem that the
-		// daemon belongs to, so the matching to the filesystem name is based on the mds daemon name
-		// with a regular expression comparison with the expected suffix.
-		// For example, if the filesystem is "myfs", the standby name may be "myfs-a" or "myfs-b".
-		matchString := fmt.Sprintf("^%s-[a-z]{1}$", fsName)
-		matched, _ := regexp.MatchString(matchString, standby.Name)
-		if matched {
-			return true
-		}
-	}
-	return false
-}
-
 func GetMDSDump(context *clusterd.Context, clusterInfo *ClusterInfo) (*MDSDump, error) {
 	args := []string{"fs", "dump"}
 	cmd := NewCephCommand(context, clusterInfo, args)
diff --git a/pkg/operator/ceph/cluster/watcher.go b/pkg/operator/ceph/cluster/watcher.go
index a99d9f545..41cd44c1b 100644
--- a/pkg/operator/ceph/cluster/watcher.go
+++ b/pkg/operator/ceph/cluster/watcher.go
@@ -100,7 +100,7 @@ func (c *clientCluster) onK8sNode(ctx context.Context, object runtime.Object, op
 		return false
 	}
 
-	if !k8sutil.GetNodeSchedulable(*node, false) {
+	if !k8sutil.GetNodeSchedulable(*node) {
 		logger.Debugf("node watcher: skipping cluster update. added node %q is unschedulable", node.Labels[corev1.LabelHostname])
 		return false
 	}
@@ -123,7 +123,7 @@ func (c *clientCluster) onK8sNode(ctx context.Context, object runtime.Object, op
 
 	logger.Debugf("node %q is ready, checking if it can run OSDs", node.Name)
 	nodesCheckedForReconcile.Insert(node.Name)
-	err := k8sutil.ValidNode(*node, cephv1.GetOSDPlacement(cluster.Spec.Placement), cluster.Spec.Storage.ScheduleAlways)
+	err := k8sutil.ValidNode(*node, cephv1.GetOSDPlacement(cluster.Spec.Placement))
 	if err == nil {
 		nodeName := node.Name
 		hostname, ok := node.Labels[corev1.LabelHostname]
diff --git a/pkg/operator/ceph/controller/network.go b/pkg/operator/ceph/controller/network.go
index 3b96c9623..b97cb454a 100644
--- a/pkg/operator/ceph/controller/network.go
+++ b/pkg/operator/ceph/controller/network.go
@@ -252,7 +252,6 @@ func discoverAddressRanges(
 		rookImage,
 		rookImage,
 		clusterSpec.CephVersion.ImagePullPolicy,
-		clusterSpec.Resources,
 	)
 	if err != nil {
 		return ranges, errors.Wrapf(err, "failed to set up ceph %q network canary", cephNetwork)
diff --git a/pkg/operator/ceph/controller/version.go b/pkg/operator/ceph/controller/version.go
index fa3d91f01..915672fda 100644
--- a/pkg/operator/ceph/controller/version.go
+++ b/pkg/operator/ceph/controller/version.go
@@ -74,7 +74,6 @@ func DetectCephVersion(ctx context.Context, rookImage, namespace, jobName string
 		rookImage,
 		cephImage,
 		cephClusterSpec.CephVersion.ImagePullPolicy,
-		cephClusterSpec.Resources,
 	)
 	if err != nil {
 		return nil, errors.Wrap(err, "failed to set up ceph version job")
diff --git a/pkg/operator/ceph/csi/controller.go b/pkg/operator/ceph/csi/controller.go
index b9c6c5034..e2fc9e39f 100644
--- a/pkg/operator/ceph/csi/controller.go
+++ b/pkg/operator/ceph/csi/controller.go
@@ -43,6 +43,7 @@ import (
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/runtime"
 	"k8s.io/apimachinery/pkg/types"
+	"k8s.io/apimachinery/pkg/version"
 )
 
 const (
@@ -177,6 +178,11 @@ func (r *ReconcileCSI) reconcile(request reconcile.Request) (reconcile.Result, e
 		r.opConfig.Parameters = opConfig.Data
 	}
 
+	serverVersion, err := r.context.Clientset.Discovery().ServerVersion()
+	if err != nil {
+		return opcontroller.ImmediateRetryResult, errors.Wrap(err, "failed to get server version")
+	}
+
 	enableCSIOperator, err = strconv.ParseBool(k8sutil.GetValue(r.opConfig.Parameters, "ROOK_USE_CSI_OPERATOR", "false"))
 	if err != nil {
 		return reconcileResult, errors.Wrap(err, "unable to parse value for 'ROOK_USE_CSI_OPERATOR'")
@@ -197,7 +203,7 @@ func (r *ReconcileCSI) reconcile(request reconcile.Request) (reconcile.Result, e
 		if kerrors.IsNotFound(err) {
 			logger.Debug("no ceph cluster found not deploying ceph csi driver")
 			EnableRBD, EnableCephFS, EnableNFS = false, false, false
-			err = r.stopDrivers()
+			err = r.stopDrivers(serverVersion)
 			if err != nil {
 				return opcontroller.ImmediateRetryResult, errors.Wrap(err, "failed to stop Drivers")
 			}
@@ -212,7 +218,7 @@ func (r *ReconcileCSI) reconcile(request reconcile.Request) (reconcile.Result, e
 	if len(cephClusters.Items) == 0 {
 		logger.Debug("no ceph cluster found not deploying ceph csi driver")
 		EnableRBD, EnableCephFS, EnableNFS = false, false, false
-		err = r.stopDrivers()
+		err = r.stopDrivers(serverVersion)
 		if err != nil {
 			return opcontroller.ImmediateRetryResult, errors.Wrap(err, "failed to stop Drivers")
 		}
@@ -274,11 +280,11 @@ func (r *ReconcileCSI) reconcile(request reconcile.Request) (reconcile.Result, e
 		// disable Rook-managed CSI drivers if CSI operator is enabled
 		if EnableCSIOperator() {
 			logger.Info("disabling csi-driver since EnableCSIOperator is true")
-			err := r.stopDrivers()
+			err := r.stopDrivers(serverVersion)
 			if err != nil {
 				return opcontroller.ImmediateRetryResult, errors.Wrap(err, "failed to stop csi Drivers")
 			}
-			err = r.reconcileOperatorConfig(cluster, clusterInfo)
+			err = r.reconcileOperatorConfig(cluster, clusterInfo, serverVersion)
 			if err != nil {
 				return opcontroller.ImmediateRetryResult, errors.Wrap(err, "failed to reconcile csi-op config CR")
 			}
@@ -287,7 +293,7 @@ func (r *ReconcileCSI) reconcile(request reconcile.Request) (reconcile.Result, e
 	}
 
 	if !disableCSI && !EnableCSIOperator() {
-		err = r.validateAndConfigureDrivers(ownerInfo)
+		err = r.validateAndConfigureDrivers(serverVersion, ownerInfo)
 		if err != nil {
 			return opcontroller.ImmediateRetryResult, errors.Wrap(err, "failed to configure ceph csi")
 		}
@@ -296,8 +302,8 @@ func (r *ReconcileCSI) reconcile(request reconcile.Request) (reconcile.Result, e
 	return reconcileResult, nil
 }
 
-func (r *ReconcileCSI) reconcileOperatorConfig(cluster cephv1.CephCluster, clusterInfo *cephclient.ClusterInfo) error {
-	if err := r.setParams(); err != nil {
+func (r *ReconcileCSI) reconcileOperatorConfig(cluster cephv1.CephCluster, clusterInfo *cephclient.ClusterInfo, serverVersion *version.Info) error {
+	if err := r.setParams(serverVersion); err != nil {
 		return errors.Wrapf(err, "failed to configure CSI parameters")
 	}
 
diff --git a/pkg/operator/ceph/csi/csi.go b/pkg/operator/ceph/csi/csi.go
index b1ab03618..4313eb0ef 100644
--- a/pkg/operator/ceph/csi/csi.go
+++ b/pkg/operator/ceph/csi/csi.go
@@ -28,14 +28,16 @@ import (
 	"github.com/pkg/errors"
 	kerrors "k8s.io/apimachinery/pkg/api/errors"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/version"
 )
 
-func (r *ReconcileCSI) validateAndConfigureDrivers(ownerInfo *k8sutil.OwnerInfo) error {
+func (r *ReconcileCSI) validateAndConfigureDrivers(serverVersion *version.Info, ownerInfo *k8sutil.OwnerInfo) error {
 	var (
+		v   *CephCSIVersion
 		err error
 	)
 
-	if err = r.setParams(); err != nil {
+	if err = r.setParams(serverVersion); err != nil {
 		return errors.Wrapf(err, "failed to configure CSI parameters")
 	}
 
@@ -43,17 +45,25 @@ func (r *ReconcileCSI) validateAndConfigureDrivers(ownerInfo *k8sutil.OwnerInfo)
 		return errors.Wrapf(err, "failed to validate CSI parameters")
 	}
 
+	if !AllowUnsupported && CSIEnabled() {
+		if v, err = r.validateCSIVersion(ownerInfo); err != nil {
+			return errors.Wrapf(err, "invalid csi version")
+		}
+	} else {
+		logger.Info("skipping csi version check, since unsupported versions are allowed or csi is disabled")
+	}
+
 	if CSIEnabled() {
-		if err = r.startDrivers(ownerInfo); err != nil {
+		if err = r.startDrivers(serverVersion, ownerInfo, v); err != nil {
 			return errors.Wrap(err, "failed to start ceph csi drivers")
 		}
 	}
 
 	// Check whether RBD or CephFS needs to be disabled
-	return r.stopDrivers()
+	return r.stopDrivers(serverVersion)
 }
 
-func (r *ReconcileCSI) setParams() error {
+func (r *ReconcileCSI) setParams(ver *version.Info) error {
 	var err error
 
 	if EnableRBD, err = strconv.ParseBool(k8sutil.GetValue(r.opConfig.Parameters, "ROOK_CSI_ENABLE_RBD", "true")); err != nil {
@@ -68,6 +78,10 @@ func (r *ReconcileCSI) setParams() error {
 		return errors.Wrap(err, "unable to parse value for 'ROOK_CSI_ENABLE_NFS'")
 	}
 
+	if AllowUnsupported, err = strconv.ParseBool(k8sutil.GetValue(r.opConfig.Parameters, "ROOK_CSI_ALLOW_UNSUPPORTED_VERSION", "false")); err != nil {
+		return errors.Wrap(err, "unable to parse value for 'ROOK_CSI_ALLOW_UNSUPPORTED_VERSION'")
+	}
+
 	if CSIParam.EnableCSIHostNetwork, err = strconv.ParseBool(k8sutil.GetValue(r.opConfig.Parameters, "CSI_ENABLE_HOST_NETWORK", "true")); err != nil {
 		return errors.Wrap(err, "failed to parse value for 'CSI_ENABLE_HOST_NETWORK'")
 	}
@@ -125,7 +139,10 @@ func (r *ReconcileCSI) setParams() error {
 		CSIParam.EnableOMAPGenerator = true
 	}
 
-	CSIParam.EnableCSIDriverSeLinuxMount = true
+	// CSIDriver SeLinuxMount option is only available from kubernetes version 1.25.
+	if ver.Major == kubeMinMajor && ver.Minor >= kubeMinVerForCSIDriverSeLinuxMount {
+		CSIParam.EnableCSIDriverSeLinuxMount = true
+	}
 
 	CSIParam.EnableRBDSnapshotter = true
 	if strings.EqualFold(k8sutil.GetValue(r.opConfig.Parameters, "CSI_ENABLE_RBD_SNAPSHOTTER", "true"), "false") {
@@ -196,6 +213,8 @@ func (r *ReconcileCSI) setParams() error {
 		CSIParam.EnablePluginSelinuxHostMount = true
 	}
 
+	logger.Infof("Kubernetes version is %s.%s", ver.Major, ver.Minor)
+
 	logLevel := k8sutil.GetValue(r.opConfig.Parameters, "CSI_LOG_LEVEL", "")
 	CSIParam.LogLevel = defaultLogLevel
 	if logLevel != "" {
diff --git a/pkg/operator/ceph/csi/spec.go b/pkg/operator/ceph/csi/spec.go
index f3e14a681..59af7f601 100644
--- a/pkg/operator/ceph/csi/spec.go
+++ b/pkg/operator/ceph/csi/spec.go
@@ -24,8 +24,11 @@ import (
 	"strings"
 	"time"
 
+	cephv1 "github.com/rook/rook/pkg/apis/ceph.rook.io/v1"
+	"github.com/rook/rook/pkg/operator/ceph/cluster/telemetry"
 	opcontroller "github.com/rook/rook/pkg/operator/ceph/controller"
 	"github.com/rook/rook/pkg/operator/k8sutil"
+	"github.com/rook/rook/pkg/operator/k8sutil/cmdreporter"
 
 	"github.com/pkg/errors"
 	apps "k8s.io/api/apps/v1"
@@ -33,6 +36,7 @@ import (
 	k8scsi "k8s.io/api/storage/v1beta1"
 	kerrors "k8s.io/apimachinery/pkg/api/errors"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+	"k8s.io/apimachinery/pkg/version"
 	"k8s.io/client-go/kubernetes"
 )
 
@@ -113,6 +117,7 @@ var (
 	EnableCephFS              = false
 	EnableNFS                 = false
 	enableCSIOperator         = false
+	AllowUnsupported          = false
 	CustomCSICephConfigExists = false
 
 	// driver names
@@ -173,6 +178,9 @@ var (
 )
 
 const (
+	kubeMinMajor                       = "1"
+	kubeMinVerForCSIDriverSeLinuxMount = "25"
+
 	// common tolerations and node affinity
 	provisionerTolerationsEnv  = "CSI_PROVISIONER_TOLERATIONS"
 	provisionerNodeAffinityEnv = "CSI_PROVISIONER_NODE_AFFINITY"
@@ -226,6 +234,7 @@ const (
 	DefaultRBDLivenessMerticsPort    uint16 = 9080
 	DefaultCSIAddonsPort             uint16 = 9070
 
+	detectCSIVersionName = "rook-ceph-csi-detect-version"
 	// default log level for csi containers
 	defaultLogLevel        uint8 = 0
 	defaultSidecarLogLevel uint8 = 0
@@ -295,7 +304,7 @@ func validateCSIParam() error {
 	return nil
 }
 
-func (r *ReconcileCSI) startDrivers(ownerInfo *k8sutil.OwnerInfo) error {
+func (r *ReconcileCSI) startDrivers(ver *version.Info, ownerInfo *k8sutil.OwnerInfo, v *CephCSIVersion) error {
 	var (
 		err                                                                             error
 		rbdPlugin, cephfsPlugin, nfsPlugin                                              *apps.DaemonSet
@@ -650,14 +659,14 @@ func (r *ReconcileCSI) startDrivers(ownerInfo *k8sutil.OwnerInfo) error {
 	return nil
 }
 
-func (r *ReconcileCSI) stopDrivers() error {
+func (r *ReconcileCSI) stopDrivers(ver *version.Info) error {
 	RBDDriverName = fmt.Sprintf("%s.rbd.csi.ceph.com", r.opConfig.OperatorNamespace)
 	CephFSDriverName = fmt.Sprintf("%s.cephfs.csi.ceph.com", r.opConfig.OperatorNamespace)
 	NFSDriverName = fmt.Sprintf("%s.nfs.csi.ceph.com", r.opConfig.OperatorNamespace)
 
 	if !EnableRBD || EnableCSIOperator() {
 		logger.Debugf("either EnableRBD if `false` or EnableCSIOperator is `true`, `EnableRBD is %t` and `EnableCSIOperator is %t", EnableRBD, EnableCSIOperator())
-		err := r.deleteCSIDriverResources(CsiRBDPlugin, csiRBDProvisioner, "csi-rbdplugin-metrics", RBDDriverName)
+		err := r.deleteCSIDriverResources(ver, CsiRBDPlugin, csiRBDProvisioner, "csi-rbdplugin-metrics", RBDDriverName)
 		if err != nil {
 			return errors.Wrap(err, "failed to remove CSI Ceph RBD driver")
 		}
@@ -666,7 +675,7 @@ func (r *ReconcileCSI) stopDrivers() error {
 
 	if !EnableCephFS || EnableCSIOperator() {
 		logger.Debugf("either EnableCephFS if `false` or EnableCSIOperator is `true`, `EnableCephFS is %t` and `EnableCSIOperator is %t", EnableRBD, EnableCSIOperator())
-		err := r.deleteCSIDriverResources(CsiCephFSPlugin, csiCephFSProvisioner, "csi-cephfsplugin-metrics", CephFSDriverName)
+		err := r.deleteCSIDriverResources(ver, CsiCephFSPlugin, csiCephFSProvisioner, "csi-cephfsplugin-metrics", CephFSDriverName)
 		if err != nil {
 			return errors.Wrap(err, "failed to remove CSI CephFS driver")
 		}
@@ -675,7 +684,7 @@ func (r *ReconcileCSI) stopDrivers() error {
 
 	if !EnableNFS || EnableCSIOperator() {
 		logger.Debugf("either EnableNFS if `false` or EnableCSIOperator is `true`, `EnableNFS is %t` and `EnableCSIOperator is %t", EnableRBD, EnableCSIOperator())
-		err := r.deleteCSIDriverResources(CsiNFSPlugin, csiNFSProvisioner, "csi-nfsplugin-metrics", NFSDriverName)
+		err := r.deleteCSIDriverResources(ver, CsiNFSPlugin, csiNFSProvisioner, "csi-nfsplugin-metrics", NFSDriverName)
 		if err != nil {
 			return errors.Wrap(err, "failed to remove CSI NFS driver")
 		}
@@ -685,7 +694,7 @@ func (r *ReconcileCSI) stopDrivers() error {
 	return nil
 }
 
-func (r *ReconcileCSI) deleteCSIDriverResources(daemonset, deployment, service, driverName string) error {
+func (r *ReconcileCSI) deleteCSIDriverResources(ver *version.Info, daemonset, deployment, service, driverName string) error {
 	csiDriverobj := v1CsiDriver{}
 	err := k8sutil.DeleteDaemonset(r.opManagerContext, r.context.Clientset, r.opConfig.OperatorNamespace, daemonset)
 	if err != nil {
@@ -729,6 +738,66 @@ func (r *ReconcileCSI) applyCephClusterNetworkConfig(ctx context.Context, object
 	return nil
 }
 
+// ValidateCSIVersion checks if the configured ceph-csi image is supported
+func (r *ReconcileCSI) validateCSIVersion(ownerInfo *k8sutil.OwnerInfo) (*CephCSIVersion, error) {
+	timeout := 15 * time.Minute
+
+	logger.Infof("detecting the ceph csi image version for image %q", CSIParam.CSIPluginImage)
+
+	versionReporter, err := cmdreporter.New(
+		r.context.Clientset,
+		ownerInfo,
+		detectCSIVersionName,
+		detectCSIVersionName,
+		r.opConfig.OperatorNamespace,
+		[]string{"cephcsi"},
+		[]string{"--version"},
+		r.opConfig.Image,
+		CSIParam.CSIPluginImage,
+		corev1.PullPolicy(CSIParam.ImagePullPolicy),
+	)
+	if err != nil {
+		return nil, errors.Wrap(err, "failed to set up ceph CSI version job")
+	}
+
+	job := versionReporter.Job()
+	job.Spec.Template.Spec.ServiceAccountName = r.opConfig.ServiceAccount
+
+	// Apply csi provisioner toleration and affinity for csi version check job
+	job.Spec.Template.Spec.Tolerations = getToleration(r.opConfig.Parameters, provisionerTolerationsEnv, []corev1.Toleration{})
+	job.Spec.Template.Spec.Affinity = &corev1.Affinity{
+		NodeAffinity: getNodeAffinity(r.opConfig.Parameters, provisionerNodeAffinityEnv, &corev1.NodeAffinity{}),
+	}
+	if r.firstCephCluster != nil {
+		cephv1.GetCmdReporterAnnotations(r.firstCephCluster.Annotations).ApplyToObjectMeta(&job.Spec.Template.ObjectMeta)
+		cephv1.GetCmdReporterLabels(r.firstCephCluster.Labels).ApplyToObjectMeta(&job.Spec.Template.ObjectMeta)
+	}
+
+	stdout, _, retcode, err := versionReporter.Run(r.opManagerContext, timeout)
+	if err != nil {
+		return nil, errors.Wrap(err, "failed to complete ceph CSI version job")
+	}
+
+	if retcode != 0 {
+		return nil, errors.Errorf("ceph CSI version job returned %d", retcode)
+	}
+
+	version, err := extractCephCSIVersion(stdout)
+	if err != nil {
+		return nil, errors.Wrap(err, "failed to extract ceph CSI version")
+	}
+	logger.Infof("Detected ceph CSI image version: %q", version)
+
+	// Set the CSI version on a package variable so that each cluster reporting the
+	// telemetry can report the same CSI version
+	telemetry.CSIVersion = version.String()
+
+	if !version.Supported() {
+		return nil, errors.Errorf("ceph CSI image needs to be at least version %q", minimum.String())
+	}
+	return version, nil
+}
+
 func validateCSIDriverNamePrefix(ctx context.Context, clientset kubernetes.Interface, namespace, driverNamePrefix string) error {
 	if EnableRBD {
 		rbdDriverNamePrefix, err := getCSIDriverNamePrefixFromDeployment(ctx, clientset, namespace, csiRBDProvisioner, "csi-rbdplugin")
diff --git a/pkg/operator/ceph/csi/version.go b/pkg/operator/ceph/csi/version.go
new file mode 100644
index 000000000..aa4d9bb26
--- /dev/null
+++ b/pkg/operator/ceph/csi/version.go
@@ -0,0 +1,114 @@
+/*
+Copyright 2020 The Rook Authors. All rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package csi
+
+import (
+	"fmt"
+	"regexp"
+	"strconv"
+
+	"github.com/pkg/errors"
+)
+
+var (
+	//minimum supported version is 3.10.0
+	minimum = CephCSIVersion{3, 11, 0}
+	//supportedCSIVersions are versions that rook supports
+	releasev312 = CephCSIVersion{3, 12, 0}
+
+	supportedCSIVersions = []CephCSIVersion{
+		minimum,
+		releasev312,
+	}
+
+	// for parsing the output of `cephcsi`
+	versionCSIPattern = regexp.MustCompile(`v(\d+)\.(\d+)\.(\d+)`)
+)
+
+// CephCSIVersion represents the Ceph CSI version format
+type CephCSIVersion struct {
+	Major  int
+	Minor  int
+	Bugfix int
+}
+
+func (v *CephCSIVersion) String() string {
+	return fmt.Sprintf("v%d.%d.%d",
+		v.Major, v.Minor, v.Bugfix)
+}
+
+// Supported checks if the detected version is part of the known supported CSI versions
+func (v *CephCSIVersion) Supported() bool {
+	if !v.isAtLeast(&minimum) {
+		return false
+	}
+
+	// if AllowUnsupported is set also a csi-image greater than the supported ones are allowed
+	if AllowUnsupported {
+		return true
+	}
+	for _, sv := range supportedCSIVersions {
+		if v.Major == sv.Major {
+			if v.Minor == sv.Minor {
+				if v.Bugfix >= sv.Bugfix {
+					return true
+				}
+			}
+		}
+	}
+	return false
+}
+
+func (v *CephCSIVersion) isAtLeast(version *CephCSIVersion) bool {
+	if v.Major > version.Major {
+		return true
+	}
+	if v.Major == version.Major && v.Minor >= version.Minor {
+		if v.Minor > version.Minor {
+			return true
+		}
+		if v.Bugfix >= version.Bugfix {
+			return true
+		}
+	}
+	return false
+}
+
+// extractCephCSIVersion extracts the major, minor and extra digit of a Ceph CSI release
+func extractCephCSIVersion(src string) (*CephCSIVersion, error) {
+	m := versionCSIPattern.FindStringSubmatch(src)
+	if m == nil || len(m) < 3 {
+		return nil, errors.Errorf("failed to parse version from: %q", CSIParam.CSIPluginImage)
+	}
+
+	major, err := strconv.Atoi(m[1])
+	if err != nil {
+		return nil, errors.Wrapf(err, "failed to parse version major part: %q", m[0])
+	}
+
+	minor, err := strconv.Atoi(m[2])
+	if err != nil {
+		return nil, errors.Wrapf(err, "failed to parse version minor part: %q", m[1])
+	}
+
+	bugfix, err := strconv.Atoi(m[3])
+	if err != nil {
+		return nil, errors.Wrapf(err, "failed to parse version bugfix part: %q", m[2])
+	}
+
+	return &CephCSIVersion{major, minor, bugfix}, nil
+}
diff --git a/pkg/operator/ceph/file/mds/mds.go b/pkg/operator/ceph/file/mds/mds.go
index de2656f34..a31510177 100644
--- a/pkg/operator/ceph/file/mds/mds.go
+++ b/pkg/operator/ceph/file/mds/mds.go
@@ -290,7 +290,7 @@ func (c *Cluster) upgradeMDS() error {
 		return errors.Wrap(err, "failed to scale down deployments during upgrade")
 	}
 	logger.Debugf("waiting for all standbys gone")
-	if err := cephclient.WaitForNoStandbys(c.context, c.clusterInfo, c.fs.Name, 3*time.Second, 120*time.Second); err != nil {
+	if err := cephclient.WaitForNoStandbys(c.context, c.clusterInfo, 3*time.Second, 120*time.Second); err != nil {
 		return errors.Wrap(err, "failed to wait for stopping all standbys")
 	}
 
diff --git a/pkg/operator/ceph/object/shared_pools.go b/pkg/operator/ceph/object/shared_pools.go
index b447cdf46..95bafb14c 100644
--- a/pkg/operator/ceph/object/shared_pools.go
+++ b/pkg/operator/ceph/object/shared_pools.go
@@ -89,7 +89,6 @@ func adjustZonePlacementPools(zone map[string]interface{}, spec cephv1.ObjectSha
 
 	fromSpec := toZonePlacementPools(spec, name)
 
-	defaultPlacementName := getDefaultPlacementName(spec)
 	inConfig := map[string]struct{}{}
 	idxToRemove := map[int]struct{}{}
 	for i, p := range placements {
@@ -102,10 +101,19 @@ func adjustZonePlacementPools(zone map[string]interface{}, spec cephv1.ObjectSha
 			return nil, fmt.Errorf("unable to get pool placement name for zone %s: %w", name, err)
 		}
 		// check if placement should be removed
-		if _, inSpec := fromSpec[placementID]; !inSpec && placementID != defaultPlacementName {
-			// remove placement if it is not in spec, but don't remove default placement
-			idxToRemove[i] = struct{}{}
-			continue
+		if _, inSpec := fromSpec[placementID]; !inSpec {
+			if placementID == defaultPlacementCephConfigName {
+				// 'default-placement' should always be kept as a workaround for https://tracker.ceph.com/issues/68775.
+				// if user specified other placement as default, then copy pool names to 'default-placement' from it:
+				if userDefault, inSpec := fromSpec[getDefaultPlacementName(spec)]; inSpec {
+					// duplicate user defined default placement under 'default-placement' name in spec to update pools on the next step
+					fromSpec[defaultPlacementCephConfigName] = userDefault
+				}
+			} else {
+				// remove placement if it is not in spec
+				idxToRemove[i] = struct{}{}
+				continue
+			}
 		}
 		// update placement with values from spec:
 		if pSpec, inSpec := fromSpec[placementID]; inSpec {
@@ -155,6 +163,29 @@ func adjustZonePlacementPools(zone map[string]interface{}, spec cephv1.ObjectSha
 		placements = append(placements, pObj)
 	}
 
+	// sort placements array.
+	// Reason: 'radosgw-admin zone set --infile' sorts placement_pools by key before storing it in ceph
+	// and returns JSON with sorted placement_pools array. So we sort input array for easy comparison with applied JSON.
+	sort.Slice(placements, func(i, j int) bool {
+		pI, ok := placements[i].(map[string]interface{})
+		if !ok {
+			return false
+		}
+		nameI, err := getObjProperty[string](pI, "key")
+		if err != nil {
+			return false
+		}
+		pJ, ok := placements[j].(map[string]interface{})
+		if !ok {
+			return false
+		}
+		nameJ, err := getObjProperty[string](pJ, "key")
+		if err != nil {
+			return false
+		}
+		return nameI < nameJ
+	})
+
 	_, err = updateObjProperty(zone, placements, "placement_pools")
 	if err != nil {
 		return nil, fmt.Errorf("unable to set pool placements for zone %q: %w", name, err)
@@ -182,16 +213,9 @@ func getDefaultMetadataPool(spec cephv1.ObjectSharedPoolsSpec) string {
 
 // toZonePlacementPools converts pool placement CRD definition to zone config json format structures
 func toZonePlacementPools(spec cephv1.ObjectSharedPoolsSpec, ns string) map[string]ZonePlacementPool {
-	hasDefault := false
 	res := make(map[string]ZonePlacementPool, len(spec.PoolPlacements)+1)
-	for _, pp := range spec.PoolPlacements {
-		if pp.Default {
-			hasDefault = true
-		}
-		res[pp.Name] = toZonePlacementPool(pp, ns)
-	}
-	if !hasDefault && spec.DataPoolName != "" && spec.MetadataPoolName != "" {
-		// set shared pools as default if no default placement was provided
+	// map sharedPools if presented:
+	if spec.DataPoolName != "" && spec.MetadataPoolName != "" {
 		res[defaultPlacementCephConfigName] = ZonePlacementPool{
 			Key: defaultPlacementCephConfigName,
 			Val: ZonePlacementPoolVal{
@@ -211,6 +235,10 @@ func toZonePlacementPools(spec cephv1.ObjectSharedPoolsSpec, ns string) map[stri
 			},
 		}
 	}
+	// map pool placements:
+	for _, pp := range spec.PoolPlacements {
+		res[pp.Name] = toZonePlacementPool(pp, ns)
+	}
 	return res
 }
 
@@ -493,7 +521,7 @@ func updateZoneGroupJSON(objContext *Context, group map[string]interface{}) (map
 	args := []string{"zonegroup", "set", zoneArg, "--infile=" + configFilename, realmArg, zoneGroupArg}
 	updatedBytes, err := RunAdminCommandNoMultisite(objContext, false, args...)
 	if err != nil {
-		return nil, errors.Wrap(err, "failed to set zone config")
+		return nil, errors.Wrap(err, fmt.Sprintf("failed to set zonegroup config %s", updatedBytes))
 	}
 	updated := map[string]interface{}{}
 	err = json.Unmarshal([]byte(updatedBytes), &updated)
diff --git a/pkg/operator/k8sutil/cmdreporter/cmdreporter.go b/pkg/operator/k8sutil/cmdreporter/cmdreporter.go
index 6a5eea450..3de895d3a 100644
--- a/pkg/operator/k8sutil/cmdreporter/cmdreporter.go
+++ b/pkg/operator/k8sutil/cmdreporter/cmdreporter.go
@@ -80,7 +80,6 @@ type cmdReporterCfg struct {
 	rookImage       string
 	runImage        string
 	imagePullPolicy v1.PullPolicy
-	resources       cephv1.ResourceSpec
 }
 
 // New creates a new CmdReporter.
@@ -102,7 +101,6 @@ func New(
 	cmd, args []string,
 	rookImage, runImage string,
 	imagePullPolicy v1.PullPolicy,
-	resources cephv1.ResourceSpec,
 ) (CmdReporterInterface, error) {
 	cfg := &cmdReporterCfg{
 		clientset:       clientset,
@@ -115,7 +113,6 @@ func New(
 		rookImage:       rookImage,
 		runImage:        runImage,
 		imagePullPolicy: imagePullPolicy,
-		resources:       resources,
 	}
 
 	// Validate contents of config struct, not inputs to function to catch any developer errors
@@ -355,7 +352,6 @@ func (cr *cmdReporterCfg) initContainers() []v1.Container {
 		},
 		Image:           cr.rookImage,
 		ImagePullPolicy: cr.imagePullPolicy,
-		Resources:       cephv1.GetCmdReporterResources(cr.resources),
 	}
 	_, copyBinsMount := copyBinariesVolAndMount()
 	c.VolumeMounts = []v1.VolumeMount{copyBinsMount}
@@ -386,7 +382,6 @@ func (cr *cmdReporterCfg) container() (*v1.Container, error) {
 		},
 		Image:           cr.runImage,
 		ImagePullPolicy: cr.imagePullPolicy,
-		Resources:       cephv1.GetCmdReporterResources(cr.resources),
 	}
 	if cr.needToCopyBinaries() {
 		_, copyBinsMount := copyBinariesVolAndMount()
@@ -424,7 +419,6 @@ func MockCmdReporterJob(
 	rookImage string,
 	runImage string,
 	imagePullPolicy v1.PullPolicy,
-	resources cephv1.ResourceSpec,
 ) (*batch.Job, error) {
 	cfg := &cmdReporterCfg{
 		clientset:       clientset,
@@ -437,7 +431,6 @@ func MockCmdReporterJob(
 		rookImage:       rookImage,
 		runImage:        runImage,
 		imagePullPolicy: imagePullPolicy,
-		resources:       resources,
 	}
 	return cfg.initJobSpec()
 }
diff --git a/pkg/operator/k8sutil/node.go b/pkg/operator/k8sutil/node.go
index 2dcce962c..0962c2692 100644
--- a/pkg/operator/k8sutil/node.go
+++ b/pkg/operator/k8sutil/node.go
@@ -22,9 +22,8 @@ import (
 	"encoding/json"
 	"errors"
 	"fmt"
-	"strings"
-
 	"sigs.k8s.io/yaml"
+	"strings"
 
 	cephv1 "github.com/rook/rook/pkg/apis/ceph.rook.io/v1"
 	v1 "k8s.io/api/core/v1"
@@ -38,7 +37,7 @@ import (
 // validNodeNoSched returns true if the node (1) meets Rook's placement terms,
 // and (2) is ready. Unlike ValidNode, this method will ignore the
 // Node.Spec.Unschedulable flag. False otherwise.
-func validNodeNoSched(node v1.Node, placement cephv1.Placement, scheduleAlways bool) error {
+func validNodeNoSched(node v1.Node, placement cephv1.Placement) error {
 	p, err := NodeMeetsPlacementTerms(node, placement, false)
 	if err != nil {
 		return fmt.Errorf("failed to check if node meets Rook placement terms. %+v", err)
@@ -48,10 +47,6 @@ func validNodeNoSched(node v1.Node, placement cephv1.Placement, scheduleAlways b
 	}
 
 	if !NodeIsReady(node) {
-		if scheduleAlways {
-			logger.Infof("node %q is not ready but scheduleAlways set", node.Name)
-			return nil
-		}
 		return errors.New("node is not ready")
 	}
 
@@ -60,12 +55,12 @@ func validNodeNoSched(node v1.Node, placement cephv1.Placement, scheduleAlways b
 
 // ValidNode returns true if the node (1) is schedulable, (2) meets Rook's placement terms, and
 // (3) is ready. False otherwise.
-func ValidNode(node v1.Node, placement cephv1.Placement, scheduleAlways bool) error {
-	if !GetNodeSchedulable(node, scheduleAlways) {
+func ValidNode(node v1.Node, placement cephv1.Placement) error {
+	if !GetNodeSchedulable(node) {
 		return errors.New("node is unschedulable")
 	}
 
-	return validNodeNoSched(node, placement, scheduleAlways)
+	return validNodeNoSched(node, placement)
 }
 
 // GetValidNodes returns all nodes that (1) are not cordoned, (2) meet Rook's placement terms, and
@@ -81,7 +76,7 @@ func GetValidNodes(ctx context.Context, rookStorage cephv1.StorageScopeSpec, cli
 	validK8sNodes := []v1.Node{}
 	reasonsForSkippingNodes := map[string][]string{}
 	for _, n := range matchingK8sNodes {
-		err := ValidNode(n, placement, rookStorage.ScheduleAlways)
+		err := ValidNode(n, placement)
 		if err != nil {
 			reason := err.Error()
 			reasonsForSkippingNodes[reason] = append(reasonsForSkippingNodes[reason], n.Name)
@@ -149,15 +144,10 @@ func GetNodeHostNames(ctx context.Context, clientset kubernetes.Interface) (map[
 // GetNodeSchedulable returns a boolean if the node is tainted as Schedulable or not
 // true -> Node is schedulable
 // false -> Node is unschedulable
-func GetNodeSchedulable(node v1.Node, scheduleAlways bool) bool {
+func GetNodeSchedulable(node v1.Node) bool {
 	// some unit tests set this to quickly emulate an unschedulable node; if this is set to true,
 	// we can shortcut deeper inspection for schedulability.
-	schedulable := !node.Spec.Unschedulable
-	if !schedulable && scheduleAlways {
-		logger.Infof("node %q is unschedulable but scheduling since scheduleAlways is set", node.Name)
-		return true
-	}
-	return schedulable
+	return !node.Spec.Unschedulable
 }
 
 // NodeMeetsPlacementTerms returns true if the Rook placement allows the node to have resources scheduled


Test patch:
diff --git a/.github/workflows/canary-integration-test.yml b/.github/workflows/canary-integration-test.yml
index ad5e75125..8069ab2cd 100644
--- a/.github/workflows/canary-integration-test.yml
+++ b/.github/workflows/canary-integration-test.yml
@@ -25,7 +25,7 @@ jobs:
         ceph-image: ${{ fromJson(inputs.ceph_images) }}
     steps:
       - name: checkout
-        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+        uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
         with:
           fetch-depth: 0
 
@@ -33,7 +33,6 @@ jobs:
         uses: ./.github/workflows/tmate_debug
         with:
           use-tmate: ${{ secrets.USE_TMATE }}
-          debug-ci: ${{ contains(github.event.pull_request.labels.*.name, 'debug-ci') }}
 
       - name: setup cluster resources
         uses: ./.github/workflows/canary-test-config
@@ -335,7 +334,7 @@ jobs:
         ceph-image: ${{ fromJson(inputs.ceph_images) }}
     steps:
       - name: checkout
-        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+        uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
         with:
           fetch-depth: 0
 
@@ -343,7 +342,6 @@ jobs:
         uses: ./.github/workflows/tmate_debug
         with:
           use-tmate: ${{ secrets.USE_TMATE }}
-          debug-ci: ${{ contains(github.event.pull_request.labels.*.name, 'debug-ci') }}
 
       - name: setup cluster resources
         uses: ./.github/workflows/canary-test-config
@@ -414,14 +412,13 @@ jobs:
         ceph-image: ${{ fromJson(inputs.ceph_images) }}
     steps:
       - name: checkout
-        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+        uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
         with:
           fetch-depth: 0
       - name: consider debugging
         uses: ./.github/workflows/tmate_debug
         with:
           use-tmate: ${{ secrets.USE_TMATE }}
-          debug-ci: ${{ contains(github.event.pull_request.labels.*.name, 'debug-ci') }}
 
       - name: setup cluster resources
         uses: ./.github/workflows/canary-test-config
@@ -466,7 +463,7 @@ jobs:
         ceph-image: ${{ fromJson(inputs.ceph_images) }}
     steps:
       - name: checkout
-        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+        uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
         with:
           fetch-depth: 0
 
@@ -474,7 +471,6 @@ jobs:
         uses: ./.github/workflows/tmate_debug
         with:
           use-tmate: ${{ secrets.USE_TMATE }}
-          debug-ci: ${{ contains(github.event.pull_request.labels.*.name, 'debug-ci') }}
 
       - name: setup cluster resources
         uses: ./.github/workflows/canary-test-config
@@ -516,7 +512,7 @@ jobs:
         ceph-image: ${{ fromJson(inputs.ceph_images) }}
     steps:
       - name: checkout
-        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+        uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
         with:
           fetch-depth: 0
 
@@ -524,7 +520,6 @@ jobs:
         uses: ./.github/workflows/tmate_debug
         with:
           use-tmate: ${{ secrets.USE_TMATE }}
-          debug-ci: ${{ contains(github.event.pull_request.labels.*.name, 'debug-ci') }}
 
       - name: setup cluster resources
         uses: ./.github/workflows/canary-test-config
@@ -575,7 +570,7 @@ jobs:
         ceph-image: ${{ fromJson(inputs.ceph_images) }}
     steps:
       - name: checkout
-        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+        uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
         with:
           fetch-depth: 0
 
@@ -583,7 +578,6 @@ jobs:
         uses: ./.github/workflows/tmate_debug
         with:
           use-tmate: ${{ secrets.USE_TMATE }}
-          debug-ci: ${{ contains(github.event.pull_request.labels.*.name, 'debug-ci') }}
 
       - name: setup cluster resources
         uses: ./.github/workflows/canary-test-config
@@ -628,7 +622,7 @@ jobs:
         ceph-image: ${{ fromJson(inputs.ceph_images) }}
     steps:
       - name: checkout
-        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+        uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
         with:
           fetch-depth: 0
 
@@ -636,7 +630,6 @@ jobs:
         uses: ./.github/workflows/tmate_debug
         with:
           use-tmate: ${{ secrets.USE_TMATE }}
-          debug-ci: ${{ contains(github.event.pull_request.labels.*.name, 'debug-ci') }}
 
       - name: setup cluster resources
         uses: ./.github/workflows/canary-test-config
@@ -686,7 +679,7 @@ jobs:
         ceph-image: ${{ fromJson(inputs.ceph_images) }}
     steps:
       - name: checkout
-        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+        uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
         with:
           fetch-depth: 0
 
@@ -694,7 +687,6 @@ jobs:
         uses: ./.github/workflows/tmate_debug
         with:
           use-tmate: ${{ secrets.USE_TMATE }}
-          debug-ci: ${{ contains(github.event.pull_request.labels.*.name, 'debug-ci') }}
 
       - name: setup cluster resources
         uses: ./.github/workflows/canary-test-config
@@ -763,7 +755,7 @@ jobs:
         ceph-image: ${{ fromJson(inputs.ceph_images) }}
     steps:
       - name: checkout
-        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+        uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
         with:
           fetch-depth: 0
 
@@ -771,7 +763,6 @@ jobs:
         uses: ./.github/workflows/tmate_debug
         with:
           use-tmate: ${{ secrets.USE_TMATE }}
-          debug-ci: ${{ contains(github.event.pull_request.labels.*.name, 'debug-ci') }}
 
       - name: setup cluster resources
         uses: ./.github/workflows/canary-test-config
@@ -816,7 +807,7 @@ jobs:
         ceph-image: ${{ fromJson(inputs.ceph_images) }}
     steps:
       - name: checkout
-        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+        uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
         with:
           fetch-depth: 0
 
@@ -824,7 +815,6 @@ jobs:
         uses: ./.github/workflows/tmate_debug
         with:
           use-tmate: ${{ secrets.USE_TMATE }}
-          debug-ci: ${{ contains(github.event.pull_request.labels.*.name, 'debug-ci') }}
 
       - name: setup cluster resources
         uses: ./.github/workflows/canary-test-config
@@ -872,7 +862,7 @@ jobs:
         ceph-image: ${{ fromJson(inputs.ceph_images) }}
     steps:
       - name: checkout
-        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+        uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
         with:
           fetch-depth: 0
 
@@ -880,7 +870,6 @@ jobs:
         uses: ./.github/workflows/tmate_debug
         with:
           use-tmate: ${{ secrets.USE_TMATE }}
-          debug-ci: ${{ contains(github.event.pull_request.labels.*.name, 'debug-ci') }}
 
       - name: setup cluster resources
         uses: ./.github/workflows/canary-test-config
@@ -941,7 +930,7 @@ jobs:
         ceph-image: ${{ fromJson(inputs.ceph_images) }}
     steps:
       - name: checkout
-        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+        uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
         with:
           fetch-depth: 0
 
@@ -949,7 +938,6 @@ jobs:
         uses: ./.github/workflows/tmate_debug
         with:
           use-tmate: ${{ secrets.USE_TMATE }}
-          debug-ci: ${{ contains(github.event.pull_request.labels.*.name, 'debug-ci') }}
 
       - name: setup cluster resources
         uses: ./.github/workflows/canary-test-config
@@ -996,7 +984,7 @@ jobs:
         ceph-image: ${{ fromJson(inputs.ceph_images) }}
     steps:
       - name: checkout
-        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+        uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
         with:
           fetch-depth: 0
 
@@ -1004,7 +992,6 @@ jobs:
         uses: ./.github/workflows/tmate_debug
         with:
           use-tmate: ${{ secrets.USE_TMATE }}
-          debug-ci: ${{ contains(github.event.pull_request.labels.*.name, 'debug-ci') }}
 
       - name: setup cluster resources
         uses: ./.github/workflows/canary-test-config
@@ -1061,7 +1048,7 @@ jobs:
         ceph-image: ${{ fromJson(inputs.ceph_images) }}
     steps:
       - name: checkout
-        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+        uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
         with:
           fetch-depth: 0
 
@@ -1069,7 +1056,6 @@ jobs:
         uses: ./.github/workflows/tmate_debug
         with:
           use-tmate: ${{ secrets.USE_TMATE }}
-          debug-ci: ${{ contains(github.event.pull_request.labels.*.name, 'debug-ci') }}
 
       - name: setup cluster resources
         uses: ./.github/workflows/canary-test-config
@@ -1152,7 +1138,7 @@ jobs:
         ceph-image: ${{ fromJson(inputs.ceph_images) }}
     steps:
       - name: checkout
-        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+        uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
         with:
           fetch-depth: 0
 
@@ -1160,7 +1146,6 @@ jobs:
         uses: ./.github/workflows/tmate_debug
         with:
           use-tmate: ${{ secrets.USE_TMATE }}
-          debug-ci: ${{ contains(github.event.pull_request.labels.*.name, 'debug-ci') }}
 
       - name: setup cluster resources
         uses: ./.github/workflows/canary-test-config
@@ -1223,7 +1208,7 @@ jobs:
         ceph-image: ${{ fromJson(inputs.ceph_images) }}
     steps:
       - name: checkout
-        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+        uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
         with:
           fetch-depth: 0
 
@@ -1231,7 +1216,6 @@ jobs:
         uses: ./.github/workflows/tmate_debug
         with:
           use-tmate: ${{ secrets.USE_TMATE }}
-          debug-ci: ${{ contains(github.event.pull_request.labels.*.name, 'debug-ci') }}
 
       - name: setup cluster resources
         uses: ./.github/workflows/canary-test-config
@@ -1281,7 +1265,7 @@ jobs:
         ceph-image: ${{ fromJson(inputs.ceph_images) }}
     steps:
       - name: checkout
-        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+        uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
         with:
           fetch-depth: 0
 
@@ -1289,7 +1273,6 @@ jobs:
         uses: ./.github/workflows/tmate_debug
         with:
           use-tmate: ${{ secrets.USE_TMATE }}
-          debug-ci: ${{ contains(github.event.pull_request.labels.*.name, 'debug-ci') }}
 
       - name: setup cluster resources
         uses: ./.github/workflows/canary-test-config
@@ -1541,7 +1524,7 @@ jobs:
         ceph-image: ${{ fromJson(inputs.ceph_images) }}
     steps:
       - name: checkout
-        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+        uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
         with:
           fetch-depth: 0
 
@@ -1549,7 +1532,6 @@ jobs:
         uses: ./.github/workflows/tmate_debug
         with:
           use-tmate: ${{ secrets.USE_TMATE }}
-          debug-ci: ${{ contains(github.event.pull_request.labels.*.name, 'debug-ci') }}
 
       - name: setup cluster resources
         uses: ./.github/workflows/canary-test-config
@@ -1637,7 +1619,7 @@ jobs:
       matrix:
         ceph-image: ${{ fromJson(inputs.ceph_images) }}
     steps:
-      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+      - uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
         with:
           fetch-depth: 0
 
@@ -1645,7 +1627,6 @@ jobs:
         uses: ./.github/workflows/tmate_debug
         with:
           use-tmate: ${{ secrets.USE_TMATE }}
-          debug-ci: ${{ contains(github.event.pull_request.labels.*.name, 'debug-ci') }}
 
       - name: run encryption KMS IBM Key Protect
         uses: ./.github/workflows/encryption-pvc-kms-ibm-kp
@@ -1658,7 +1639,6 @@ jobs:
           ibm-service-api-key: ${{ secrets.IBM_SERVICE_API_KEY }}
           github-token: ${{ secrets.GITHUB_TOKEN }}
           artifact-name: ${{ github.job }}-${{ matrix.ceph-image }}
-          ceph-image: ${{ matrix.ceph-image }}
 
   multus-public-and-cluster:
     runs-on: ubuntu-22.04
@@ -1668,7 +1648,7 @@ jobs:
         ceph-image: ${{ fromJson(inputs.ceph_images) }}
     steps:
       - name: checkout
-        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+        uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
         with:
           fetch-depth: 0
 
@@ -1676,7 +1656,6 @@ jobs:
         uses: ./.github/workflows/tmate_debug
         with:
           use-tmate: ${{ secrets.USE_TMATE }}
-          debug-ci: ${{ contains(github.event.pull_request.labels.*.name, 'debug-ci') }}
 
       - name: setup cluster resources
         uses: ./.github/workflows/canary-test-config
@@ -1750,7 +1729,7 @@ jobs:
         ceph-image: ${{ fromJson(inputs.ceph_images) }}
     steps:
       - name: checkout
-        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+        uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
         with:
           fetch-depth: 0
 
@@ -1758,7 +1737,6 @@ jobs:
         uses: ./.github/workflows/tmate_debug
         with:
           use-tmate: ${{ secrets.USE_TMATE }}
-          debug-ci: ${{ contains(github.event.pull_request.labels.*.name, 'debug-ci') }}
 
       - name: setup cluster resources
         uses: ./.github/workflows/canary-test-config
diff --git a/.github/workflows/integration-test-helm-suite.yaml b/.github/workflows/integration-test-helm-suite.yaml
index 8d30d681c..f00ccdc79 100644
--- a/.github/workflows/integration-test-helm-suite.yaml
+++ b/.github/workflows/integration-test-helm-suite.yaml
@@ -31,7 +31,7 @@ jobs:
         kubernetes-versions: ["v1.26.15", "v1.31.0"]
     steps:
       - name: checkout
-        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+        uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
         with:
           fetch-depth: 0
 
@@ -39,7 +39,6 @@ jobs:
         uses: ./.github/workflows/tmate_debug
         with:
           use-tmate: ${{ secrets.USE_TMATE }}
-          debug-ci: ${{ contains(github.event.pull_request.labels.*.name, 'debug-ci') }}
 
       - name: setup latest cluster resources
         uses: ./.github/workflows/integration-test-config-latest-k8s
diff --git a/.github/workflows/integration-test-keystone-auth-suite.yaml b/.github/workflows/integration-test-keystone-auth-suite.yaml
index 5d4db9c53..014652a5c 100644
--- a/.github/workflows/integration-test-keystone-auth-suite.yaml
+++ b/.github/workflows/integration-test-keystone-auth-suite.yaml
@@ -31,7 +31,7 @@ jobs:
         kubernetes-versions: ["v1.26.15", "v1.31.0"]
     steps:
       - name: checkout
-        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+        uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
         with:
           fetch-depth: 0
 
@@ -39,7 +39,6 @@ jobs:
         uses: ./.github/workflows/tmate_debug
         with:
           use-tmate: ${{ secrets.USE_TMATE }}
-          debug-ci: ${{ contains(github.event.pull_request.labels.*.name, 'debug-ci') }}
 
       - name: setup latest cluster resources
         uses: ./.github/workflows/integration-test-config-latest-k8s
diff --git a/.github/workflows/integration-test-mgr-suite.yaml b/.github/workflows/integration-test-mgr-suite.yaml
index 68a56d486..916feff75 100644
--- a/.github/workflows/integration-test-mgr-suite.yaml
+++ b/.github/workflows/integration-test-mgr-suite.yaml
@@ -30,7 +30,7 @@ jobs:
         kubernetes-versions: ["v1.31.0"]
     steps:
       - name: checkout
-        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+        uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
         with:
           fetch-depth: 0
 
@@ -38,7 +38,6 @@ jobs:
         uses: ./.github/workflows/tmate_debug
         with:
           use-tmate: ${{ secrets.USE_TMATE }}
-          debug-ci: ${{ contains(github.event.pull_request.labels.*.name, 'debug-ci') }}
 
       - name: setup cluster resources
         uses: ./.github/workflows/integration-test-config-latest-k8s
diff --git a/.github/workflows/integration-test-multi-cluster-suite.yaml b/.github/workflows/integration-test-multi-cluster-suite.yaml
index 05233cf08..cc1f24252 100644
--- a/.github/workflows/integration-test-multi-cluster-suite.yaml
+++ b/.github/workflows/integration-test-multi-cluster-suite.yaml
@@ -31,7 +31,7 @@ jobs:
         kubernetes-versions: ["v1.31.0"]
     steps:
       - name: checkout
-        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+        uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
         with:
           fetch-depth: 0
 
@@ -39,7 +39,6 @@ jobs:
         uses: ./.github/workflows/tmate_debug
         with:
           use-tmate: ${{ secrets.USE_TMATE }}
-          debug-ci: ${{ contains(github.event.pull_request.labels.*.name, 'debug-ci') }}
 
       - name: setup cluster resources
         uses: ./.github/workflows/integration-test-config-latest-k8s
diff --git a/.github/workflows/integration-test-object-suite.yaml b/.github/workflows/integration-test-object-suite.yaml
index fa13bcd91..d3980eed0 100644
--- a/.github/workflows/integration-test-object-suite.yaml
+++ b/.github/workflows/integration-test-object-suite.yaml
@@ -31,7 +31,7 @@ jobs:
         kubernetes-versions: ["v1.26.15", "v1.31.0"]
     steps:
       - name: checkout
-        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+        uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
         with:
           fetch-depth: 0
 
@@ -39,7 +39,6 @@ jobs:
         uses: ./.github/workflows/tmate_debug
         with:
           use-tmate: ${{ secrets.USE_TMATE }}
-          debug-ci: ${{ contains(github.event.pull_request.labels.*.name, 'debug-ci') }}
 
       - name: setup latest cluster resources
         uses: ./.github/workflows/integration-test-config-latest-k8s
diff --git a/.github/workflows/integration-test-smoke-suite.yaml b/.github/workflows/integration-test-smoke-suite.yaml
index 5712ed754..d64bcdf01 100644
--- a/.github/workflows/integration-test-smoke-suite.yaml
+++ b/.github/workflows/integration-test-smoke-suite.yaml
@@ -31,7 +31,7 @@ jobs:
         kubernetes-versions: ["v1.26.15", "v1.31.0"]
     steps:
       - name: checkout
-        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+        uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
         with:
           fetch-depth: 0
 
@@ -39,7 +39,6 @@ jobs:
         uses: ./.github/workflows/tmate_debug
         with:
           use-tmate: ${{ secrets.USE_TMATE }}
-          debug-ci: ${{ contains(github.event.pull_request.labels.*.name, 'debug-ci') }}
 
       - name: setup cluster resources
         uses: ./.github/workflows/integration-test-config-latest-k8s
diff --git a/.github/workflows/integration-test-upgrade-suite.yaml b/.github/workflows/integration-test-upgrade-suite.yaml
index c4c384e87..99051c6f1 100644
--- a/.github/workflows/integration-test-upgrade-suite.yaml
+++ b/.github/workflows/integration-test-upgrade-suite.yaml
@@ -31,7 +31,7 @@ jobs:
         kubernetes-versions: ["v1.26.15", "v1.31.0"]
     steps:
       - name: checkout
-        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+        uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
         with:
           fetch-depth: 0
 
@@ -39,7 +39,6 @@ jobs:
         uses: ./.github/workflows/tmate_debug
         with:
           use-tmate: ${{ secrets.USE_TMATE }}
-          debug-ci: ${{ contains(github.event.pull_request.labels.*.name, 'debug-ci') }}
 
       - name: setup cluster resources
         uses: ./.github/workflows/integration-test-config-latest-k8s
@@ -76,7 +75,7 @@ jobs:
         kubernetes-versions: ["v1.26.15", "v1.31.0"]
     steps:
       - name: checkout
-        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+        uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
         with:
           fetch-depth: 0
 
@@ -84,7 +83,6 @@ jobs:
         uses: ./.github/workflows/tmate_debug
         with:
           use-tmate: ${{ secrets.USE_TMATE }}
-          debug-ci: ${{ contains(github.event.pull_request.labels.*.name, 'debug-ci') }}
 
       - name: setup cluster resources
         uses: ./.github/workflows/integration-test-config-latest-k8s
diff --git a/.github/workflows/integration-tests-on-release.yaml b/.github/workflows/integration-tests-on-release.yaml
index e4fb4c5c3..32bd9039d 100644
--- a/.github/workflows/integration-tests-on-release.yaml
+++ b/.github/workflows/integration-tests-on-release.yaml
@@ -24,7 +24,7 @@ jobs:
         kubernetes-versions: ["v1.26.15", "v1.28.12", "v1.29.7", "v1.31.0"]
     steps:
       - name: checkout
-        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+        uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
         with:
           fetch-depth: 0
 
@@ -64,7 +64,7 @@ jobs:
         kubernetes-versions: ["v1.26.15", "v1.28.12", "v1.29.7", "v1.31.0"]
     steps:
       - name: checkout
-        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+        uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
         with:
           fetch-depth: 0
 
@@ -105,7 +105,7 @@ jobs:
         kubernetes-versions: ["v1.26.15", "v1.28.12", "v1.29.7", "v1.31.0"]
     steps:
       - name: checkout
-        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+        uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
         with:
           fetch-depth: 0
 
@@ -143,7 +143,7 @@ jobs:
         kubernetes-versions: ["v1.26.15", "v1.28.12", "v1.29.7", "v1.31.0"]
     steps:
       - name: checkout
-        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+        uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
         with:
           fetch-depth: 0
 
@@ -181,7 +181,7 @@ jobs:
         kubernetes-versions: ["v1.26.15", "v1.28.12", "v1.29.7", "v1.31.0"]
     steps:
       - name: checkout
-        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+        uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
         with:
           fetch-depth: 0
 
@@ -222,7 +222,7 @@ jobs:
         kubernetes-versions: ["v1.26.15", "v1.31.0"]
     steps:
       - name: checkout
-        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+        uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
         with:
           fetch-depth: 0
 
diff --git a/.github/workflows/unit-test.yml b/.github/workflows/unit-test.yml
index 704862473..3167fd743 100644
--- a/.github/workflows/unit-test.yml
+++ b/.github/workflows/unit-test.yml
@@ -37,11 +37,11 @@ jobs:
     if: "!contains(github.event.pull_request.labels.*.name, 'skip-ci')"
     steps:
       - name: checkout
-        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+        uses: actions/checkout@eef61447b9ff4aafe5dcd4e0bbf5d482be7e7871 # v4.2.1
         with:
           fetch-depth: 0
 
-      - uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed # v5.1.0
+      - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2
         with:
           go-version: "1.22"
 
diff --git a/pkg/daemon/ceph/client/filesystem_test.go b/pkg/daemon/ceph/client/filesystem_test.go
index 2fcb89992..e23a84128 100644
--- a/pkg/daemon/ceph/client/filesystem_test.go
+++ b/pkg/daemon/ceph/client/filesystem_test.go
@@ -520,33 +520,9 @@ func TestGetMDSDump(t *testing.T) {
 	assert.Error(t, err)
 }
 
-func TestFSHasStandby(t *testing.T) {
-	// Not found in an empty list
-	fsName := "foo"
-	dump := &MDSDump{}
-	assert.False(t, filesystemHasStandby(dump, fsName))
-
-	// Not found with a non-matching name
-	dump = &MDSDump{Standbys: []MDSStandBy{{Name: "foo-cd"}}}
-	assert.False(t, filesystemHasStandby(dump, fsName))
-
-	// Found in the list with -a suffix
-	dump = &MDSDump{Standbys: []MDSStandBy{{Name: "foo-a"}}}
-	assert.True(t, filesystemHasStandby(dump, fsName))
-
-	// Found in the list with -c suffix
-	dump = &MDSDump{Standbys: []MDSStandBy{{Name: "foo-c"}}}
-	assert.True(t, filesystemHasStandby(dump, fsName))
-
-	// Does not match another filesystem
-	fsName = "bar"
-	assert.False(t, filesystemHasStandby(dump, fsName))
-}
-
 func TestWaitForNoStandbys(t *testing.T) {
 	executor := &exectest.MockExecutor{}
 	context := &clusterd.Context{Executor: executor}
-	fsName := "rook-ceph-filesystem"
 	executor.MockExecuteCommandWithOutput = func(command string, args ...string) (string, error) {
 		logger.Infof("Command: %s %v", command, args)
 		if args[0] == "fs" {
@@ -566,7 +542,7 @@ func TestWaitForNoStandbys(t *testing.T) {
 		return "", errors.Errorf("unexpected ceph command %q", args)
 	}
 
-	err := WaitForNoStandbys(context, AdminTestClusterInfo("mycluster"), fsName, time.Millisecond, 5*time.Millisecond)
+	err := WaitForNoStandbys(context, AdminTestClusterInfo("mycluster"), time.Millisecond, 5*time.Millisecond)
 	assert.Error(t, err)
 
 	executor.MockExecuteCommandWithOutput = func(command string, args ...string) (string, error) {
@@ -579,7 +555,7 @@ func TestWaitForNoStandbys(t *testing.T) {
 		return "", errors.Errorf("unexpected ceph command %q", args)
 	}
 
-	err = WaitForNoStandbys(context, AdminTestClusterInfo("mycluster"), fsName, time.Millisecond, 5*time.Millisecond)
+	err = WaitForNoStandbys(context, AdminTestClusterInfo("mycluster"), time.Millisecond, 5*time.Millisecond)
 	assert.Error(t, err)
 
 	firstCall := true
@@ -606,7 +582,7 @@ func TestWaitForNoStandbys(t *testing.T) {
 		}
 		return "", errors.Errorf("unexpected ceph command %q", args)
 	}
-	err = WaitForNoStandbys(context, AdminTestClusterInfo("mycluster"), fsName, time.Millisecond, 5*time.Millisecond)
+	err = WaitForNoStandbys(context, AdminTestClusterInfo("mycluster"), time.Millisecond, 5*time.Millisecond)
 	assert.NoError(t, err)
 }
 
diff --git a/pkg/operator/ceph/controller/network_test.go b/pkg/operator/ceph/controller/network_test.go
index afb967b19..8cc5beab1 100644
--- a/pkg/operator/ceph/controller/network_test.go
+++ b/pkg/operator/ceph/controller/network_test.go
@@ -54,9 +54,9 @@ func (m *mockCmdReporter) Run(ctx context.Context, timeout time.Duration) (stdou
 }
 
 // returns a newCmdReporter function that returns the given mockCmdReporter and error
-func mockNewCmdReporter(m *mockCmdReporter, err error) func(clientset kubernetes.Interface, ownerInfo *k8sutil.OwnerInfo, appName string, jobName string, jobNamespace string, cmd []string, args []string, rookImage string, runImage string, imagePullPolicy v1.PullPolicy, resources cephv1.ResourceSpec) (cmdreporter.CmdReporterInterface, error) {
-	return func(clientset kubernetes.Interface, ownerInfo *k8sutil.OwnerInfo, appName, jobName, jobNamespace string, cmd, args []string, rookImage, runImage string, imagePullPolicy v1.PullPolicy, resources cephv1.ResourceSpec) (cmdreporter.CmdReporterInterface, error) {
-		job, err := cmdreporter.MockCmdReporterJob(clientset, ownerInfo, appName, jobName, jobNamespace, cmd, args, rookImage, runImage, imagePullPolicy, resources)
+func mockNewCmdReporter(m *mockCmdReporter, err error) func(clientset kubernetes.Interface, ownerInfo *k8sutil.OwnerInfo, appName string, jobName string, jobNamespace string, cmd []string, args []string, rookImage string, runImage string, imagePullPolicy v1.PullPolicy) (cmdreporter.CmdReporterInterface, error) {
+	return func(clientset kubernetes.Interface, ownerInfo *k8sutil.OwnerInfo, appName, jobName, jobNamespace string, cmd, args []string, rookImage, runImage string, imagePullPolicy v1.PullPolicy) (cmdreporter.CmdReporterInterface, error) {
+		job, err := cmdreporter.MockCmdReporterJob(clientset, ownerInfo, appName, jobName, jobNamespace, cmd, args, rookImage, runImage, imagePullPolicy)
 		if err != nil {
 			// okay to panic here because this is a unit test setup failure, not part of code testing
 			panic(fmt.Sprintf("error setting up mock CmdReporter job: %v", err))
@@ -82,6 +82,7 @@ func newTestConfigsWithNetworkSpec(n cephv1.NetworkSpec) (*clusterd.Context, *ce
 func Test_discoverAddressRanges(t *testing.T) {
 	oldNewCmdReporter := newCmdReporter
 	defer func() { newCmdReporter = oldNewCmdReporter }()
+
 	t.Run("public network selected", func(t *testing.T) {
 		clusterdCtx, clusterSpec, clusterInfo := newTestConfigsWithNetworkSpec(
 			cephv1.NetworkSpec{
diff --git a/pkg/operator/ceph/csi/controller_test.go b/pkg/operator/ceph/csi/controller_test.go
index 2df439cef..b1998bab4 100644
--- a/pkg/operator/ceph/csi/controller_test.go
+++ b/pkg/operator/ceph/csi/controller_test.go
@@ -61,6 +61,7 @@ func TestCephCSIController(t *testing.T) {
 	t.Setenv(k8sutil.PodNameEnvVar, "rook-ceph-operator")
 	t.Setenv(k8sutil.PodNamespaceEnvVar, namespace)
 
+	t.Setenv("ROOK_CSI_ALLOW_UNSUPPORTED_VERSION", "true")
 	CSIParam = Param{
 		CSIPluginImage:   "image",
 		RegistrarImage:   "image",
diff --git a/pkg/operator/ceph/csi/version_test.go b/pkg/operator/ceph/csi/version_test.go
new file mode 100644
index 000000000..7bd518c6b
--- /dev/null
+++ b/pkg/operator/ceph/csi/version_test.go
@@ -0,0 +1,129 @@
+/*
+Copyright 2020 The Rook Authors. All rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+	http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package csi
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+var (
+	testMinVersion   = CephCSIVersion{3, 11, 0}
+	testReleaseV390  = CephCSIVersion{3, 9, 0}
+	testReleaseV391  = CephCSIVersion{3, 9, 1}
+	testreleasev310  = CephCSIVersion{3, 10, 0}
+	testReleaseV3101 = CephCSIVersion{3, 10, 1}
+	testReleaseV3102 = CephCSIVersion{3, 10, 2}
+	testReleaseV3110 = CephCSIVersion{3, 11, 0}
+	testReleaseV3120 = CephCSIVersion{3, 12, 0}
+
+	testVersionUnsupported = CephCSIVersion{4, 0, 0}
+)
+
+func TestIsAtLeast(t *testing.T) {
+	// Test version which is smaller
+	var version = CephCSIVersion{1, 40, 10}
+	ret := testMinVersion.isAtLeast(&version)
+	assert.Equal(t, true, ret)
+
+	// Test version which is equal
+	ret = testMinVersion.isAtLeast(&testMinVersion)
+	assert.Equal(t, true, ret)
+
+	// Test for 3.9.0
+	ret = testReleaseV390.isAtLeast(&testMinVersion)
+	assert.Equal(t, false, ret)
+
+	// Test for 3.9.1
+	ret = testReleaseV391.isAtLeast(&testMinVersion)
+	assert.Equal(t, false, ret)
+
+	// Test for 3.10.0
+	ret = testreleasev310.isAtLeast(&testReleaseV390)
+	assert.Equal(t, true, ret)
+
+	// Test for 3.10.1
+	ret = testReleaseV3101.isAtLeast(&testReleaseV3101)
+	assert.Equal(t, true, ret)
+
+	// Test for 3.10.2
+	ret = testReleaseV3102.isAtLeast(&testReleaseV3102)
+	assert.Equal(t, true, ret)
+
+	// Test for 3.11.0
+	ret = testReleaseV3110.isAtLeast(&testReleaseV3110)
+	assert.Equal(t, true, ret)
+
+	// Test for 3.12.0
+	ret = testReleaseV3120.isAtLeast(&testReleaseV3120)
+	assert.Equal(t, true, ret)
+
+}
+
+func TestSupported(t *testing.T) {
+	AllowUnsupported = false
+	ret := testMinVersion.Supported()
+	assert.Equal(t, true, ret)
+
+	ret = testVersionUnsupported.Supported()
+	assert.Equal(t, false, ret)
+
+	// 3.9.x is not supported after 3.11.0 release
+	ret = testReleaseV390.Supported()
+	assert.Equal(t, false, ret)
+
+	ret = testReleaseV391.Supported()
+	assert.Equal(t, false, ret)
+
+	ret = testreleasev310.Supported()
+	assert.Equal(t, false, ret)
+
+	ret = testReleaseV3101.Supported()
+	assert.Equal(t, false, ret)
+
+	ret = testReleaseV3110.Supported()
+	assert.Equal(t, true, ret)
+
+	ret = testReleaseV3120.Supported()
+	assert.Equal(t, true, ret)
+}
+
+func Test_extractCephCSIVersion(t *testing.T) {
+	expectedVersion := CephCSIVersion{3, 0, 0}
+	csiString := []byte(`Cephcsi Version: v3.0.0
+		Git Commit: e58d537a07ca0184f67d33db85bf6b4911624b44
+		Go Version: go1.12.15
+		Compiler: gc
+		Platform: linux/amd64
+		`)
+	version, err := extractCephCSIVersion(string(csiString))
+
+	assert.Equal(t, &expectedVersion, version)
+	assert.Nil(t, err)
+
+	csiString = []byte(`Cephcsi Version: rubbish
+	Git Commit: e58d537a07ca0184f67d33db85bf6b4911624b44
+	Go Version: go1.12.15
+	Compiler: gc
+	Platform: linux/amd64
+	`)
+	version, err = extractCephCSIVersion(string(csiString))
+
+	assert.Nil(t, version)
+	assert.Contains(t, err.Error(), "failed to parse version from")
+}
diff --git a/pkg/operator/ceph/object/objectstore_test.go b/pkg/operator/ceph/object/objectstore_test.go
index ee5aa4c83..f384d6c70 100644
--- a/pkg/operator/ceph/object/objectstore_test.go
+++ b/pkg/operator/ceph/object/objectstore_test.go
@@ -152,7 +152,7 @@ const (
   "notif_pool": "%[1]s:store-a.log.notif"
 }`
 
-	objectZonegroupJsonTempl = `{
+	objectZonegroupJson = `{
     "id": "610c9e3d-19e7-40b0-9f88-03319c4bc65a",
     "name": "store-a",
     "api_name": "test",
@@ -174,14 +174,14 @@ const (
     ],
     "placement_targets": [
         {
-            "name": "%[1]s",
+            "name": "default-placement",
             "tags": [],
             "storage_classes": [
                 "STANDARD"
             ]
         }
     ],
-    "default_placement": "%[1]s",
+    "default_placement": "default-placement",
     "realm_id": "29e28253-be54-4581-90dd-206020d2fcdd",
     "sync_policy": {
         "groups": []
@@ -229,7 +229,6 @@ func TestReconcileRealm(t *testing.T) {
 
 func TestConfigureStoreWithSharedPools(t *testing.T) {
 	sharedMetaPoolAlreadySet, sharedDataPoolAlreadySet := "", ""
-	defaultPlacement := defaultPlacementCephConfigName
 	zoneGetCalled := false
 	zoneSetCalled := false
 	zoneGroupGetCalled := false
@@ -274,7 +273,7 @@ func TestConfigureStoreWithSharedPools(t *testing.T) {
 		} else if args[0] == "zonegroup" {
 			if args[1] == "get" {
 				zoneGroupGetCalled = true
-				return fmt.Sprintf(objectZonegroupJsonTempl, defaultPlacement), nil
+				return objectZonegroupJson, nil
 			} else if args[1] == "set" {
 				zoneGroupSetCalled = true
 				for _, arg := range args {
@@ -288,7 +287,7 @@ func TestConfigureStoreWithSharedPools(t *testing.T) {
 					}
 					return string(inBytes), nil
 				}
-				return fmt.Sprintf(objectZonegroupJsonTempl, defaultPlacement), nil
+				return objectZonegroupJson, nil
 			}
 		}
 		return "", errors.Errorf("unexpected ceph command %q", args)
@@ -331,7 +330,7 @@ func TestConfigureStoreWithSharedPools(t *testing.T) {
 		assert.True(t, zoneGroupGetCalled)
 		assert.False(t, zoneGroupSetCalled) // zone group is set only if extra pool placements specified
 	})
-	t.Run("configure with default placement", func(t *testing.T) {
+	t.Run("configure with new default placement", func(t *testing.T) {
 		sharedPools := cephv1.ObjectSharedPoolsSpec{
 			PoolPlacements: []cephv1.PoolPlacementSpec{
 				{
@@ -342,22 +341,22 @@ func TestConfigureStoreWithSharedPools(t *testing.T) {
 				},
 			},
 		}
-		defaultPlacement = "default"
 		err := ConfigureSharedPoolsForZone(context, sharedPools)
 		assert.NoError(t, err)
 		assert.True(t, zoneGetCalled)
 		assert.True(t, zoneSetCalled)
 		assert.False(t, placementModifyCalled) // mock returns applied namespases, no workaround needed
 		assert.True(t, zoneGroupGetCalled)
-		assert.False(t, zoneGroupSetCalled) // zone group is set only if extra pool placements specified
+		assert.True(t, zoneGroupSetCalled)
 	})
 	t.Run("data pool already set", func(t *testing.T) {
+		// reset
+		zoneGroupSetCalled = false
 		// Simulate that the data pool has already been set and the zone update can be skipped
 		sharedPools := cephv1.ObjectSharedPoolsSpec{
 			MetadataPoolName: "test-meta",
 			DataPoolName:     "test-data",
 		}
-		defaultPlacement = defaultPlacementCephConfigName
 		sharedMetaPoolAlreadySet, sharedDataPoolAlreadySet = "test-meta", "test-data"
 		zoneGetCalled = false
 		zoneSetCalled = false
diff --git a/pkg/operator/ceph/object/shared_pools_test.go b/pkg/operator/ceph/object/shared_pools_test.go
index f40306141..81f26b799 100644
--- a/pkg/operator/ceph/object/shared_pools_test.go
+++ b/pkg/operator/ceph/object/shared_pools_test.go
@@ -630,7 +630,7 @@ func Test_toZonePlacementPools(t *testing.T) {
 			},
 		},
 		{
-			name: "default placement overrides shared pools",
+			name: "shared pools not removed if default placement set",
 			args: args{
 				spec: cephv1.ObjectSharedPoolsSpec{
 					MetadataPoolName:                   "meta",
@@ -655,6 +655,19 @@ func Test_toZonePlacementPools(t *testing.T) {
 				ns: "rgw-instance",
 			},
 			want: map[string]ZonePlacementPool{
+				defaultPlacementCephConfigName: {
+					Key: defaultPlacementCephConfigName,
+					Val: ZonePlacementPoolVal{
+						DataExtraPool: "meta:rgw-instance.buckets.non-ec",
+						IndexPool:     "meta:rgw-instance.buckets.index",
+						StorageClasses: map[string]ZonePlacementStorageClass{
+							"STANDARD": {
+								DataPool: "data:rgw-instance.buckets.data",
+							},
+						},
+						InlineData: true,
+					},
+				},
 				"some_name": {
 					Key: "some_name",
 					Val: ZonePlacementPoolVal{
@@ -1444,6 +1457,156 @@ func Test_adjustZonePlacementPools(t *testing.T) {
             }
         }
     ]
+}`,
+			wantChanged: true,
+			wantErr:     false,
+		},
+		{
+			name: "sharedPools placement not removed if default set",
+			args: args{
+				beforeJSON: `{
+    "id": "f539c2c0-e1ed-4c42-9294-41742352eeae",
+    "name": "test",
+    "placement_pools": [
+        {
+            "key": "default-placement",
+            "val": {
+                "index_pool": "test.rgw.buckets.index",
+                "storage_classes": {
+                    "STANDARD": {
+                        "data_pool": "test.rgw.buckets.data"
+                    }
+                },
+                "data_extra_pool": "test.rgw.buckets.non-ec",
+                "index_type": 5,
+                "inline_data": true
+            }
+        }
+    ]
+}`,
+				spec: cephv1.ObjectSharedPoolsSpec{
+					MetadataPoolName:                   "meta-pool",
+					DataPoolName:                       "data-pool",
+					PreserveRadosNamespaceDataOnDelete: false,
+					PoolPlacements: []cephv1.PoolPlacementSpec{
+						{
+							Name:              "slow",
+							Default:           true,
+							MetadataPoolName:  "slow-meta",
+							DataPoolName:      "slow-data",
+							DataNonECPoolName: "slow-non-ec",
+							StorageClasses:    []cephv1.PlacementStorageClassSpec{},
+						},
+					},
+				},
+			},
+			wantJSON: `{
+    "id": "f539c2c0-e1ed-4c42-9294-41742352eeae",
+    "name": "test",
+    "placement_pools": [
+        {
+            "key": "default-placement",
+            "val": {
+                "index_pool": "meta-pool:test.buckets.index",
+                "storage_classes": {
+                    "STANDARD": {
+                        "data_pool": "data-pool:test.buckets.data"
+                    }
+                },
+                "data_extra_pool": "meta-pool:test.buckets.non-ec",
+                "index_type": 5,
+                "inline_data": true
+            }
+        },
+        {
+            "key": "slow",
+            "val": {
+                "index_pool": "slow-meta:test.slow.index",
+                "storage_classes": {
+                    "STANDARD": {
+                        "data_pool": "slow-data:test.slow.data"
+                    }
+                },
+                "data_extra_pool": "slow-non-ec:test.slow.data.non-ec",
+                "inline_data": true
+            }
+        }
+    ]
+}`,
+			wantChanged: true,
+			wantErr:     false,
+		},
+		{
+			name: "if sharedPools not set and default placement is set, then default placements values are copied to 'default-placement'",
+			args: args{
+				beforeJSON: `{
+    "id": "f539c2c0-e1ed-4c42-9294-41742352eeae",
+    "name": "test",
+    "placement_pools": [
+        {
+            "key": "default-placement",
+            "val": {
+                "index_pool": "test.rgw.buckets.index",
+                "storage_classes": {
+                    "STANDARD": {
+                        "data_pool": "test.rgw.buckets.data"
+                    }
+                },
+                "data_extra_pool": "test.rgw.buckets.non-ec",
+                "index_type": 5,
+                "inline_data": true
+            }
+        }
+    ]
+}`,
+				spec: cephv1.ObjectSharedPoolsSpec{
+					MetadataPoolName:                   "",
+					DataPoolName:                       "",
+					PreserveRadosNamespaceDataOnDelete: false,
+					PoolPlacements: []cephv1.PoolPlacementSpec{
+						{
+							Name:              "slow",
+							Default:           true,
+							MetadataPoolName:  "slow-meta",
+							DataPoolName:      "slow-data",
+							DataNonECPoolName: "slow-non-ec",
+							StorageClasses:    []cephv1.PlacementStorageClassSpec{},
+						},
+					},
+				},
+			},
+			wantJSON: `{
+    "id": "f539c2c0-e1ed-4c42-9294-41742352eeae",
+    "name": "test",
+    "placement_pools": [
+        {
+            "key": "default-placement",
+            "val": {
+                "index_pool": "slow-meta:test.slow.index",
+                "storage_classes": {
+                    "STANDARD": {
+                        "data_pool": "slow-data:test.slow.data"
+                    }
+                },
+                "data_extra_pool": "slow-non-ec:test.slow.data.non-ec",
+                "index_type": 5,
+                "inline_data": true
+            }
+        },
+        {
+            "key": "slow",
+            "val": {
+                "index_pool": "slow-meta:test.slow.index",
+                "storage_classes": {
+                    "STANDARD": {
+                        "data_pool": "slow-data:test.slow.data"
+                    }
+                },
+                "data_extra_pool": "slow-non-ec:test.slow.data.non-ec",
+                "inline_data": true
+            }
+        }
+    ]
 }`,
 			wantChanged: true,
 			wantErr:     false,
diff --git a/pkg/operator/k8sutil/node_test.go b/pkg/operator/k8sutil/node_test.go
index c3cff521d..b92b3dd8c 100644
--- a/pkg/operator/k8sutil/node_test.go
+++ b/pkg/operator/k8sutil/node_test.go
@@ -25,7 +25,6 @@ import (
 	cephv1 "github.com/rook/rook/pkg/apis/ceph.rook.io/v1"
 	optest "github.com/rook/rook/pkg/operator/test"
 	"github.com/stretchr/testify/assert"
-	"github.com/stretchr/testify/require"
 	v1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/client-go/kubernetes/fake"
@@ -75,17 +74,8 @@ func TestValidNode(t *testing.T) {
 	t.Run("test valid node", func(t *testing.T) {
 		var placement cephv1.Placement
 		validNodes := GetValidNodes(context.TODO(), storage, clientset, placement)
-		assert.Equal(t, 1, len(validNodes))
-		assert.Equal(t, "nodeA", validNodes[0].Name)
-	})
-
-	t.Run("test nodes always valid", func(t *testing.T) {
-		var placement cephv1.Placement
-		storage.ScheduleAlways = true
-		validNodes := GetValidNodes(context.TODO(), storage, clientset, placement)
-		require.Equal(t, 2, len(validNodes))
+		assert.Equal(t, len(validNodes), 1)
 		assert.Equal(t, "nodeA", validNodes[0].Name)
-		assert.Equal(t, "nodeB", validNodes[1].Name)
 	})
 
 	t.Run("test placement", func(t *testing.T) {
@@ -448,12 +438,12 @@ func TestGenerateNodeAffinity(t *testing.T) {
 			name: "GenerateNodeAffinityWithYAMLInputUsingDoesNotExistOperator",
 			args: args{
 				nodeAffinity: `
----
-requiredDuringSchedulingIgnoredDuringExecution:
-  nodeSelectorTerms:
-    -
-      matchExpressions:
-        -
+--- 
+requiredDuringSchedulingIgnoredDuringExecution: 
+  nodeSelectorTerms: 
+    - 
+      matchExpressions: 
+        - 
           key: myKey
           operator: DoesNotExist`,
 			},
@@ -477,15 +467,15 @@ requiredDuringSchedulingIgnoredDuringExecution:
 			name: "GenerateNodeAffinityWithYAMLInputUsingNotInOperator",
 			args: args{
 				nodeAffinity: `
----
-requiredDuringSchedulingIgnoredDuringExecution:
-  nodeSelectorTerms:
-    -
-      matchExpressions:
-        -
+--- 
+requiredDuringSchedulingIgnoredDuringExecution: 
+  nodeSelectorTerms: 
+    - 
+      matchExpressions: 
+        - 
           key: myKey
           operator: NotIn
-          values:
+          values: 
             - myValue`,
 			},
 			want: &v1.NodeAffinity{
diff --git a/tests/framework/installer/ceph_installer.go b/tests/framework/installer/ceph_installer.go
index 42adcebe7..0e2287190 100644
--- a/tests/framework/installer/ceph_installer.go
+++ b/tests/framework/installer/ceph_installer.go
@@ -98,8 +98,7 @@ func ReturnCephVersion() cephv1.CephVersionSpec {
 	case "squid-devel":
 		return SquidDevelVersion
 	default:
-		// Default to the latest stable version
-		return SquidVersion
+		return SquidDevelVersion
 	}
 }
 
diff --git a/tests/integration/ceph_base_object_test.go b/tests/integration/ceph_base_object_test.go
index 79521d1ec..6358935aa 100644
--- a/tests/integration/ceph_base_object_test.go
+++ b/tests/integration/ceph_base_object_test.go
@@ -238,7 +238,8 @@ func assertObjectStoreDeletion(t *testing.T, k8sh *utils.K8sHelper, namespace, s
 
 func createCephObjectUser(
 	s *suite.Suite, helper *clients.TestClient, k8sh *utils.K8sHelper,
-	namespace, storeName, userID string, checkQuotaAndCaps bool) {
+	namespace, storeName, userID string,
+	checkPhase, checkQuotaAndCaps bool) {
 
 	maxObjectInt, err := strconv.Atoi(maxObject)
 	assert.Nil(s.T(), err)
@@ -253,13 +254,14 @@ func createCephObjectUser(
 		time.Sleep(5 * time.Second)
 	}
 
-	checkCephObjectUser(s, helper, k8sh, namespace, storeName, userID, checkQuotaAndCaps)
+	checkCephObjectUser(s, helper, k8sh, namespace, storeName, userID, checkPhase, checkQuotaAndCaps)
 }
 
 func checkCephObjectUser(
 	s *suite.Suite, helper *clients.TestClient, k8sh *utils.K8sHelper,
-	namespace, storeName, userID string, checkQuotaAndCaps bool) {
-
+	namespace, storeName, userID string,
+	checkPhase, checkQuotaAndCaps bool,
+) {
 	logger.Infof("checking object store \"%s/%s\" user %q", namespace, storeName, userID)
 	assert.True(s.T(), helper.ObjectUserClient.UserSecretExists(namespace, storeName, userID))
 
@@ -268,9 +270,23 @@ func checkCephObjectUser(
 	assert.Equal(s.T(), userID, userInfo.UserID)
 	assert.Equal(s.T(), userdisplayname, *userInfo.DisplayName)
 
-	phase, err := k8sh.GetResource("--namespace", namespace, "cephobjectstoreuser", userID, "--output", "jsonpath={.status.phase}")
-	assert.NoError(s.T(), err)
-	assert.Equal(s.T(), k8sutil.ReadyStatus, phase)
+	if checkPhase {
+		// status.phase doesn't exist before Rook v1.6
+		phase, err := k8sh.GetResource("--namespace", namespace, "cephobjectstoreuser", userID, "--output", "jsonpath={.status.phase}")
+		assert.NoError(s.T(), err)
+		assert.Equal(s.T(), k8sutil.ReadyStatus, phase)
+	}
+	if checkQuotaAndCaps {
+		// following fields in CephObjectStoreUser CRD doesn't exist before Rook v1.7.3
+		maxObjectInt, err := strconv.Atoi(maxObject)
+		assert.Nil(s.T(), err)
+		maxSizeInt, err := strconv.Atoi(maxSize)
+		assert.Nil(s.T(), err)
+		assert.Equal(s.T(), maxBucket, userInfo.MaxBuckets)
+		assert.Equal(s.T(), int64(maxObjectInt), *userInfo.UserQuota.MaxObjects)
+		assert.Equal(s.T(), int64(maxSizeInt), *userInfo.UserQuota.MaxSize)
+		assert.Equal(s.T(), userCap, userInfo.Caps[0].Perm)
+	}
 }
 
 func objectStoreCleanUp(s *suite.Suite, helper *clients.TestClient, k8sh *utils.K8sHelper, namespace, storeName string) {
diff --git a/tests/integration/ceph_object_test.go b/tests/integration/ceph_object_test.go
index 3763a3c1e..db55cd39e 100644
--- a/tests/integration/ceph_object_test.go
+++ b/tests/integration/ceph_object_test.go
@@ -167,7 +167,7 @@ func testObjectStoreOperations(s *suite.Suite, helper *clients.TestClient, k8sh
 
 	logger.Infof("Testing Object Operations on %s", storeName)
 	t.Run("create CephObjectStoreUser", func(t *testing.T) {
-		createCephObjectUser(s, helper, k8sh, namespace, storeName, userid, true)
+		createCephObjectUser(s, helper, k8sh, namespace, storeName, userid, true, true)
 		i := 0
 		for i = 0; i < 4; i++ {
 			if helper.ObjectUserClient.UserSecretExists(namespace, storeName, userid) {
diff --git a/tests/integration/ceph_upgrade_test.go b/tests/integration/ceph_upgrade_test.go
index 849ad06eb..04ee2b4bb 100644
--- a/tests/integration/ceph_upgrade_test.go
+++ b/tests/integration/ceph_upgrade_test.go
@@ -145,7 +145,7 @@ func (s *UpgradeSuite) testUpgrade(useHelm bool, initialCephVersion v1.CephVersi
 	rbdFilesToRead = append(rbdFilesToRead, newFile)
 	cephfsFilesToRead = append(cephfsFilesToRead, newFile)
 
-	checkCephObjectUser(&s.Suite, s.helper, s.k8sh, s.namespace, installer.ObjectStoreName, objectUserID, false)
+	checkCephObjectUser(&s.Suite, s.helper, s.k8sh, s.namespace, installer.ObjectStoreName, objectUserID, true, false)
 
 	// should be Bound after upgrade to Rook master
 	// do not need retry b/c the OBC controller runs parallel to Rook-Ceph orchestration
@@ -169,7 +169,7 @@ func (s *UpgradeSuite) testUpgrade(useHelm bool, initialCephVersion v1.CephVersi
 	s.verifyFilesAfterUpgrade(newFile, rbdFilesToRead, cephfsFilesToRead)
 	logger.Infof("Verified upgrade from reef to squid")
 
-	checkCephObjectUser(&s.Suite, s.helper, s.k8sh, s.namespace, installer.ObjectStoreName, objectUserID, false)
+	checkCephObjectUser(&s.Suite, s.helper, s.k8sh, s.namespace, installer.ObjectStoreName, objectUserID, true, false)
 }
 
 func (s *UpgradeSuite) TestUpgradeCephToReefDevel() {
@@ -203,7 +203,7 @@ func (s *UpgradeSuite) TestUpgradeCephToReefDevel() {
 	s.verifyFilesAfterUpgrade(newFile, rbdFilesToRead, cephfsFilesToRead)
 	logger.Infof("verified upgrade from reef stable to reef devel")
 
-	checkCephObjectUser(&s.Suite, s.helper, s.k8sh, s.namespace, installer.ObjectStoreName, objectUserID, false)
+	checkCephObjectUser(&s.Suite, s.helper, s.k8sh, s.namespace, installer.ObjectStoreName, objectUserID, true, false)
 }
 
 func (s *UpgradeSuite) TestUpgradeCephToSquidDevel() {
@@ -237,7 +237,7 @@ func (s *UpgradeSuite) TestUpgradeCephToSquidDevel() {
 	s.verifyFilesAfterUpgrade(newFile, rbdFilesToRead, cephfsFilesToRead)
 	logger.Infof("verified upgrade from squid stable to squid devel")
 
-	checkCephObjectUser(&s.Suite, s.helper, s.k8sh, s.namespace, installer.ObjectStoreName, objectUserID, false)
+	checkCephObjectUser(&s.Suite, s.helper, s.k8sh, s.namespace, installer.ObjectStoreName, objectUserID, true, false)
 }
 
 func (s *UpgradeSuite) deployClusterforUpgrade(baseRookImage, objectUserID, preFilename string) (int, []string, []string) {
@@ -274,7 +274,7 @@ func (s *UpgradeSuite) deployClusterforUpgrade(baseRookImage, objectUserID, preF
 	}
 
 	logger.Infof("Initializing object user before the upgrade")
-	createCephObjectUser(&s.Suite, s.helper, s.k8sh, s.namespace, installer.ObjectStoreName, objectUserID, false)
+	createCephObjectUser(&s.Suite, s.helper, s.k8sh, s.namespace, installer.ObjectStoreName, objectUserID, false, false)
 
 	logger.Info("Initializing object bucket claim before the upgrade")
 	cobErr := s.helper.BucketClient.CreateBucketStorageClass(s.namespace, installer.ObjectStoreName, installer.ObjectStoreName, "Delete")