]> git.proxmox.com Git - ceph.git/commitdiff
import ceph 15.2.15
authorThomas Lamprecht <t.lamprecht@proxmox.com>
Thu, 21 Oct 2021 11:16:09 +0000 (13:16 +0200)
committerThomas Lamprecht <t.lamprecht@proxmox.com>
Thu, 21 Oct 2021 11:27:14 +0000 (13:27 +0200)
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
120 files changed:
ceph/.github/pull_request_template.md
ceph/CMakeLists.txt
ceph/alpine/APKBUILD
ceph/alpine/APKBUILD.in
ceph/ceph.spec
ceph/ceph.spec.in
ceph/changelog.upstream
ceph/cmake/modules/BuildBoost.cmake
ceph/debian/ceph-osd.install
ceph/debian/ceph-test.install
ceph/doc/ceph-volume/index.rst
ceph/doc/ceph-volume/lvm/index.rst
ceph/doc/ceph-volume/lvm/migrate.rst [new file with mode: 0644]
ceph/doc/ceph-volume/lvm/newdb.rst [new file with mode: 0644]
ceph/doc/ceph-volume/lvm/newwal.rst [new file with mode: 0644]
ceph/doc/man/8/ceph-volume.rst
ceph/doc/rados/operations/monitoring.rst
ceph/make-dist
ceph/monitoring/grafana/dashboards/ceph-cluster.json
ceph/monitoring/grafana/dashboards/host-details.json
ceph/monitoring/grafana/dashboards/osd-device-details.json
ceph/monitoring/grafana/dashboards/radosgw-detail.json
ceph/monitoring/prometheus/alerts/ceph_default_alerts.yml
ceph/monitoring/prometheus/alerts/test_alerts.yml
ceph/qa/distros/podman/centos_8.2_container_tools_3.0.yaml [new file with mode: 0644]
ceph/qa/distros/podman/centos_8.2_kubic_stable.yaml [deleted file]
ceph/qa/distros/podman/centos_8.3_container_tools_3.0.yaml [new file with mode: 0644]
ceph/qa/standalone/osd/osd-bluefs-volume-ops.sh
ceph/qa/suites/rados/cephadm/smoke/distro/centos_8.2_container_tools_3.0.yaml [new symlink]
ceph/qa/suites/rados/cephadm/smoke/distro/centos_8.2_kubic_stable.yaml [deleted symlink]
ceph/qa/suites/rados/cephadm/smoke/distro/rhel_8.3_kubic_stable.yaml [deleted symlink]
ceph/qa/suites/rados/cephadm/workunits/0-distro/centos_8.2_kubic_stable.yaml [deleted symlink]
ceph/qa/suites/rbd/mirror/workloads/rbd-mirror-bootstrap-workunit.yaml [deleted file]
ceph/qa/suites/rbd/mirror/workloads/rbd-mirror-journal-bootstrap-workunit.yaml [new file with mode: 0644]
ceph/qa/suites/rbd/mirror/workloads/rbd-mirror-snapshot-bootstrap-workunit.yaml [new file with mode: 0644]
ceph/qa/suites/upgrade/octopus-p2p/octopus-p2p-parallel/point-to-point-upgrade.yaml
ceph/qa/suites/upgrade/octopus-p2p/octopus-p2p-stress-split/1-ceph-install/octopus.yaml
ceph/qa/tasks/ceph_manager.py
ceph/qa/tasks/mgr/dashboard/test_rgw.py
ceph/qa/tasks/mgr/dashboard/test_user.py
ceph/qa/tasks/mgr/test_insights.py
ceph/qa/workunits/mon/test_mon_config_key.py
ceph/qa/workunits/rbd/rbd_mirror_bootstrap.sh
ceph/run-make-check.sh
ceph/src/.git_version
ceph/src/auth/KeyRing.cc
ceph/src/ceph-volume/CMakeLists.txt
ceph/src/ceph-volume/ceph_volume/api/lvm.py
ceph/src/ceph-volume/ceph_volume/devices/lvm/activate.py
ceph/src/ceph-volume/ceph_volume/devices/lvm/batch.py
ceph/src/ceph-volume/ceph_volume/devices/lvm/deactivate.py
ceph/src/ceph-volume/ceph_volume/devices/lvm/migrate.py
ceph/src/ceph-volume/ceph_volume/devices/raw/list.py
ceph/src/ceph-volume/ceph_volume/tests/conftest.py
ceph/src/ceph-volume/ceph_volume/tests/devices/lvm/test_activate.py
ceph/src/ceph-volume/ceph_volume/tests/devices/lvm/test_batch.py
ceph/src/ceph-volume/ceph_volume/tests/devices/lvm/test_migrate.py
ceph/src/ceph-volume/ceph_volume/tests/devices/raw/test_list.py [new file with mode: 0644]
ceph/src/ceph-volume/ceph_volume/tests/functional/lvm/centos8/bluestore/dmcrypt/test.yml
ceph/src/ceph-volume/ceph_volume/tests/functional/lvm/centos8/filestore/dmcrypt/test.yml
ceph/src/ceph-volume/ceph_volume/tests/functional/lvm/playbooks/test_bluestore.yml
ceph/src/ceph-volume/ceph_volume/tests/functional/lvm/playbooks/test_filestore.yml
ceph/src/ceph-volume/ceph_volume/tests/util/test_arg_validators.py
ceph/src/ceph-volume/ceph_volume/tests/util/test_device.py
ceph/src/ceph-volume/ceph_volume/util/device.py
ceph/src/ceph-volume/ceph_volume/util/disk.py
ceph/src/cephadm/cephadm
ceph/src/cls/rgw/cls_rgw.cc
ceph/src/cls/rgw/cls_rgw_types.h
ceph/src/common/buffer.cc
ceph/src/common/legacy_config_opts.h
ceph/src/common/options.cc
ceph/src/krbd.cc
ceph/src/mon/Monitor.cc
ceph/src/mon/OSDMonitor.cc
ceph/src/mon/OSDMonitor.h
ceph/src/mon/PGMap.cc
ceph/src/os/bluestore/BlueFS.cc
ceph/src/os/bluestore/BlueStore.cc
ceph/src/osd/OSD.cc
ceph/src/pybind/mgr/cephadm/module.py
ceph/src/pybind/mgr/dashboard/HACKING.rst
ceph/src/pybind/mgr/dashboard/ci/cephadm/bootstrap-cluster.sh
ceph/src/pybind/mgr/dashboard/ci/cephadm/run-cephadm-e2e-tests.sh
ceph/src/pybind/mgr/dashboard/ci/cephadm/start-cluster.sh
ceph/src/pybind/mgr/dashboard/controllers/rgw.py
ceph/src/pybind/mgr/influx/module.py
ceph/src/pybind/mgr/insights/module.py
ceph/src/pybind/mgr/prometheus/module.py
ceph/src/pybind/mgr/tests/__init__.py
ceph/src/pybind/rbd/rbd.pyx
ceph/src/rgw/rgw_op.cc
ceph/src/rgw/rgw_quota.cc
ceph/src/rgw/rgw_quota.h
ceph/src/rgw/rgw_rados.cc
ceph/src/rgw/rgw_rest_role.cc
ceph/src/rgw/rgw_rest_s3.cc
ceph/src/rgw/rgw_sts.cc
ceph/src/test/CMakeLists.txt
ceph/src/test/bufferlist.cc
ceph/src/test/ceph-erasure-code-tool/test_ceph-erasure-code-tool.sh [new file with mode: 0755]
ceph/src/test/cli-integration/rbd/unmap.t
ceph/src/test/erasure-code/CMakeLists.txt
ceph/src/test/erasure-code/ceph_erasure_code.cc [deleted file]
ceph/src/test/mon/PGMap.cc
ceph/src/test/rbd_mirror/image_replayer/snapshot/test_mock_Replayer.cc
ceph/src/test/rbd_mirror/test_main.cc
ceph/src/tools/CMakeLists.txt
ceph/src/tools/ceph_monstore_tool.cc
ceph/src/tools/erasure-code/CMakeLists.txt [new file with mode: 0644]
ceph/src/tools/erasure-code/ceph-erasure-code-tool.cc [new file with mode: 0644]
ceph/src/tools/rbd_mirror/ImageReplayer.cc
ceph/src/tools/rbd_mirror/InstanceReplayer.cc
ceph/src/tools/rbd_mirror/RemotePoolPoller.cc
ceph/src/tools/rbd_mirror/Types.cc
ceph/src/tools/rbd_mirror/Types.h
ceph/src/tools/rbd_mirror/image_replayer/journal/Replayer.cc
ceph/src/tools/rbd_mirror/image_replayer/snapshot/Replayer.cc
ceph/src/tools/rbd_mirror/image_replayer/snapshot/Replayer.h
ceph/src/tools/rbd_mirror/main.cc

index 3a1ab14e95fa53e80cf1c1f767600e1d94af9f6d..924db3b7ccdf97eef468e5ade3642293a7394000 100644 (file)
@@ -47,7 +47,8 @@ https://raw.githubusercontent.com/ceph/ceph/master/SubmittingPatches.rst
 - `jenkins test make check arm64`
 - `jenkins test submodules`
 - `jenkins test dashboard`
-- `jenkins test dashboard backend`
+- `jenkins test dashboard cephadm`
+- `jenkins test api`
 - `jenkins test docs`
 - `jenkins render docs`
 - `jenkins test ceph-volume all`
index 3ac7fb27ed80398cca06c473c7eddce2019c4a52..947b8e9475a049a9fff9c5ab5cfdc30dfe09aacc 100644 (file)
@@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.10.2)
 # remove cmake/modules/FindPython* once 3.12 is required
 
 project(ceph
-  VERSION 15.2.14
+  VERSION 15.2.15
   LANGUAGES CXX C ASM)
 
 foreach(policy
index ef43085d6dd748a249e7d9dcc94353b0e132f4c0..e54edab50bbac2df79acf2c096422952b4c6577b 100644 (file)
@@ -1,7 +1,7 @@
 # Contributor: John Coyle <dx9err@gmail.com>
 # Maintainer: John Coyle <dx9err@gmail.com>
 pkgname=ceph
-pkgver=15.2.14
+pkgver=15.2.15
 pkgrel=0
 pkgdesc="Ceph is a distributed object store and file system"
 pkgusers="ceph"
@@ -63,7 +63,7 @@ makedepends="
        xmlstarlet
        yasm
 "
-source="ceph-15.2.14.tar.bz2"
+source="ceph-15.2.15.tar.bz2"
 subpackages="
        $pkgname-base
        $pkgname-common
@@ -116,7 +116,7 @@ _sysconfdir=/etc
 _udevrulesdir=/etc/udev/rules.d
 _python_sitelib=/usr/lib/python2.7/site-packages
 
-builddir=$srcdir/ceph-15.2.14
+builddir=$srcdir/ceph-15.2.15
 
 build() {
        export CEPH_BUILD_VIRTUALENV=$builddir
@@ -415,7 +415,6 @@ ceph_test() {
                ceph_bench_log \
                ceph_kvstorebench \
                ceph_multi_stress_watch \
-               ceph_erasure_code \
                ceph_erasure_code_benchmark \
                ceph_omapbench \
                ceph_objectstore_bench \
index 9348f9ccf91474f5d334bb8c36b83de48b6f8343..a9a9a58ecfced5e547783fab35340dce6eb0e168 100644 (file)
@@ -415,7 +415,6 @@ ceph_test() {
                ceph_bench_log \
                ceph_kvstorebench \
                ceph_multi_stress_watch \
-               ceph_erasure_code \
                ceph_erasure_code_benchmark \
                ceph_omapbench \
                ceph_objectstore_bench \
index a966466df6ce1be57bfc582b08f5399be39ec506..fc9587d9dedcdf947519fd28a56e85472e349c04 100644 (file)
@@ -98,7 +98,7 @@
 # main package definition
 #################################################################################
 Name:          ceph
-Version:       15.2.14
+Version:       15.2.15
 Release:       0%{?dist}
 %if 0%{?fedora} || 0%{?rhel}
 Epoch:         2
@@ -114,7 +114,7 @@ License:    LGPL-2.1 and LGPL-3.0 and CC-BY-SA-3.0 and GPL-2.0 and BSL-1.0 and BSD-
 Group:         System/Filesystems
 %endif
 URL:           http://ceph.com/
-Source0:       %{?_remote_tarball_prefix}ceph-15.2.14.tar.bz2
+Source0:       %{?_remote_tarball_prefix}ceph-15.2.15.tar.bz2
 %if 0%{?suse_version}
 # _insert_obs_source_lines_here
 ExclusiveArch:  x86_64 aarch64 ppc64le s390x
@@ -1150,7 +1150,7 @@ This package provides Ceph’s default alerts for Prometheus.
 # common
 #################################################################################
 %prep
-%autosetup -p1 -n ceph-15.2.14
+%autosetup -p1 -n ceph-15.2.15
 
 %build
 # LTO can be enabled as soon as the following GCC bug is fixed:
@@ -1957,6 +1957,7 @@ fi
 %files osd
 %{_bindir}/ceph-clsinfo
 %{_bindir}/ceph-bluestore-tool
+%{_bindir}/ceph-erasure-code-tool
 %{_bindir}/ceph-objectstore-tool
 %{_bindir}/ceph-osdomap-tool
 %{_bindir}/ceph-osd
@@ -2175,7 +2176,6 @@ fi
 %{_bindir}/ceph_bench_log
 %{_bindir}/ceph_kvstorebench
 %{_bindir}/ceph_multi_stress_watch
-%{_bindir}/ceph_erasure_code
 %{_bindir}/ceph_erasure_code_benchmark
 %{_bindir}/ceph_omapbench
 %{_bindir}/ceph_objectstore_bench
@@ -2244,13 +2244,21 @@ if diff ${FILE_CONTEXT} ${FILE_CONTEXT}.pre > /dev/null 2>&1; then
    exit 0
 fi
 
+# Stop ceph.target while relabeling if CEPH_AUTO_RESTART_ON_UPGRADE=yes
+SYSCONF_CEPH=%{_sysconfdir}/sysconfig/ceph
+if [ -f $SYSCONF_CEPH -a -r $SYSCONF_CEPH ] ; then
+    source $SYSCONF_CEPH
+fi
+
 # Check whether the daemons are running
 /usr/bin/systemctl status ceph.target > /dev/null 2>&1
 STATUS=$?
 
 # Stop the daemons if they were running
 if test $STATUS -eq 0; then
-    /usr/bin/systemctl stop ceph.target > /dev/null 2>&1
+    if [ "X$CEPH_AUTO_RESTART_ON_UPGRADE" = "Xyes" ] ; then
+        /usr/bin/systemctl stop ceph.target > /dev/null 2>&1
+    fi
 fi
 
 # Relabel the files fix for first package install
@@ -2262,7 +2270,9 @@ rm -f ${FILE_CONTEXT}.pre
 
 # Start the daemons iff they were running before
 if test $STATUS -eq 0; then
-    /usr/bin/systemctl start ceph.target > /dev/null 2>&1 || :
+    if [ "X$CEPH_AUTO_RESTART_ON_UPGRADE" = "Xyes" ] ; then
+        /usr/bin/systemctl start ceph.target > /dev/null 2>&1 || :
+    fi
 fi
 exit 0
 
@@ -2282,13 +2292,21 @@ if [ $1 -eq 0 ]; then
         exit 0
     fi
 
+    # Stop ceph.target while relabeling if CEPH_AUTO_RESTART_ON_UPGRADE=yes
+    SYSCONF_CEPH=%{_sysconfdir}/sysconfig/ceph
+    if [ -f $SYSCONF_CEPH -a -r $SYSCONF_CEPH ] ; then
+        source $SYSCONF_CEPH
+    fi
+
     # Check whether the daemons are running
     /usr/bin/systemctl status ceph.target > /dev/null 2>&1
     STATUS=$?
 
     # Stop the daemons if they were running
     if test $STATUS -eq 0; then
-        /usr/bin/systemctl stop ceph.target > /dev/null 2>&1
+        if [ "X$CEPH_AUTO_RESTART_ON_UPGRADE" = "Xyes" ] ; then
+            /usr/bin/systemctl stop ceph.target > /dev/null 2>&1
+        fi
     fi
 
     /usr/sbin/fixfiles -C ${FILE_CONTEXT}.pre restore 2> /dev/null
@@ -2298,7 +2316,9 @@ if [ $1 -eq 0 ]; then
 
     # Start the daemons if they were running before
     if test $STATUS -eq 0; then
-       /usr/bin/systemctl start ceph.target > /dev/null 2>&1 || :
+        if [ "X$CEPH_AUTO_RESTART_ON_UPGRADE" = "Xyes" ] ; then
+           /usr/bin/systemctl start ceph.target > /dev/null 2>&1 || :
+        fi
     fi
 fi
 exit 0
index ccafc4562941c5df3352506d3afe7e6dd240e9fa..38f3029eaf79ecee6e84ea09db451d6a6c7256c1 100644 (file)
@@ -1957,6 +1957,7 @@ fi
 %files osd
 %{_bindir}/ceph-clsinfo
 %{_bindir}/ceph-bluestore-tool
+%{_bindir}/ceph-erasure-code-tool
 %{_bindir}/ceph-objectstore-tool
 %{_bindir}/ceph-osdomap-tool
 %{_bindir}/ceph-osd
@@ -2175,7 +2176,6 @@ fi
 %{_bindir}/ceph_bench_log
 %{_bindir}/ceph_kvstorebench
 %{_bindir}/ceph_multi_stress_watch
-%{_bindir}/ceph_erasure_code
 %{_bindir}/ceph_erasure_code_benchmark
 %{_bindir}/ceph_omapbench
 %{_bindir}/ceph_objectstore_bench
@@ -2244,13 +2244,21 @@ if diff ${FILE_CONTEXT} ${FILE_CONTEXT}.pre > /dev/null 2>&1; then
    exit 0
 fi
 
+# Stop ceph.target while relabeling if CEPH_AUTO_RESTART_ON_UPGRADE=yes
+SYSCONF_CEPH=%{_sysconfdir}/sysconfig/ceph
+if [ -f $SYSCONF_CEPH -a -r $SYSCONF_CEPH ] ; then
+    source $SYSCONF_CEPH
+fi
+
 # Check whether the daemons are running
 /usr/bin/systemctl status ceph.target > /dev/null 2>&1
 STATUS=$?
 
 # Stop the daemons if they were running
 if test $STATUS -eq 0; then
-    /usr/bin/systemctl stop ceph.target > /dev/null 2>&1
+    if [ "X$CEPH_AUTO_RESTART_ON_UPGRADE" = "Xyes" ] ; then
+        /usr/bin/systemctl stop ceph.target > /dev/null 2>&1
+    fi
 fi
 
 # Relabel the files fix for first package install
@@ -2262,7 +2270,9 @@ rm -f ${FILE_CONTEXT}.pre
 
 # Start the daemons iff they were running before
 if test $STATUS -eq 0; then
-    /usr/bin/systemctl start ceph.target > /dev/null 2>&1 || :
+    if [ "X$CEPH_AUTO_RESTART_ON_UPGRADE" = "Xyes" ] ; then
+        /usr/bin/systemctl start ceph.target > /dev/null 2>&1 || :
+    fi
 fi
 exit 0
 
@@ -2282,13 +2292,21 @@ if [ $1 -eq 0 ]; then
         exit 0
     fi
 
+    # Stop ceph.target while relabeling if CEPH_AUTO_RESTART_ON_UPGRADE=yes
+    SYSCONF_CEPH=%{_sysconfdir}/sysconfig/ceph
+    if [ -f $SYSCONF_CEPH -a -r $SYSCONF_CEPH ] ; then
+        source $SYSCONF_CEPH
+    fi
+
     # Check whether the daemons are running
     /usr/bin/systemctl status ceph.target > /dev/null 2>&1
     STATUS=$?
 
     # Stop the daemons if they were running
     if test $STATUS -eq 0; then
-        /usr/bin/systemctl stop ceph.target > /dev/null 2>&1
+        if [ "X$CEPH_AUTO_RESTART_ON_UPGRADE" = "Xyes" ] ; then
+            /usr/bin/systemctl stop ceph.target > /dev/null 2>&1
+        fi
     fi
 
     /usr/sbin/fixfiles -C ${FILE_CONTEXT}.pre restore 2> /dev/null
@@ -2298,7 +2316,9 @@ if [ $1 -eq 0 ]; then
 
     # Start the daemons if they were running before
     if test $STATUS -eq 0; then
-       /usr/bin/systemctl start ceph.target > /dev/null 2>&1 || :
+        if [ "X$CEPH_AUTO_RESTART_ON_UPGRADE" = "Xyes" ] ; then
+           /usr/bin/systemctl start ceph.target > /dev/null 2>&1 || :
+        fi
     fi
 fi
 exit 0
index 14b0e4b50e9dd48594728c187000f9f29d4ecc7f..647e2a5cfa8e546a506ace601de8f5d83a3343e8 100644 (file)
@@ -1,7 +1,13 @@
-ceph (15.2.14-1bionic) bionic; urgency=medium
+ceph (15.2.15-1bionic) bionic; urgency=medium
 
 
- -- Jenkins Build Slave User <jenkins-build@adami08.front.sepia.ceph.com>  Thu, 05 Aug 2021 17:22:18 +0000
+ -- Jenkins Build Slave User <jenkins-build@adami05.front.sepia.ceph.com>  Wed, 20 Oct 2021 14:31:05 +0000
+
+ceph (15.2.15-1) stable; urgency=medium
+
+  * New upstream release
+
+ -- Ceph Release Team <ceph-maintainers@ceph.com>  Wed, 20 Oct 2021 14:19:54 +0000
 
 ceph (15.2.14-1) stable; urgency=medium
 
index 2e92132366c6c6987d2bc61af419dc9996bad6ef..17b560c5652ad30459b52355cf7efd5085f26b1a 100644 (file)
@@ -152,7 +152,7 @@ function(do_build_boost version)
     set(boost_sha256 59c9b274bc451cf91a9ba1dd2c7fdcaf5d60b1b3aa83f2c9fa143417cc660722)
     string(REPLACE "." "_" boost_version_underscore ${boost_version} )
     set(boost_url 
-      https://dl.bintray.com/boostorg/release/${boost_version}/source/boost_${boost_version_underscore}.tar.bz2)
+      https://boostorg.jfrog.io/artifactory/main/release/${boost_version}/source/boost_${boost_version_underscore}.tar.bz2)
     if(CMAKE_VERSION VERSION_GREATER 3.7)
       set(boost_url
         "${boost_url} http://downloads.sourceforge.net/project/boost/boost/${boost_version}/boost_${boost_version_underscore}.tar.bz2")
index 3f73ec16a2de5c2bd2e26b1c7a073bf0455f1358..9b49ff9f9d54adfdf75d639a53aa6e47ad5a5002 100755 (executable)
@@ -4,6 +4,7 @@ lib/systemd/system/ceph-osd*
 lib/systemd/system/ceph-volume@.service
 usr/bin/ceph-bluestore-tool
 usr/bin/ceph-clsinfo
+usr/bin/ceph-erasure-code-tool
 usr/bin/ceph-objectstore-tool
 usr/bin/ceph-osdomap-tool
 usr/bin/${CEPH_OSD_BASENAME} => /usr/bin/ceph-osd
index dae063ab6c42593a8aa0c53e3c25f5c2cc490bca..52e29010501137b5c71df79366af6b13a4a6ad14 100644 (file)
@@ -1,7 +1,6 @@
 usr/bin/ceph-client-debug
 usr/bin/ceph-coverage
 usr/bin/ceph_bench_log
-usr/bin/ceph_erasure_code
 usr/bin/ceph_erasure_code_benchmark
 usr/bin/ceph_kvstorebench
 usr/bin/ceph_multi_stress_watch
index a9c18abb7228c7f9bc9029626e378601f74bbab0..9271bc2a0e96796704eb74f7446be2f2c49b0077 100644 (file)
@@ -76,6 +76,9 @@ and ``ceph-disk`` is fully disabled. Encryption is fully supported.
    lvm/systemd
    lvm/list
    lvm/zap
+   lvm/migrate
+   lvm/newdb
+   lvm/newwal
    simple/index
    simple/activate
    simple/scan
index 9a2191fb519359107b3464589cf0bbd4146d6b93..962e51a51c685788081fca83cb46bae510da46c7 100644 (file)
@@ -15,6 +15,12 @@ Implements the functionality needed to deploy OSDs from the ``lvm`` subcommand:
 
 * :ref:`ceph-volume-lvm-list`
 
+* :ref:`ceph-volume-lvm-migrate`
+
+* :ref:`ceph-volume-lvm-newdb`
+
+* :ref:`ceph-volume-lvm-newwal`
+
 .. not yet implemented
 .. * :ref:`ceph-volume-lvm-scan`
 
diff --git a/ceph/doc/ceph-volume/lvm/migrate.rst b/ceph/doc/ceph-volume/lvm/migrate.rst
new file mode 100644 (file)
index 0000000..983d2e7
--- /dev/null
@@ -0,0 +1,47 @@
+.. _ceph-volume-lvm-migrate:
+
+``migrate``
+===========
+
+Moves BlueFS data from source volume(s) to the target one, source volumes
+(except the main, i.e. data or block one) are removed on success.
+
+LVM volumes are permitted for Target only, both already attached or new one.
+
+In the latter case it is attached to the OSD replacing one of the source
+devices.
+
+Following replacement rules apply (in the order of precedence, stop
+on the first match):
+
+    - if source list has DB volume - target device replaces it.
+    - if source list has WAL volume - target device replaces it.
+    - if source list has slow volume only - operation is not permitted,
+      requires explicit allocation via new-db/new-wal command.
+
+Moves BlueFS data from main device to LV already attached as DB::
+
+    ceph-volume lvm migrate --osd-id 1 --osd-fsid <uuid> --from data --target vgname/db
+
+Moves BlueFS data from shared main device to LV which will be attached as a
+new DB::
+
+    ceph-volume lvm migrate --osd-id 1 --osd-fsid <uuid> --from data --target vgname/new_db
+
+Moves BlueFS data from DB device to new LV, DB is replaced::
+
+    ceph-volume lvm migrate --osd-id 1 --osd-fsid <uuid> --from db --target vgname/new_db
+
+Moves BlueFS data from main and DB devices to new LV, DB is replaced::
+
+    ceph-volume lvm migrate --osd-id 1 --osd-fsid <uuid> --from data db --target vgname/new_db
+
+Moves BlueFS data from main, DB and WAL devices to new LV, WAL is  removed and
+DB is replaced::
+
+    ceph-volume lvm migrate --osd-id 1 --osd-fsid <uuid> --from data db wal --target vgname/new_db
+
+Moves BlueFS data from main, DB and WAL devices to main device, WAL and DB are
+removed::
+
+    ceph-volume lvm migrate --osd-id 1 --osd-fsid <uuid> --from db wal --target vgname/data
diff --git a/ceph/doc/ceph-volume/lvm/newdb.rst b/ceph/doc/ceph-volume/lvm/newdb.rst
new file mode 100644 (file)
index 0000000..dcc87fc
--- /dev/null
@@ -0,0 +1,11 @@
+.. _ceph-volume-lvm-newdb:
+
+``new-db``
+===========
+
+Attaches the given logical volume to OSD as a DB.
+Logical volume name format is vg/lv. Fails if OSD has already got attached DB.
+
+Attach vgname/lvname as a DB volume to OSD 1::
+
+    ceph-volume lvm new-db --osd-id 1 --osd-fsid 55BD4219-16A7-4037-BC20-0F158EFCC83D --target vgname/new_db
diff --git a/ceph/doc/ceph-volume/lvm/newwal.rst b/ceph/doc/ceph-volume/lvm/newwal.rst
new file mode 100644 (file)
index 0000000..05f87ff
--- /dev/null
@@ -0,0 +1,11 @@
+.. _ceph-volume-lvm-newwal:
+
+``new-wal``
+===========
+
+Attaches the given logical volume to the given OSD as a WAL volume.
+Logical volume format is vg/lv. Fails if OSD has already got attached DB.
+
+Attach vgname/lvname as a WAL volume to OSD 1::
+
+    ceph-volume lvm new-wal --osd-id 1 --osd-fsid 55BD4219-16A7-4037-BC20-0F158EFCC83D --target vgname/new_wal
index 9bd51744eafc7a3dff811753a1e215f3f5829302..7dd4a82205f414c5665a51ecca411f56770d6970 100644 (file)
@@ -252,6 +252,7 @@ Usage::
 Optional arguments:
 
 * [-h, --help]          show the help message and exit
+* [--no-systemd]        skip checking OSD systemd unit
 
 Required arguments:
 
@@ -271,6 +272,7 @@ Usage::
 Optional arguments:
 
 * [-h, --help]          show the help message and exit
+* [--no-systemd]        skip checking OSD systemd unit
 
 Required arguments:
 
@@ -298,6 +300,7 @@ Usage::
 Optional arguments:
 
 * [-h, --help]          show the help message and exit
+* [--no-systemd]        skip checking OSD systemd unit
 
 Required arguments:
 
index ce2040a838480d40c52b97bd04647e46a14ac42d..c0252541f5a95532c5fc2facb3871d2ebb28b5f6 100644 (file)
@@ -372,6 +372,16 @@ on the number of replicas, clones and snapshots.
 - **MAX AVAIL:** An estimate of the notional amount of data that can be written
   to this pool.
 - **OBJECTS:** The notional number of objects stored per pool.
+- **QUOTA OBJECTS:** The number of quota objects.
+- **QUOTA BYTES:** The number of bytes in the quota objects.
+- **DIRTY:** The number of objects in the cache pool that have been written to
+  the cache pool but have not been flushed yet to the base pool. This field is
+  only available when cache tiering is in use.
+- **USED COMPR:** amount of space allocated for compressed data (i.e. this
+  includes comrpessed data plus all the allocation, replication and erasure
+  coding overhead).
+- **UNDER COMPR:** amount of data passed through compression (summed over all
+  replicas) and beneficial enough to be stored in a compressed form.
 
 .. note:: The numbers in the **POOLS** section are notional. They are not 
    inclusive of the number of replicas, snapshots or clones. As a result, 
index c24de14ab6ff735ae454dfaa845464530129254a..355a024e7fc123bd256e8ebcf559605663ca845e 100755 (executable)
@@ -155,7 +155,7 @@ tar cvf $outfile.version.tar $outfile/src/.git_version $outfile/ceph.spec $outfi
 # at the three URLs referenced below (may involve uploading to download.ceph.com)
 boost_version=1.72.0
 download_boost $boost_version 59c9b274bc451cf91a9ba1dd2c7fdcaf5d60b1b3aa83f2c9fa143417cc660722 \
-               https://dl.bintray.com/boostorg/release/$boost_version/source \
+               https://boostorg.jfrog.io/artifactory/main/release/$boost_version/source \
                https://downloads.sourceforge.net/project/boost/boost/$boost_version \
                https://download.ceph.com/qa
 
index 447dbb29322e599664eac167ea6510bf719d0e53..21b46f14ed374e22ad3d8b49106661a695d0bdc7 100644 (file)
       "decimals": 2,
       "format": "percentunit",
       "gauge": {
-        "maxValue": 100,
+        "maxValue": 1,
         "minValue": 0,
         "show": true,
         "thresholdLabels": false,
           "refId": "A"
         }
       ],
-      "thresholds": "70,80",
+      "thresholds": "0.7,0.8",
       "title": "Capacity used",
       "type": "singlestat",
       "valueFontSize": "80%",
index 237349dd36a1f42a9ddb10e4e5b837949c87d0b1..88185a95a15559ec0f5cebab197a0a1187db8dc6 100644 (file)
       "steppedLine": false,
       "targets": [
         {
-          "expr": "(node_memory_MemTotal{instance=~\"[[ceph_hosts]]([\\\\.:].*)?\"} or node_memory_MemTotal_bytes{instance=~\"[[ceph_hosts]]([\\\\.:].*)?\"})- (\n  (node_memory_MemFree{instance=~\"[[ceph_hosts]]([\\\\.:].*)?\"} or node_memory_MemFree_bytes{instance=~\"[[ceph_hosts]]([\\\\.:].*)?\"})  + \n  (node_memory_Cached{instance=~\"[[ceph_hosts]]([\\\\.:].*)?\"} or node_memory_Cached_bytes{instance=~\"[[ceph_hosts]]([\\\\.:].*)?\"}) + \n  (node_memory_Buffers{instance=~\"[[ceph_hosts]]([\\\\.:].*)?\"} or node_memory_Buffers_bytes{instance=~\"[[ceph_hosts]]([\\\\.:].*)?\"}) +\n  (node_memory_Slab{instance=~\"[[ceph_hosts]]([\\\\.:].*)?\"} or node_memory_Slab_bytes{instance=~\"[[ceph_hosts]]([\\\\.:].*)?\"})\n  )\n  \n",
+          "expr": "(node_memory_MemTotal{instance=~\"$ceph_hosts([\\\\.:].*)?\"} or node_memory_MemTotal_bytes{instance=~\"$ceph_hosts([\\\\.:].*)?\"})- (\n  (node_memory_MemFree{instance=~\"$ceph_hosts([\\\\.:].*)?\"} or node_memory_MemFree_bytes{instance=~\"$ceph_hosts([\\\\.:].*)?\"})  + \n  (node_memory_Cached{instance=~\"$ceph_hosts([\\\\.:].*)?\"} or node_memory_Cached_bytes{instance=~\"$ceph_hosts([\\\\.:].*)?\"}) + \n  (node_memory_Buffers{instance=~\"$ceph_hosts([\\\\.:].*)?\"} or node_memory_Buffers_bytes{instance=~\"$ceph_hosts([\\\\.:].*)?\"}) +\n  (node_memory_Slab{instance=~\"$ceph_hosts([\\\\.:].*)?\"} or node_memory_Slab_bytes{instance=~\"$ceph_hosts([\\\\.:].*)?\"})\n  )\n  \n",
           "format": "time_series",
           "intervalFactor": 1,
           "legendFormat": "used",
           "refId": "D"
         },
         {
-          "expr": "node_memory_MemFree{instance=~\"[[ceph_hosts]]([\\\\.:].*)?\"} or node_memory_MemFree_bytes{instance=~\"[[ceph_hosts]]([\\\\.:].*)?\"} ",
+          "expr": "node_memory_MemFree{instance=~\"$ceph_hosts([\\\\.:].*)?\"} or node_memory_MemFree_bytes{instance=~\"$ceph_hosts([\\\\.:].*)?\"} ",
           "format": "time_series",
           "hide": false,
           "intervalFactor": 1,
           "refId": "A"
         },
         {
-          "expr": "(node_memory_Cached{instance=~\"[[ceph_hosts]]([\\\\.:].*)?\"} or node_memory_Cached_bytes{instance=~\"[[ceph_hosts]]([\\\\.:].*)?\"}) + \n(node_memory_Buffers{instance=~\"[[ceph_hosts]]([\\\\.:].*)?\"} or node_memory_Buffers_bytes{instance=~\"[[ceph_hosts]]([\\\\.:].*)?\"}) +\n(node_memory_Slab{instance=~\"[[ceph_hosts]]([\\\\.:].*)?\"} or node_memory_Slab_bytes{instance=~\"[[ceph_hosts]]([\\\\.:].*)?\"}) \n",
+          "expr": "(node_memory_Cached{instance=~\"$ceph_hosts([\\\\.:].*)?\"} or node_memory_Cached_bytes{instance=~\"$ceph_hosts([\\\\.:].*)?\"}) + \n(node_memory_Buffers{instance=~\"$ceph_hosts([\\\\.:].*)?\"} or node_memory_Buffers_bytes{instance=~\"$ceph_hosts([\\\\.:].*)?\"}) +\n(node_memory_Slab{instance=~\"$ceph_hosts([\\\\.:].*)?\"} or node_memory_Slab_bytes{instance=~\"$ceph_hosts([\\\\.:].*)?\"}) \n",
           "format": "time_series",
           "hide": false,
           "intervalFactor": 1,
           "refId": "C"
         },
         {
-          "expr": "node_memory_MemTotal{instance=~\"[[ceph_hosts]]([\\\\.:].*)?\"} or node_memory_MemTotal_bytes{instance=~\"[[ceph_hosts]]([\\\\.:].*)?\"} ",
+          "expr": "node_memory_MemTotal{instance=~\"$ceph_hosts([\\\\.:].*)?\"} or node_memory_MemTotal_bytes{instance=~\"$ceph_hosts([\\\\.:].*)?\"} ",
           "format": "time_series",
           "hide": false,
           "intervalFactor": 1,
       "steppedLine": false,
       "targets": [
         {
-          "expr": "irate(node_network_receive_drop{instance=~\"[[ceph_hosts]]([\\\\.:].*)?\"}[1m]) or irate(node_network_receive_drop_total{instance=~\"[[ceph_hosts]]([\\\\.:].*)?\"}[1m])",
+          "expr": "irate(node_network_receive_drop{instance=~\"$ceph_hosts([\\\\.:].*)?\"}[1m]) or irate(node_network_receive_drop_total{instance=~\"$ceph_hosts([\\\\.:].*)?\"}[1m])",
           "format": "time_series",
           "instant": false,
           "intervalFactor": 1,
           "refId": "A"
         },
         {
-          "expr": "irate(node_network_transmit_drop{instance=~\"[[ceph_hosts]]([\\\\.:].*)?\"}[1m]) or irate(node_network_transmit_drop_total{instance=~\"[[ceph_hosts]]([\\\\.:].*)?\"}[1m])",
+          "expr": "irate(node_network_transmit_drop{instance=~\"$ceph_hosts([\\\\.:].*)?\"}[1m]) or irate(node_network_transmit_drop_total{instance=~\"$ceph_hosts([\\\\.:].*)?\"}[1m])",
           "format": "time_series",
           "intervalFactor": 1,
           "legendFormat": "{{device}}.tx",
       "steppedLine": false,
       "targets": [
         {
-          "expr": "irate(node_network_receive_errs{instance=~\"[[ceph_hosts]]([\\\\.:].*)?\"}[1m]) or irate(node_network_receive_errs_total{instance=~\"[[ceph_hosts]]([\\\\.:].*)?\"}[1m])",
+          "expr": "irate(node_network_receive_errs{instance=~\"$ceph_hosts([\\\\.:].*)?\"}[1m]) or irate(node_network_receive_errs_total{instance=~\"$ceph_hosts([\\\\.:].*)?\"}[1m])",
           "format": "time_series",
           "instant": false,
           "intervalFactor": 1,
           "refId": "A"
         },
         {
-          "expr": "irate(node_network_transmit_errs{instance=~\"[[ceph_hosts]]([\\\\.:].*)?\"}[1m]) or irate(node_network_transmit_errs_total{instance=~\"[[ceph_hosts]]([\\\\.:].*)?\"}[1m])",
+          "expr": "irate(node_network_transmit_errs{instance=~\"$ceph_hosts([\\\\.:].*)?\"}[1m]) or irate(node_network_transmit_errs_total{instance=~\"$ceph_hosts([\\\\.:].*)?\"}[1m])",
           "format": "time_series",
           "intervalFactor": 1,
           "legendFormat": "{{device}}.tx",
index deb6ed7d2b3a9b1cd9a7830a5bab49cbd28d45cb..eefb5912579172530cf7fe93f8acf98107939d0f 100644 (file)
       },
       "yaxes": [
         {
-          "format": "ms",
+          "format": "s",
           "label": "Read (-) / Write (+)",
           "logBase": 1,
           "max": null,
index 648abab89ce962844ac794d7b37d3bfc17b4bff4..bf5b16b8a2c64915c39041cfbf6354f4370113af 100644 (file)
       "steppedLine": false,
       "targets": [
         {
-          "expr": "rate(ceph_rgw_get_b{ceph_daemon=~\"[[rgw_servers]]\"}[30s])",
+          "expr": "rate(ceph_rgw_get_b{ceph_daemon=~\"$rgw_servers\"}[30s])",
           "format": "time_series",
           "intervalFactor": 1,
           "legendFormat": "GETs {{ceph_daemon}}",
           "refId": "B"
         },
         {
-          "expr": "rate(ceph_rgw_put_b{ceph_daemon=~\"[[rgw_servers]]\"}[30s])",
+          "expr": "rate(ceph_rgw_put_b{ceph_daemon=~\"$rgw_servers\"}[30s])",
           "format": "time_series",
           "intervalFactor": 1,
           "legendFormat": "PUTs {{ceph_daemon}}",
       "steppedLine": false,
       "targets": [
         {
-          "expr": "rate(ceph_rgw_failed_req{ceph_daemon=~\"[[rgw_servers]]\"}[30s])",
+          "expr": "rate(ceph_rgw_failed_req{ceph_daemon=~\"$rgw_servers\"}[30s])",
           "format": "time_series",
           "intervalFactor": 1,
           "legendFormat": "Requests Failed {{ceph_daemon}}",
           "refId": "B"
         },
         {
-          "expr": "rate(ceph_rgw_get{ceph_daemon=~\"[[rgw_servers]]\"}[30s])",
+          "expr": "rate(ceph_rgw_get{ceph_daemon=~\"$rgw_servers\"}[30s])",
           "format": "time_series",
           "intervalFactor": 1,
           "legendFormat": "GETs {{ceph_daemon}}",
           "refId": "C"
         },
         {
-          "expr": "rate(ceph_rgw_put{ceph_daemon=~\"[[rgw_servers]]\"}[30s])",
+          "expr": "rate(ceph_rgw_put{ceph_daemon=~\"$rgw_servers\"}[30s])",
           "format": "time_series",
           "intervalFactor": 1,
           "legendFormat": "PUTs {{ceph_daemon}}",
           "refId": "D"
         },
         {
-          "expr": "rate(ceph_rgw_req{ceph_daemon=~\"[[rgw_servers]]\"}[30s]) -\n  (rate(ceph_rgw_get{ceph_daemon=~\"[[rgw_servers]]\"}[30s]) +\n   rate(ceph_rgw_put{ceph_daemon=~\"[[rgw_servers]]\"}[30s]))",
+          "expr": "rate(ceph_rgw_req{ceph_daemon=~\"$rgw_servers\"}[30s]) -\n  (rate(ceph_rgw_get{ceph_daemon=~\"$rgw_servers\"}[30s]) +\n   rate(ceph_rgw_put{ceph_daemon=~\"$rgw_servers\"}[30s]))",
           "format": "time_series",
           "intervalFactor": 1,
           "legendFormat": "Other {{ceph_daemon}}",
       "strokeWidth": 1,
       "targets": [
         {
-          "expr": "rate(ceph_rgw_failed_req{ceph_daemon=~\"[[rgw_servers]]\"}[30s])",
+          "expr": "rate(ceph_rgw_failed_req{ceph_daemon=~\"$rgw_servers\"}[30s])",
           "format": "time_series",
           "intervalFactor": 1,
           "legendFormat": "Failures {{ceph_daemon}}",
           "refId": "A"
         },
         {
-          "expr": "rate(ceph_rgw_get{ceph_daemon=~\"[[rgw_servers]]\"}[30s])",
+          "expr": "rate(ceph_rgw_get{ceph_daemon=~\"$rgw_servers\"}[30s])",
           "format": "time_series",
           "intervalFactor": 1,
           "legendFormat": "GETs {{ceph_daemon}}",
           "refId": "B"
         },
         {
-          "expr": "rate(ceph_rgw_put{ceph_daemon=~\"[[rgw_servers]]\"}[30s])",
+          "expr": "rate(ceph_rgw_put{ceph_daemon=~\"$rgw_servers\"}[30s])",
           "format": "time_series",
           "intervalFactor": 1,
           "legendFormat": "PUTs {{ceph_daemon}}",
           "refId": "C"
         },
         {
-          "expr": "rate(ceph_rgw_req{ceph_daemon=~\"[[rgw_servers]]\"}[30s]) -\n  (rate(ceph_rgw_get{ceph_daemon=~\"[[rgw_servers]]\"}[30s]) +\n   rate(ceph_rgw_put{ceph_daemon=~\"[[rgw_servers]]\"}[30s]))",
+          "expr": "rate(ceph_rgw_req{ceph_daemon=~\"$rgw_servers\"}[30s]) -\n  (rate(ceph_rgw_get{ceph_daemon=~\"$rgw_servers\"}[30s]) +\n   rate(ceph_rgw_put{ceph_daemon=~\"$rgw_servers\"}[30s]))",
           "format": "time_series",
           "intervalFactor": 1,
           "legendFormat": "Other (DELETE,LIST) {{ceph_daemon}}",
index d4a0b8209e8aa5c21782008983d31da5b202b4b1..71fc864cddf7db4db38327eb199cf22fcb13eb5c 100644 (file)
@@ -233,7 +233,7 @@ groups:
             rate of the past 48 hours.
 
       - alert: MTU Mismatch
-        expr: node_network_mtu_bytes{device!="lo"} != on() group_left() (quantile(0.5, node_network_mtu_bytes{device!="lo"}))
+        expr: node_network_mtu_bytes{device!="lo"} * (node_network_up{device!="lo"} > 0) != on() group_left() (quantile(0.5, node_network_mtu_bytes{device!="lo"}))
         labels:
           severity: warning
           type: ceph_default
index 8bc35aa2643aeb2277852aa525764feff184bb8e..913c207339b1c7d73d250efd61617dae096310ca 100644 (file)
@@ -680,13 +680,27 @@ tests:
     - series: 'node_network_mtu_bytes{device="eth4",instance="node-exporter",
       job="node-exporter"}'
       values: '9000 9000 9000 9000 9000'
+    - series: 'node_network_up{device="eth0",instance="node-exporter",
+      job="node-exporter"}'
+      values: '0 0 0 0 0'
+    - series: 'node_network_up{device="eth1",instance="node-exporter",
+      job="node-exporter"}'
+      values: '0 0 0 0 0'
+    - series: 'node_network_up{device="eth2",instance="node-exporter",
+      job="node-exporter"}'
+      values: '1 1 1 1 1'
+    - series: 'node_network_up{device="eth3",instance="node-exporter",
+      job="node-exporter"}'
+      values: '0 0 0 0 0'
+    - series: 'node_network_up{device="eth4",instance="node-exporter",
+      job="node-exporter"}'
+      values: '1 1 1 1 1'  
    promql_expr_test:
-     - expr: node_network_mtu_bytes{device!="lo"} != on() group_left()
+     - expr: node_network_mtu_bytes{device!="lo"} * (node_network_up{device!="lo"} > 0) != on() group_left()
              (quantile(0.5, node_network_mtu_bytes{device!="lo"}))
        eval_time: 1m
        exp_samples:
-         - labels: '{__name__="node_network_mtu_bytes", device="eth4",
-           instance="node-exporter", job="node-exporter"}'
+         - labels: '{device="eth4", instance="node-exporter", job="node-exporter"}'
            value: 9000
    alert_rule_test:
      - eval_time: 1m
diff --git a/ceph/qa/distros/podman/centos_8.2_container_tools_3.0.yaml b/ceph/qa/distros/podman/centos_8.2_container_tools_3.0.yaml
new file mode 100644 (file)
index 0000000..6d27d15
--- /dev/null
@@ -0,0 +1,14 @@
+os_type: centos
+os_version: "8.2"
+overrides:
+  selinux:
+    whitelist:
+      - scontext=system_u:system_r:logrotate_t:s0
+
+tasks:
+- pexec:
+    all:
+    - sudo cp /etc/containers/registries.conf /etc/containers/registries.conf.backup
+    - sudo dnf -y  module reset container-tools
+    - sudo dnf -y  module install container-tools:3.0
+    - sudo cp /etc/containers/registries.conf.backup /etc/containers/registries.conf
diff --git a/ceph/qa/distros/podman/centos_8.2_kubic_stable.yaml b/ceph/qa/distros/podman/centos_8.2_kubic_stable.yaml
deleted file mode 100644 (file)
index 5ea907f..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-os_type: centos
-os_version: "8.2"
-overrides:
-  selinux:
-    whitelist:
-      - scontext=system_u:system_r:logrotate_t:s0
-
-tasks:
-- exec:
-    all:
-    - echo -e "[[registry]]\nlocation = 'docker.io'\n\n[[registry.mirror]]\nlocation='docker-mirror.front.sepia.ceph.com:5000'\n" | sudo tee /etc/containers/registries.conf
-    - sudo cp /etc/containers/registries.conf /etc/containers/registries.conf.backup
-    - sudo dnf -y module disable container-tools
-    - sudo dnf -y install 'dnf-command(copr)'
-    - sudo dnf -y copr enable rhcontainerbot/container-selinux
-    - sudo curl -L -o /etc/yum.repos.d/devel:kubic:libcontainers:stable.repo https://download.opensuse.org/repositories/devel:/kubic:/libcontainers:/stable/CentOS_8/devel:kubic:libcontainers:stable.repo
-    - sudo dnf remove -y podman
-    - sudo dnf -y install podman
-    - sudo cp /etc/containers/registries.conf.backup /etc/containers/registries.conf
diff --git a/ceph/qa/distros/podman/centos_8.3_container_tools_3.0.yaml b/ceph/qa/distros/podman/centos_8.3_container_tools_3.0.yaml
new file mode 100644 (file)
index 0000000..e2cad5e
--- /dev/null
@@ -0,0 +1,14 @@
+os_type: centos
+os_version: "8.3"
+overrides:
+  selinux:
+    whitelist:
+      - scontext=system_u:system_r:logrotate_t:s0
+
+tasks:
+- pexec:
+    all:
+    - sudo cp /etc/containers/registries.conf /etc/containers/registries.conf.backup
+    - sudo dnf -y  module reset container-tools
+    - sudo dnf -y  module install container-tools:3.0
+    - sudo cp /etc/containers/registries.conf.backup /etc/containers/registries.conf
index 1c9c5cf2a9953c36b39bbc4ab1176b8f9a45e64a..3ac70ccf59c02bb36b9ae4f995bf12299466bad3 100755 (executable)
@@ -8,18 +8,6 @@ function run() {
     local dir=$1
     shift
 
-    export CEPH_MON="127.0.0.1:7146" # git grep '\<7146\>' : there must be only one
-    export CEPH_ARGS
-    CEPH_ARGS+="--fsid=$(uuidgen) --auth-supported=none "
-    CEPH_ARGS+="--mon-host=$CEPH_MON "
-    CEPH_ARGS+="--bluestore_block_size=2147483648 "
-    CEPH_ARGS+="--bluestore_block_db_create=true "
-    CEPH_ARGS+="--bluestore_block_db_size=1073741824 "
-    CEPH_ARGS+="--bluestore_block_wal_size=536870912 "
-    CEPH_ARGS+="--bluestore_bluefs_min=536870912 "
-    CEPH_ARGS+="--bluestore_bluefs_min_free=536870912 "
-    CEPH_ARGS+="--bluestore_block_wal_create=true "
-    CEPH_ARGS+="--bluestore_fsck_on_mount=true "
     local funcs=${@:-$(set | sed -n -e 's/^\(TEST_[0-9a-z_]*\) .*/\1/p')}
     for func in $funcs ; do
         setup $dir || return 1
@@ -35,6 +23,18 @@ function TEST_bluestore() {
     if [ $flimit -lt 1536 ]; then
         echo "Low open file limit ($flimit), test may fail. Increase to 1536 or higher and retry if that happens."
     fi
+    export CEPH_MON="127.0.0.1:7146" # git grep '\<7146\>' : there must be only one
+    export CEPH_ARGS
+    CEPH_ARGS+="--fsid=$(uuidgen) --auth-supported=none "
+    CEPH_ARGS+="--mon-host=$CEPH_MON "
+    CEPH_ARGS+="--bluestore_block_size=2147483648 "
+    CEPH_ARGS+="--bluestore_block_db_create=true "
+    CEPH_ARGS+="--bluestore_block_db_size=1073741824 "
+    CEPH_ARGS+="--bluestore_block_wal_size=536870912 "
+    CEPH_ARGS+="--bluestore_bluefs_min=536870912 "
+    CEPH_ARGS+="--bluestore_bluefs_min_free=536870912 "
+    CEPH_ARGS+="--bluestore_block_wal_create=true "
+    CEPH_ARGS+="--bluestore_fsck_on_mount=true "
 
     run_mon $dir a || return 1
     run_mgr $dir x || return 1
@@ -339,6 +339,65 @@ function TEST_bluestore() {
     wait_for_clean || return 1
 }
 
+function TEST_bluestore2() {
+    local dir=$1
+
+    local flimit=$(ulimit -n)
+    if [ $flimit -lt 1536 ]; then
+        echo "Low open file limit ($flimit), test may fail. Increase to 1536 or higher and retry if that happens."
+    fi
+    export CEPH_MON="127.0.0.1:7146" # git grep '\<7146\>' : there must be only one
+    export CEPH_ARGS
+    CEPH_ARGS+="--fsid=$(uuidgen) --auth-supported=none "
+    CEPH_ARGS+="--mon-host=$CEPH_MON "
+    CEPH_ARGS+="--bluestore_block_size=4294967296 "
+    CEPH_ARGS+="--bluestore_block_db_create=true "
+    CEPH_ARGS+="--bluestore_block_db_size=1073741824 "
+    CEPH_ARGS+="--bluestore_bluefs_min=536870912 "
+    CEPH_ARGS+="--bluestore_bluefs_min_free=536870912 "
+    CEPH_ARGS+="--bluestore_block_wal_create=false "
+    CEPH_ARGS+="--bluestore_fsck_on_mount=true "
+    CEPH_ARGS+="--osd_pool_default_size=1 "
+    CEPH_ARGS+="--osd_pool_default_min_size=1 "
+    CEPH_ARGS+="--bluestore_debug_enforce_settings=ssd "
+
+    run_mon $dir a || return 1
+    run_mgr $dir x || return 1
+    run_osd $dir 0 || return 1
+    osd_pid0=$(cat $dir/osd.0.pid)
+
+    sleep 5
+    create_pool foo 16
+
+    # write some objects
+    timeout 60 rados bench -p foo 10 write --write-omap --no-cleanup #|| return 1
+
+    #give RocksDB some time to cooldown and put files to slow level(s)
+    sleep 10
+
+    spilled_over=$( ceph tell osd.0 perf dump bluefs | jq ".bluefs.slow_used_bytes" )
+    test $spilled_over -gt 0 || return 1
+
+    while kill $osd_pid0; do sleep 1 ; done
+    ceph osd down 0
+
+    ceph-bluestore-tool --path $dir/0 \
+      --devs-source $dir/0/block.db \
+      --dev-target $dir/0/block \
+      --command bluefs-bdev-migrate || return 1
+
+    ceph-bluestore-tool --path $dir/0 \
+      --command bluefs-bdev-sizes || return 1
+
+    ceph-bluestore-tool --path $dir/0 \
+      --command fsck || return 1
+
+    activate_osd $dir 0 || return 1
+    osd_pid0=$(cat $dir/osd.0.pid)
+
+    wait_for_clean || return 1
+}
+
 main osd-bluefs-volume-ops "$@"
 
 # Local Variables:
diff --git a/ceph/qa/suites/rados/cephadm/smoke/distro/centos_8.2_container_tools_3.0.yaml b/ceph/qa/suites/rados/cephadm/smoke/distro/centos_8.2_container_tools_3.0.yaml
new file mode 120000 (symlink)
index 0000000..d1965f3
--- /dev/null
@@ -0,0 +1 @@
+.qa/distros/podman/centos_8.2_container_tools_3.0.yaml
\ No newline at end of file
diff --git a/ceph/qa/suites/rados/cephadm/smoke/distro/centos_8.2_kubic_stable.yaml b/ceph/qa/suites/rados/cephadm/smoke/distro/centos_8.2_kubic_stable.yaml
deleted file mode 120000 (symlink)
index 3afeed7..0000000
+++ /dev/null
@@ -1 +0,0 @@
-.qa/distros/podman/centos_8.2_kubic_stable.yaml
\ No newline at end of file
diff --git a/ceph/qa/suites/rados/cephadm/smoke/distro/rhel_8.3_kubic_stable.yaml b/ceph/qa/suites/rados/cephadm/smoke/distro/rhel_8.3_kubic_stable.yaml
deleted file mode 120000 (symlink)
index 20f0f7c..0000000
+++ /dev/null
@@ -1 +0,0 @@
-.qa/distros/podman/rhel_8.3_kubic_stable.yaml
\ No newline at end of file
diff --git a/ceph/qa/suites/rados/cephadm/workunits/0-distro/centos_8.2_kubic_stable.yaml b/ceph/qa/suites/rados/cephadm/workunits/0-distro/centos_8.2_kubic_stable.yaml
deleted file mode 120000 (symlink)
index 3afeed7..0000000
+++ /dev/null
@@ -1 +0,0 @@
-.qa/distros/podman/centos_8.2_kubic_stable.yaml
\ No newline at end of file
diff --git a/ceph/qa/suites/rbd/mirror/workloads/rbd-mirror-bootstrap-workunit.yaml b/ceph/qa/suites/rbd/mirror/workloads/rbd-mirror-bootstrap-workunit.yaml
deleted file mode 100644 (file)
index 585f582..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-meta:
-- desc: run the rbd_mirror_bootstrap.sh workunit to test the rbd-mirror daemon
-tasks:
-- workunit:
-    clients:
-      cluster1.client.mirror: [rbd/rbd_mirror_bootstrap.sh]
-    env:
-      # override workunit setting of CEPH_ARGS='--cluster'
-      CEPH_ARGS: ''
-      RBD_MIRROR_INSTANCES: '1'
-      RBD_MIRROR_USE_EXISTING_CLUSTER: '1'
diff --git a/ceph/qa/suites/rbd/mirror/workloads/rbd-mirror-journal-bootstrap-workunit.yaml b/ceph/qa/suites/rbd/mirror/workloads/rbd-mirror-journal-bootstrap-workunit.yaml
new file mode 100644 (file)
index 0000000..b9c5562
--- /dev/null
@@ -0,0 +1,13 @@
+meta:
+- desc: run the rbd_mirror_bootstrap.sh workunit to test the rbd-mirror daemon
+tasks:
+- workunit:
+    clients:
+      cluster1.client.mirror: [rbd/rbd_mirror_bootstrap.sh]
+    env:
+      # override workunit setting of CEPH_ARGS='--cluster'
+      CEPH_ARGS: ''
+      RBD_MIRROR_INSTANCES: '1'
+      RBD_MIRROR_USE_EXISTING_CLUSTER: '1'
+      MIRROR_POOL_MODE: 'pool'
+      MIRROR_IMAGE_MODE: 'journal'
diff --git a/ceph/qa/suites/rbd/mirror/workloads/rbd-mirror-snapshot-bootstrap-workunit.yaml b/ceph/qa/suites/rbd/mirror/workloads/rbd-mirror-snapshot-bootstrap-workunit.yaml
new file mode 100644 (file)
index 0000000..5ad7847
--- /dev/null
@@ -0,0 +1,13 @@
+meta:
+- desc: run the rbd_mirror_bootstrap.sh workunit to test the rbd-mirror daemon
+tasks:
+- workunit:
+    clients:
+      cluster1.client.mirror: [rbd/rbd_mirror_bootstrap.sh]
+    env:
+      # override workunit setting of CEPH_ARGS='--cluster'
+      CEPH_ARGS: ''
+      RBD_MIRROR_INSTANCES: '1'
+      RBD_MIRROR_USE_EXISTING_CLUSTER: '1'
+      MIRROR_POOL_MODE: 'image'
+      MIRROR_IMAGE_MODE: 'snapshot'
index c904139acf49b4fc9c6d05ec7f435312fcc734a0..5b83372055eb080cb5a4339d8b297e1bdd04bebf 100644 (file)
@@ -3,7 +3,7 @@ meta:
    Run ceph on two nodes, using one of them as a client,
    with a separate client-only node.
    Use xfs beneath the osds.
-   install ceph/octopus v15.2.10 and the v15.2.x point versions
+   install ceph/octopus v15.2.14 and the v15.2.x point versions
    run workload and upgrade-sequence in parallel
    (every point release should be tested)
    run workload and upgrade-sequence in parallel
@@ -68,32 +68,32 @@ openstack:
     count: 3
     size: 30 # GB
 tasks:
-- print: "****  done octopus v15.2.10 about to install"
+- print: "****  done octopus v15.2.13 about to install"
 - install:
-    tag: v15.2.10
+    tag: v15.2.13
     # line below can be removed its from jewel test
     #exclude_packages: ['ceph-mgr','libcephfs2','libcephfs-devel','libcephfs-dev', 'librgw2']
-- print: "**** done v15.2.10 install"
+- print: "**** done v15.2.13 install"
 - ceph:
    fs: xfs
    add_osds_to_crush: true
 - print: "**** done ceph xfs"
 - sequential:
    - workload
-- print: "**** done workload v15.2.10"
+- print: "**** done workload v15.2.13"
 
 
-#######  upgrade to v15.2.11
+#######  upgrade to v15.2.14
 - install.upgrade:
     #exclude_packages: ['ceph-mgr','libcephfs2','libcephfs-devel','libcephfs-dev']
     mon.a:
-      tag: v15.2.11
+      tag: v15.2.14
     mon.b:
-      tag: v15.2.11
+      tag: v15.2.14
 - parallel:
    - workload_octopus
    - upgrade-sequence_octopus
-- print: "**** done parallel octopus v15.2.11"
+- print: "**** done parallel octopus v15.2.14"
 
 ####  upgrade to latest octopus
 - install.upgrade:
index cc1c8467ca25cd273dc1c6f8637c10bb013e1d16..c63b78d9d6f543ca10f41e9a0972ebd764f28718 100644 (file)
@@ -1,11 +1,11 @@
 meta:
-- desc: install ceph/octopus v15.2.11
+- desc: install ceph/octopus v15.2.14
 tasks:
 - install:
-    tag: v15.2.11
+    tag: v15.2.14
     exclude_packages: ['librados3']
     extra_packages: ['librados2']
-- print: "**** done install octopus v15.2.11"
+- print: "**** done install octopus v15.2.14"
 - ceph:
 - exec:
     osd.0:
index 293d8eadd8e1bb2a6b822250a5bfdb02c369bfef..c599b1034c172a84a1438542d923c3b0d58bd6c9 100644 (file)
@@ -3,6 +3,7 @@ ceph manager -- Thrasher and CephManager objects
 """
 from functools import wraps
 import contextlib
+import errno
 import random
 import signal
 import time
@@ -2888,13 +2889,22 @@ class CephManager:
         Loop until quorum size is reached.
         """
         self.log('waiting for quorum size %d' % size)
-        start = time.time()
-        while not len(self.get_mon_quorum()) == size:
-            if timeout is not None:
-                assert time.time() - start < timeout, \
-                    ('failed to reach quorum size %d '
-                     'before timeout expired' % size)
-            time.sleep(3)
+        sleep = 3
+        with safe_while(sleep=sleep,
+                        tries=timeout // sleep,
+                        action=f'wait for quorum size {size}') as proceed:
+            while proceed():
+                try:
+                    if len(self.get_mon_quorum()) == size:
+                        break
+                except CommandFailedError as e:
+                    # could fail instea4d of blocked if the rotating key of the
+                    # connected monitor is not updated yet after they form the
+                    # quorum
+                    if e.exitstatus == errno.EACCES:
+                        pass
+                    else:
+                        raise
         self.log("quorum is size %d" % size)
 
     def get_mon_health(self, debug=False):
index 9760276a3d3ae51587e82e766a3294907b096c91..be7729df848d2c02305333f72abe915765091e07 100644 (file)
@@ -223,6 +223,11 @@ class RgwBucketTest(RgwTestCase):
             'tenant': JLeaf(str),
         }, allow_unknown=True))
 
+        # List all buckets names without stats.
+        data = self._get('/api/rgw/bucket?stats=false')
+        self.assertStatus(200)
+        self.assertEqual(data, ['teuth-test-bucket'])
+
         # Get the bucket.
         data = self._get('/api/rgw/bucket/teuth-test-bucket')
         self.assertStatus(200)
index 171a8f3abeb98f7fa5fb9a9165f96134390a0ce9..9507b56b5e743edc03ec5b90cf11e4233b362c0c 100644 (file)
@@ -402,6 +402,9 @@ class UserTest(DashboardTestCase):
         user_1 = self._get('/api/user/user1')
         self.assertStatus(200)
 
+        # Let's wait 1 s to ensure pwd expiration date is not the same
+        time.sleep(1)
+
         self.login('user1', 'mypassword10#')
         self._post('/api/user/user1/change_password', {
             'old_password': 'mypassword10#',
index 423a4a8feae7ee92ef303b0e0ec29c5b6bef3eff..4a83c6c589a9c36f89837ee4e17a6918775295f5 100644 (file)
@@ -148,17 +148,6 @@ class TestInsights(MgrTestCase):
         active_id = self.mgr_cluster.get_active_id()
         self.mgr_cluster.mgr_restart(active_id)
 
-        # ensure that at least one of the checks is present after the restart.
-        # we don't for them all to be present because "earlier" checks may not
-        # have sat in memory long enough to be flushed.
-        all_missing = True
-        report = self._insights()
-        for check in check_names:
-            if check in report["health"]["history"]["checks"]:
-                all_missing = False
-                break
-        self.assertFalse(all_missing)
-
         # pruning really removes history
         self.mgr_cluster.mon_manager.raw_cluster_cmd_result(
             "insights", "prune-health", "0")
index 285d1f4cea6ad28ce8e534a3f97907aeecf44faa..5e0760af3dab7bb2af0361a6862a56a5f4351dd6 100755 (executable)
@@ -78,39 +78,19 @@ def run_cmd(cmd, expects=0):
     cmdlog = LOG.getChild('run_cmd')
     cmdlog.debug('{fc}'.format(fc=' '.join(full_cmd)))
 
-    proc = subprocess.Popen(full_cmd,
-                            stdout=subprocess.PIPE,
-                            stderr=subprocess.PIPE)
-
-    stdout = []
-    stderr = []
-    while True:
-        try:
-            out, err = proc.communicate()
-            if out is not None:
-                stdout += out.decode().split('\n')
-                cmdlog.debug('stdout: {s}'.format(s=out))
-            if err is not None:
-                stdout += err.decode().split('\n')
-                cmdlog.debug('stderr: {s}'.format(s=err))
-        except ValueError:
-            ret = proc.wait()
-            break
-
-    if ret != expects:
-        cmdlog.error('cmd > {cmd}'.format(cmd=full_cmd))
-        cmdlog.error("expected return '{expected}' got '{got}'".format(
-            expected=expects, got=ret))
+    proc = subprocess.run(full_cmd,
+                          stdout=subprocess.PIPE,
+                          stderr=subprocess.PIPE,
+                          universal_newlines=True)
+    if proc.returncode != expects:
+        cmdlog.error(f'cmd > {proc.args}')
+        cmdlog.error(f'expected return "{expects}" got "{proc.returncode}"')
         cmdlog.error('stdout')
-        for i in stdout:
-            cmdlog.error('{x}'.format(x=i))
+        cmdlog.error(proc.stdout)
         cmdlog.error('stderr')
-        for i in stderr:
-            cmdlog.error('{x}'.format(x=i))
+        cmdlog.error(proc.stderr)
 
 
-# end run_cmd
-
 def gen_data(size, rnd):
     chars = string.ascii_letters + string.digits
     return ''.join(rnd.choice(chars) for _ in range(size))
index e0a096ee8a370a2a3d9992886563df814ad83663..fb77c0d9bf869f5665042c4666e5ce30446296dd 100755 (executable)
@@ -27,7 +27,7 @@ testlog "TEST: verify rx-only direction"
 [ "$(rbd --cluster ${CLUSTER1} --pool ${POOL} mirror pool info --format xml |
        ${XMLSTARLET} sel -t -v  '//mirror/peers/peer[1]/uuid')" = "" ]
 
-create_image ${CLUSTER1} ${POOL} image1
+create_image_and_enable_mirror ${CLUSTER1} ${POOL} image1
 
 wait_for_image_replay_started ${CLUSTER2} ${POOL} image1
 write_image ${CLUSTER1} ${POOL} image1 100
index 5da562fafbf3ef9c95e8d5028119c67c88dbc695..1b56c2ded136589ad77bfdba3aa34912a8650082 100755 (executable)
@@ -40,7 +40,7 @@ function run() {
 
     CHECK_MAKEOPTS=${CHECK_MAKEOPTS:-$DEFAULT_MAKEOPTS}
     if in_jenkins; then
-        if ! ctest $CHECK_MAKEOPTS --no-compress-output --output-on-failure -T Test; then
+        if ! ctest $CHECK_MAKEOPTS --no-compress-output --output-on-failure --test-output-size-failed 1024000 -T Test; then
             # do not return failure, as the jenkins publisher will take care of this
             rm -fr ${TMPDIR:-/tmp}/ceph-asok.*
         fi
index 59bde605806d07617b9cfadd6b4183c3e5cc3b5d..f359a0a08377fbb4509eb0a0377383a3667b0824 100644 (file)
@@ -1,2 +1,2 @@
-cd3bb7e87a2f62c1b862ff3fd8b1eec13391a5be
-15.2.14
+2dfb18841cfecc2f7eb7eb2afd65986ca4d95985
+15.2.15
index a2833183633a4c04050b3d76ae9e28dee9003608..a49cf5094ffa122e4cf81588a807ab53daef5a22 100644 (file)
@@ -41,7 +41,7 @@ int KeyRing::from_ceph_context(CephContext *cct)
     if (ret < 0)
       lderr(cct) << "failed to load " << filename
                 << ": " << cpp_strerror(ret) << dendl;
-  } else {
+  } else if (conf->key.empty() && conf->keyfile.empty()) {
     lderr(cct) << "unable to find a keyring on " << conf->keyring
               << ": " << cpp_strerror(ret) << dendl;
   }
index 4224499c4749840311b2e5a1a8c0decc015788c1..9166553dc7354a1896e7046f0b472581457541e0 100644 (file)
@@ -13,7 +13,7 @@ set(CEPH_VOLUME_VIRTUALENV ${CEPH_BUILD_VIRTUALENV}/ceph-volume-virtualenv)
 
 add_custom_command(
   OUTPUT ${CEPH_VOLUME_VIRTUALENV}/bin/python
-  COMMAND ${CMAKE_SOURCE_DIR}/src/tools/setup-virtualenv.sh --python=${Python_EXECUTABLE} ${CEPH_VOLUME_VIRTUALENV}
+  COMMAND ${CMAKE_SOURCE_DIR}/src/tools/setup-virtualenv.sh --python=${Python3_EXECUTABLE} ${CEPH_VOLUME_VIRTUALENV}
   WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/src/ceph-volume
   COMMENT "ceph-volume venv is being created")
 
index e4b932b80e2ebec1186289237e72a943788471a2..c6199fc5fb754bf8203f01320592bf485055461c 100644 (file)
@@ -463,7 +463,7 @@ def get_pvs(fields=PV_FIELDS, filters='', tags=None):
     :returns: list of class PVolume object representing pvs on the system
     """
     filters = make_filters_lvmcmd_ready(filters, tags)
-    args = ['pvs', '--no-heading', '--readonly', '--separator=";"', '-S',
+    args = ['pvs', '--noheadings', '--readonly', '--separator=";"', '-S',
             filters, '-o', fields]
 
     stdout, stderr, returncode = process.call(args, verbose_on_failure=False)
index e4ac074a4f4147e935528b94df250f68cdf823fe..70fceeab6457257416b3368044f41c359ec9249d 100644 (file)
@@ -245,7 +245,7 @@ class Activate(object):
             terminal.warning('Verify OSDs are present with "ceph-volume lvm list"')
             return
         for osd_fsid, osd_id in osds.items():
-            if systemctl.osd_is_active(osd_id):
+            if not args.no_systemd and systemctl.osd_is_active(osd_id):
                 terminal.warning(
                     'OSD ID %s FSID %s process is active. Skipping activation' % (osd_id, osd_fsid)
                 )
@@ -269,6 +269,11 @@ class Activate(object):
             tags = {'ceph.osd_id': osd_id, 'ceph.osd_fsid': osd_fsid}
         elif not osd_id and osd_fsid:
             tags = {'ceph.osd_fsid': osd_fsid}
+        elif osd_id and not osd_fsid:
+            raise RuntimeError('could not activate osd.{}, please provide the '
+                               'osd_fsid too'.format(osd_id))
+        else:
+            raise RuntimeError('Please provide both osd_id and osd_fsid')
         lvs = api.get_lvs(tags=tags)
         if not lvs:
             raise RuntimeError('could not find osd.%s with osd_fsid %s' %
index 40c0fea4eec148d03715346bcbf552a8bd211662..e64e4b64e40f8347ed6017fc00bc4de32501a731 100644 (file)
@@ -106,7 +106,7 @@ def get_physical_fast_allocs(devices, type_, fast_slots_per_device, new_osds, ar
         requested_slots = fast_slots_per_device
 
     requested_size = getattr(args, '{}_size'.format(type_), 0)
-    if requested_size == 0:
+    if not requested_size or requested_size == 0:
         # no size argument was specified, check ceph.conf
         get_size_fct = getattr(prepare, 'get_{}_size'.format(type_))
         requested_size = get_size_fct(lv_format=False)
@@ -126,6 +126,7 @@ def get_physical_fast_allocs(devices, type_, fast_slots_per_device, new_osds, ar
         if requested_size:
             if requested_size <= abs_size:
                 abs_size = requested_size
+                relative_size = int(abs_size) / dev_size
             else:
                 mlogger.error(
                     '{} was requested for {}, but only {} can be fulfilled'.format(
@@ -511,10 +512,11 @@ class Batch(object):
         # fill up uneven distributions across fast devices: 5 osds and 2 fast
         # devices? create 3 slots on each device rather then deploying
         # heterogeneous osds
-        if (requested_osds - len(lvm_devs)) % len(phys_devs):
-            fast_slots_per_device = int((requested_osds - len(lvm_devs)) / len(phys_devs)) + 1
+        slot_divider = max(1, len(phys_devs))
+        if (requested_osds - len(lvm_devs)) % slot_divider:
+            fast_slots_per_device = int((requested_osds - len(lvm_devs)) / slot_divider) + 1
         else:
-            fast_slots_per_device = int((requested_osds - len(lvm_devs)) / len(phys_devs))
+            fast_slots_per_device = int((requested_osds - len(lvm_devs)) / slot_divider)
 
 
         ret.extend(get_physical_fast_allocs(phys_devs,
index 5de6dbe36b5a15e3a90bac0d4a99d097496eccf1..46846a1dcbede5d8dfcf5cbc694252a69365d28b 100644 (file)
@@ -54,8 +54,6 @@ class Deactivate(object):
 
             ceph-volume lvm deactivate {ID} {FSID}
 
-        To deactivate all volumes use the --all flag.
-            ceph-volume lvm deactivate --all
         """)
         parser = argparse.ArgumentParser(
             prog='ceph-volume lvm deactivate',
index 3410dd5087967de98965cdcf0f650e5a8ba6ee23..886b9f7b4fd6b1a6e54a6b71be50358d33482f15 100644 (file)
@@ -400,7 +400,7 @@ class Migrate(object):
 
     @decorators.needs_root
     def migrate_osd(self):
-        if self.args.osd_id:
+        if self.args.osd_id and not self.args.no_systemd:
             osd_is_running = systemctl.osd_is_active(self.args.osd_id)
             if osd_is_running:
                 mlogger.error('OSD is running, stop it with: '
@@ -436,7 +436,46 @@ class Migrate(object):
                 devices,
                 target_lv)
 
-    def parse_argv(self):
+    def make_parser(self, prog, sub_command_help):
+        parser = argparse.ArgumentParser(
+            prog=prog,
+            formatter_class=argparse.RawDescriptionHelpFormatter,
+            description=sub_command_help,
+        )
+
+        parser.add_argument(
+            '--osd-id',
+            required=True,
+            help='Specify an OSD ID to detect associated devices for zapping',
+        )
+
+        parser.add_argument(
+            '--osd-fsid',
+            required=True,
+            help='Specify an OSD FSID to detect associated devices for zapping',
+        )
+        parser.add_argument(
+            '--target',
+            required=True,
+            help='Specify target Logical Volume (LV) to migrate data to',
+        )
+        parser.add_argument(
+            '--from',
+            nargs='*',
+            dest='from_',
+            required=True,
+            choices=['data', 'db', 'wal'],
+            help='Copy BlueFS data from DB device',
+        )
+        parser.add_argument(
+            '--no-systemd',
+            dest='no_systemd',
+            action='store_true',
+            help='Skip checking OSD systemd unit',
+        )
+        return parser
+
+    def main(self):
         sub_command_help = dedent("""
         Moves BlueFS data from source volume(s) to the target one, source
         volumes (except the main (i.e. data or block) one) are removed on
@@ -479,44 +518,15 @@ class Migrate(object):
             ceph-volume lvm migrate --osd-id 1 --osd-fsid <uuid> --from db wal --target vgname/data
 
         """)
-        parser = argparse.ArgumentParser(
-            prog='ceph-volume lvm migrate',
-            formatter_class=argparse.RawDescriptionHelpFormatter,
-            description=sub_command_help,
-        )
 
-        parser.add_argument(
-            '--osd-id',
-            required=True,
-            help='Specify an OSD ID to detect associated devices for zapping',
-        )
-
-        parser.add_argument(
-            '--osd-fsid',
-            required=True,
-            help='Specify an OSD FSID to detect associated devices for zapping',
-        )
-        parser.add_argument(
-            '--target',
-            required=True,
-            help='Specify target Logical Volume (LV) to migrate data to',
-        )
-        parser.add_argument(
-            '--from',
-            nargs='*',
-            dest='from_',
-            required=True,
-            choices=['data', 'db', 'wal'],
-            help='Copy BlueFS data from DB device',
-        )
+        parser = self.make_parser('ceph-volume lvm migrate', sub_command_help)
 
         if len(self.argv) == 0:
             print(sub_command_help)
             return
+
         self.args = parser.parse_args(self.argv)
 
-    def main(self):
-        self.parse_argv()
         self.migrate_osd()
 
 class NewVolume(object):
@@ -547,6 +557,12 @@ class NewVolume(object):
             required=True,
             help='Specify target Logical Volume (LV) to attach',
         )
+        parser.add_argument(
+            '--no-systemd',
+            dest='no_systemd',
+            action='store_true',
+            help='Skip checking OSD systemd unit',
+        )
         return parser
 
     @decorators.needs_root
@@ -587,7 +603,7 @@ class NewVolume(object):
 
     @decorators.needs_root
     def new_volume(self):
-        if self.args.osd_id:
+        if self.args.osd_id and not self.args.no_systemd:
             osd_is_running = systemctl.osd_is_active(self.args.osd_id)
             if osd_is_running:
                 mlogger.error('OSD ID is running, stop it with:'
index bb15bf199012bb52dff50262247c78c31906ca02..c86d4996f8d05699e1c5e3297ea10dbd412d9abd 100644 (file)
@@ -4,10 +4,12 @@ import json
 import logging
 from textwrap import dedent
 from ceph_volume import decorators, process
+from ceph_volume.util import disk
 
 
 logger = logging.getLogger(__name__)
 
+
 def direct_report(devices):
     """
     Other non-cli consumers of listing information will want to consume the
@@ -18,6 +20,40 @@ def direct_report(devices):
     _list = List([])
     return _list.generate(devices)
 
+def _get_bluestore_info(dev):
+    out, err, rc = process.call([
+        'ceph-bluestore-tool', 'show-label',
+        '--dev', dev], verbose_on_failure=False)
+    if rc:
+        # ceph-bluestore-tool returns an error (below) if device is not bluestore OSD
+        #   > unable to read label for <device>: (2) No such file or directory
+        # but it's possible the error could be for a different reason (like if the disk fails)
+        logger.debug('assuming device {} is not BlueStore; ceph-bluestore-tool failed to get info from device: {}\n{}'.format(dev, out, err))
+        return None
+    oj = json.loads(''.join(out))
+    if dev not in oj:
+        # should be impossible, so warn
+        logger.warning('skipping device {} because it is not reported in ceph-bluestore-tool output: {}'.format(dev, out))
+        return None
+    try:
+        if oj[dev]['description'] != 'main':
+            # ignore non-main devices, for now
+            logger.info('ignoring non-main device {}'.format(dev))
+            return None
+        whoami = oj[dev]['whoami']
+        return {
+            'type': 'bluestore',
+            'osd_id': int(whoami),
+            'osd_uuid': oj[dev]['osd_uuid'],
+            'ceph_fsid': oj[dev]['ceph_fsid'],
+            'device': dev
+        }
+    except KeyError as e:
+        # this will appear for devices that have a bluestore header but aren't valid OSDs
+        # for example, due to incomplete rollback of OSDs: https://tracker.ceph.com/issues/51869
+        logger.error('device {} does not have all BlueStore data needed to be a valid OSD: {}\n{}'.format(dev, out, e))
+        return None
+
 
 class List(object):
 
@@ -27,64 +63,53 @@ class List(object):
         self.argv = argv
 
     def generate(self, devs=None):
-        if not devs:
-            logger.debug('Listing block devices via lsblk...')
+        logger.debug('Listing block devices via lsblk...')
+
+        if devs is None or devs == []:
             devs = []
-            # adding '--inverse' allows us to get the mapper devices list in that command output.
-            # not listing root devices containing partitions shouldn't have side effect since we are
-            # in `ceph-volume raw` context.
-            #
-            #   example:
-            #   running `lsblk --paths --nodeps --output=NAME --noheadings` doesn't allow to get the mapper list
-            #   because the output is like following :
-            #
-            #   $ lsblk --paths --nodeps --output=NAME --noheadings
-            #   /dev/sda
-            #   /dev/sdb
-            #   /dev/sdc
-            #   /dev/sdd
-            #
-            #   the dmcrypt mappers are hidden because of the `--nodeps` given they are displayed as a dependency.
-            #
-            #   $ lsblk --paths --output=NAME --noheadings
-            #   /dev/sda
-            #   |-/dev/mapper/ceph-3b52c90d-6548-407d-bde1-efd31809702f-sda-block-dmcrypt
-            #   `-/dev/mapper/ceph-3b52c90d-6548-407d-bde1-efd31809702f-sda-db-dmcrypt
-            #   /dev/sdb
-            #   /dev/sdc
-            #   /dev/sdd
-            #
-            #   adding `--inverse` is a trick to get around this issue, the counterpart is that we can't list root devices if they contain
-            #   at least one partition but this shouldn't be an issue in `ceph-volume raw` context given we only deal with raw devices.
+            # If no devs are given initially, we want to list ALL devices including children and
+            # parents. Parent disks with child partitions may be the appropriate device to return if
+            # the parent disk has a bluestore header, but children may be the most appropriate
+            # devices to return if the parent disk does not have a bluestore header.
             out, err, ret = process.call([
-                'lsblk', '--paths', '--nodeps', '--output=NAME', '--noheadings', '--inverse'
+                'lsblk', '--paths', '--output=NAME', '--noheadings', '--list'
             ])
             assert not ret
             devs = out
+
         result = {}
+        logger.debug('inspecting devices: {}'.format(devs))
         for dev in devs:
-            logger.debug('Examining %s' % dev)
-            # bluestore?
-            out, err, ret = process.call([
-                'ceph-bluestore-tool', 'show-label',
-                '--dev', dev], verbose_on_failure=False)
-            if ret:
-                logger.debug('No label on %s' % dev)
-                continue
-            oj = json.loads(''.join(out))
-            if dev not in oj:
+            info = disk.lsblk(dev, abspath=True)
+            # Linux kernels built with CONFIG_ATARI_PARTITION enabled can falsely interpret
+            # bluestore's on-disk format as an Atari partition table. These false Atari partitions
+            # can be interpreted as real OSDs if a bluestore OSD was previously created on the false
+            # partition. See https://tracker.ceph.com/issues/52060 for more info. If a device has a
+            # parent, it is a child. If the parent is a valid bluestore OSD, the child will only
+            # exist if it is a phantom Atari partition, and the child should be ignored. If the
+            # parent isn't bluestore, then the child could be a valid bluestore OSD. If we fail to
+            # determine whether a parent is bluestore, we should err on the side of not reporting
+            # the child so as not to give a false negative.
+            if 'PKNAME' in info and info['PKNAME'] != "":
+                parent = info['PKNAME']
+                try:
+                    if disk.has_bluestore_label(parent):
+                        logger.warning(('ignoring child device {} whose parent {} is a BlueStore OSD.'.format(dev, parent),
+                                        'device is likely a phantom Atari partition. device info: {}'.format(info)))
+                        continue
+                except OSError as e:
+                    logger.error(('ignoring child device {} to avoid reporting invalid BlueStore data from phantom Atari partitions.'.format(dev),
+                                  'failed to determine if parent device {} is BlueStore. err: {}'.format(parent, e)))
+                    continue
+
+            bs_info = _get_bluestore_info(dev)
+            if bs_info is None:
+                # None is also returned in the rare event that there is an issue reading info from
+                # a BlueStore disk, so be sure to log our assumption that it isn't bluestore
+                logger.info('device {} does not have BlueStore information'.format(dev))
                 continue
-            if oj[dev]['description'] != 'main':
-                # ignore non-main devices, for now
-                continue
-            whoami = oj[dev]['whoami']
-            result[whoami] = {
-                'type': 'bluestore',
-                'osd_id': int(whoami),
-            }
-            for f in ['osd_uuid', 'ceph_fsid']:
-                result[whoami][f] = oj[dev][f]
-            result[whoami]['device'] = dev
+            result[bs_info['osd_uuid']] = bs_info
+
         return result
 
     @decorators.needs_root
index 2abedac3240dbc20942b7dfa39cc898fa2d1879e..149afbbc6523568f2559f0337afa96fc81a9c34c 100644 (file)
@@ -52,6 +52,8 @@ def mock_lv_device_generator():
         dev.used_by_ceph = False
         dev.vg_size = [size]
         dev.vg_free = dev.vg_size
+        dev.available_lvm = True
+        dev.is_device = False
         dev.lvs = [lvm.Volume(vg_name=dev.vg_name, lv_name=dev.lv_name, lv_size=size, lv_tags='')]
         return dev
     return mock_lv
index 33e0ed32b619a51a00839b57f9c175c6a2a305d0..1fae5c4e0c34c472b1be1a69b283fe1943fb9de6 100644 (file)
@@ -58,6 +58,18 @@ class TestActivate(object):
         with pytest.raises(RuntimeError):
             activate.Activate([]).activate(args)
 
+    def test_osd_id_no_osd_fsid(self, is_root):
+        args = Args(osd_id=42, osd_fsid=None)
+        with pytest.raises(RuntimeError) as result:
+            activate.Activate([]).activate(args)
+        assert result.value.args[0] == 'could not activate osd.42, please provide the osd_fsid too'
+
+    def test_no_osd_id_no_osd_fsid(self, is_root):
+        args = Args(osd_id=None, osd_fsid=None)
+        with pytest.raises(RuntimeError) as result:
+            activate.Activate([]).activate(args)
+        assert result.value.args[0] == 'Please provide both osd_id and osd_fsid'
+
     def test_filestore_no_systemd(self, is_root, monkeypatch, capture):
         monkeypatch.setattr('ceph_volume.configuration.load', lambda: None)
         fake_enable = Capture()
@@ -345,7 +357,7 @@ class TestActivateAll(object):
         assert 'a8789a96ce8b process is active. Skipping activation' in err
         assert 'b8218eaa1634 process is active. Skipping activation' in err
 
-    def test_detects_osds_to_activate(self, is_root, capture, monkeypatch):
+    def test_detects_osds_to_activate_systemd(self, is_root, capture, monkeypatch):
         monkeypatch.setattr('ceph_volume.devices.lvm.activate.direct_report', lambda: direct_report)
         monkeypatch.setattr('ceph_volume.devices.lvm.activate.systemctl.osd_is_active', lambda x: False)
         args = ['--all']
@@ -358,6 +370,18 @@ class TestActivateAll(object):
         assert calls[1]['kwargs']['osd_id'] == '1'
         assert calls[1]['kwargs']['osd_fsid'] == 'd0f3e4ad-e52a-4520-afc0-a8789a96ce8b'
 
+    def test_detects_osds_to_activate_no_systemd(self, is_root, capture, monkeypatch):
+        monkeypatch.setattr('ceph_volume.devices.lvm.activate.direct_report', lambda: direct_report)
+        args = ['--all', '--no-systemd']
+        activation = activate.Activate(args)
+        activation.activate = capture
+        activation.main()
+        calls = sorted(capture.calls, key=lambda x: x['kwargs']['osd_id'])
+        assert calls[0]['kwargs']['osd_id'] == '0'
+        assert calls[0]['kwargs']['osd_fsid'] == '957d22b7-24ce-466a-9883-b8218eaa1634'
+        assert calls[1]['kwargs']['osd_id'] == '1'
+        assert calls[1]['kwargs']['osd_fsid'] == 'd0f3e4ad-e52a-4520-afc0-a8789a96ce8b'
+
 #
 # Activate All fixture
 #
index 7c968ae81d5e5b7f9d5db00b42a855a487b20c9b..4bf026ae1f22fd42f542b4247a5590a6a28de8d1 100644 (file)
@@ -209,6 +209,15 @@ class TestBatch(object):
                                               'block_db', 2, 2, args)
         assert len(fast) == 2
 
+    def test_batch_fast_allocations_one_block_db_length(self, factory, conf_ceph_stub,
+                                                  mock_lv_device_generator):
+        conf_ceph_stub('[global]\nfsid=asdf-lkjh')
+
+        b = batch.Batch([])
+        db_lv_devices = [mock_lv_device_generator()]
+        fast = b.fast_allocations(db_lv_devices, 1, 0, 'block_db')
+        assert len(fast) == 1
+
     @pytest.mark.parametrize('occupied_prior', range(7))
     @pytest.mark.parametrize('slots,num_devs',
                              [l for sub in [list(zip([x]*x, range(1, x + 1))) for x in range(1,7)] for l in sub])
index dc429793de4fc3c05038ae095f92b5e9e1d37eb4..6c20273507a4a782d296183abeda60fe97faddf3 100644 (file)
@@ -660,6 +660,160 @@ class TestNew(object):
             '--dev-target', '/dev/VolGroup/target_volume',
             '--command', 'bluefs-bdev-new-db']
 
+    def test_newdb_active_systemd(self, is_root, monkeypatch, capsys):
+        source_tags = \
+        'ceph.osd_id=0,ceph.type=data,ceph.osd_fsid=1234,'\
+        'ceph.wal_uuid=wal_uuid,ceph.db_device=/dbdevice'
+        source_wal_tags = \
+        'ceph.wal_uuid=uuid,ceph.wal_device=device,' \
+        'ceph.osd_id=0,ceph.type=wal'
+
+        data_vol = api.Volume(lv_name='volume1', lv_uuid='datauuid',
+                              vg_name='vg',
+                              lv_path='/dev/VolGroup/lv1',
+                              lv_tags=source_tags)
+        wal_vol = api.Volume(lv_name='volume3',
+                             lv_uuid='waluuid',
+                             vg_name='vg',
+                             lv_path='/dev/VolGroup/lv3',
+                             lv_tags=source_wal_tags)
+
+        self.mock_single_volumes = {'/dev/VolGroup/lv1': data_vol,
+                                    '/dev/VolGroup/lv3': wal_vol}
+
+        monkeypatch.setattr(migrate.api, 'get_first_lv',
+            self.mock_get_first_lv)
+
+        self.mock_process_input = []
+        monkeypatch.setattr(process, 'call', self.mock_process)
+
+        self.mock_volume = api.Volume(lv_name='target_volume1', lv_uuid='y',
+                                      vg_name='vg',
+                                      lv_path='/dev/VolGroup/target_volume',
+                                      lv_tags='')
+        monkeypatch.setattr(api, 'get_lv_by_fullname',
+            self.mock_get_lv_by_fullname)
+
+        monkeypatch.setattr("ceph_volume.systemd.systemctl.osd_is_active",
+            lambda id: True)
+
+        #find_associated_devices will call get_lvs() 4 times
+        # and it this needs results to be arranged that way
+        self.mock_volumes = []
+        self.mock_volumes.append([data_vol, wal_vol])
+        self.mock_volumes.append([data_vol])
+        self.mock_volumes.append([])
+        self.mock_volumes.append([wal_vol])
+
+        monkeypatch.setattr(migrate.api, 'get_lvs', self.mock_get_lvs)
+
+        monkeypatch.setattr(migrate, 'get_cluster_name',
+            lambda osd_id, osd_fsid: 'ceph_cluster')
+        monkeypatch.setattr(system, 'chown', lambda path: 0)
+
+        m = migrate.NewDB(argv=[
+            '--osd-id', '1',
+            '--osd-fsid', '55BD4219-16A7-4037-BC20-0F158EFCC83D',
+            '--target', 'vgname/new_db'])
+
+        with pytest.raises(SystemExit) as error:
+            m.main()
+
+        stdout, stderr = capsys.readouterr()
+
+        assert 'Unable to attach new volume for OSD: 1' == str(error.value)
+        assert '--> OSD ID is running, stop it with: systemctl stop ceph-osd@1' == stderr.rstrip()
+        assert not stdout
+
+    def test_newdb_no_systemd(self, is_root, monkeypatch):
+        source_tags = \
+        'ceph.osd_id=0,ceph.type=data,ceph.osd_fsid=1234,'\
+        'ceph.wal_uuid=wal_uuid,ceph.db_device=/dbdevice'
+        source_wal_tags = \
+        'ceph.wal_uuid=uuid,ceph.wal_device=device,' \
+        'ceph.osd_id=0,ceph.type=wal'
+
+        data_vol = api.Volume(lv_name='volume1', lv_uuid='datauuid',
+                              vg_name='vg',
+                              lv_path='/dev/VolGroup/lv1',
+                              lv_tags=source_tags)
+        wal_vol = api.Volume(lv_name='volume3',
+                             lv_uuid='waluuid',
+                             vg_name='vg',
+                             lv_path='/dev/VolGroup/lv3',
+                             lv_tags=source_wal_tags)
+
+        self.mock_single_volumes = {'/dev/VolGroup/lv1': data_vol,
+                                    '/dev/VolGroup/lv3': wal_vol}
+
+        monkeypatch.setattr(migrate.api, 'get_first_lv',
+            self.mock_get_first_lv)
+
+        self.mock_process_input = []
+        monkeypatch.setattr(process, 'call', self.mock_process)
+
+        self.mock_volume = api.Volume(lv_name='target_volume1', lv_uuid='y',
+                                      vg_name='vg',
+                                      lv_path='/dev/VolGroup/target_volume',
+                                      lv_tags='')
+        monkeypatch.setattr(api, 'get_lv_by_fullname',
+            self.mock_get_lv_by_fullname)
+
+        #find_associated_devices will call get_lvs() 4 times
+        # and it this needs results to be arranged that way
+        self.mock_volumes = []
+        self.mock_volumes.append([data_vol, wal_vol])
+        self.mock_volumes.append([data_vol])
+        self.mock_volumes.append([])
+        self.mock_volumes.append([wal_vol])
+
+        monkeypatch.setattr(migrate.api, 'get_lvs', self.mock_get_lvs)
+
+        monkeypatch.setattr(migrate, 'get_cluster_name',
+            lambda osd_id, osd_fsid: 'ceph_cluster')
+        monkeypatch.setattr(system, 'chown', lambda path: 0)
+
+        migrate.NewDB(argv=[
+            '--osd-id', '1',
+            '--osd-fsid', '55BD4219-16A7-4037-BC20-0F158EFCC83D',
+            '--target', 'vgname/new_db',
+            '--no-systemd']).main()
+
+        n = len(self.mock_process_input)
+        assert n >= 5
+
+        assert self.mock_process_input[n - 5] == [
+            'lvchange',
+            '--deltag', 'ceph.db_device=/dbdevice',
+            '/dev/VolGroup/lv1']
+        assert self.mock_process_input[n - 4] == [
+            'lvchange',
+            '--addtag', 'ceph.db_uuid=y',
+            '--addtag', 'ceph.db_device=/dev/VolGroup/target_volume',
+            '/dev/VolGroup/lv1']
+
+        assert self.mock_process_input[n - 3].sort() == [
+            'lvchange',
+            '--addtag', 'ceph.wal_uuid=uuid',
+            '--addtag', 'ceph.osd_id=0',
+            '--addtag', 'ceph.type=db',
+            '--addtag', 'ceph.osd_fsid=1234',
+            '--addtag', 'ceph.db_uuid=y',
+            '--addtag', 'ceph.db_device=/dev/VolGroup/target_volume',
+            '/dev/VolGroup/target_volume'].sort()
+
+        assert self.mock_process_input[n - 2] == [
+            'lvchange',
+            '--addtag', 'ceph.db_uuid=y',
+            '--addtag', 'ceph.db_device=/dev/VolGroup/target_volume',
+            '/dev/VolGroup/lv3']
+
+        assert self.mock_process_input[n - 1] == [
+            'ceph-bluestore-tool',
+            '--path', '/var/lib/ceph/osd/ceph_cluster-1',
+            '--dev-target', '/dev/VolGroup/target_volume',
+            '--command', 'bluefs-bdev-new-db']
+
     @patch('os.getuid')
     def test_newwal(self, m_getuid, monkeypatch, capsys):
         m_getuid.return_value = 0
@@ -726,6 +880,116 @@ class TestNew(object):
             '--dev-target', '/dev/VolGroup/target_volume',
             '--command', 'bluefs-bdev-new-wal']
 
+    def test_newwal_active_systemd(self, is_root, monkeypatch, capsys):
+        source_tags = \
+        'ceph.osd_id=0,ceph.type=data,ceph.osd_fsid=1234'
+
+        data_vol = api.Volume(lv_name='volume1', lv_uuid='datauuid', vg_name='vg',
+                         lv_path='/dev/VolGroup/lv1', lv_tags=source_tags)
+
+        self.mock_single_volumes = {'/dev/VolGroup/lv1': data_vol}
+
+        monkeypatch.setattr(migrate.api, 'get_first_lv', self.mock_get_first_lv)
+
+        self.mock_process_input = []
+        monkeypatch.setattr(process, 'call', self.mock_process)
+
+        self.mock_volume = api.Volume(lv_name='target_volume1', lv_uuid='y', vg_name='vg',
+                                      lv_path='/dev/VolGroup/target_volume',
+                                      lv_tags='')
+        monkeypatch.setattr(api, 'get_lv_by_fullname', self.mock_get_lv_by_fullname)
+
+        monkeypatch.setattr("ceph_volume.systemd.systemctl.osd_is_active", lambda id: True)
+
+        #find_associated_devices will call get_lvs() 4 times
+        # and it this needs results to be arranged that way
+        self.mock_volumes = []
+        self.mock_volumes.append([data_vol])
+        self.mock_volumes.append([data_vol])
+        self.mock_volumes.append([])
+        self.mock_volumes.append([])
+
+        monkeypatch.setattr(migrate.api, 'get_lvs', self.mock_get_lvs)
+
+        monkeypatch.setattr(migrate, 'get_cluster_name', lambda osd_id, osd_fsid: 'cluster')
+        monkeypatch.setattr(system, 'chown', lambda path: 0)
+
+        m = migrate.NewWAL(argv=[
+            '--osd-id', '2',
+            '--osd-fsid', '55BD4219-16A7-4037-BC20-0F158EFCC83D',
+            '--target', 'vgname/new_wal'])
+
+        with pytest.raises(SystemExit) as error:
+            m.main()
+
+        stdout, stderr = capsys.readouterr()
+
+        assert 'Unable to attach new volume for OSD: 2' == str(error.value)
+        assert '--> OSD ID is running, stop it with: systemctl stop ceph-osd@2' == stderr.rstrip()
+        assert not stdout
+
+    def test_newwal_no_systemd(self, is_root, monkeypatch):
+        source_tags = \
+        'ceph.osd_id=0,ceph.type=data,ceph.osd_fsid=1234'
+
+        data_vol = api.Volume(lv_name='volume1', lv_uuid='datauuid', vg_name='vg',
+                         lv_path='/dev/VolGroup/lv1', lv_tags=source_tags)
+
+        self.mock_single_volumes = {'/dev/VolGroup/lv1': data_vol}
+
+        monkeypatch.setattr(migrate.api, 'get_first_lv', self.mock_get_first_lv)
+
+        self.mock_process_input = []
+        monkeypatch.setattr(process, 'call', self.mock_process)
+
+        self.mock_volume = api.Volume(lv_name='target_volume1', lv_uuid='y', vg_name='vg',
+                                      lv_path='/dev/VolGroup/target_volume',
+                                      lv_tags='')
+        monkeypatch.setattr(api, 'get_lv_by_fullname', self.mock_get_lv_by_fullname)
+
+        #find_associated_devices will call get_lvs() 4 times
+        # and it this needs results to be arranged that way
+        self.mock_volumes = []
+        self.mock_volumes.append([data_vol])
+        self.mock_volumes.append([data_vol])
+        self.mock_volumes.append([])
+        self.mock_volumes.append([])
+
+        monkeypatch.setattr(migrate.api, 'get_lvs', self.mock_get_lvs)
+
+        monkeypatch.setattr(migrate, 'get_cluster_name', lambda osd_id, osd_fsid: 'cluster')
+        monkeypatch.setattr(system, 'chown', lambda path: 0)
+
+        migrate.NewWAL(argv=[
+            '--osd-id', '2',
+            '--osd-fsid', '55BD4219-16A7-4037-BC20-0F158EFCC83D',
+            '--target', 'vgname/new_wal',
+            '--no-systemd']).main()
+
+        n = len(self.mock_process_input)
+        assert n >= 3
+
+        assert self.mock_process_input[n - 3] == [
+            'lvchange',
+            '--addtag', 'ceph.wal_uuid=y',
+            '--addtag', 'ceph.wal_device=/dev/VolGroup/target_volume',
+            '/dev/VolGroup/lv1']
+
+        assert self.mock_process_input[n - 2].sort() == [
+            'lvchange',
+            '--addtag', 'ceph.osd_id=0',
+            '--addtag', 'ceph.type=wal',
+            '--addtag', 'ceph.osd_fsid=1234',
+            '--addtag', 'ceph.wal_uuid=y',
+            '--addtag', 'ceph.wal_device=/dev/VolGroup/target_volume',
+            '/dev/VolGroup/target_volume'].sort()
+
+        assert self.mock_process_input[n - 1] == [
+            'ceph-bluestore-tool',
+            '--path', '/var/lib/ceph/osd/cluster-2',
+            '--dev-target', '/dev/VolGroup/target_volume',
+            '--command', 'bluefs-bdev-new-wal']
+
 class TestMigrate(object):
 
     mock_volume = None
@@ -758,7 +1022,402 @@ class TestMigrate(object):
                               lv_path='/dev/VolGroup/lv1',
                               lv_tags=source_tags)
         db_vol = api.Volume(lv_name='volume2',
-                            lv_uuid='datauuid',
+                            lv_uuid='datauuid',
+                            vg_name='vg',
+                            lv_path='/dev/VolGroup/lv2',
+                            lv_tags=source_db_tags)
+
+        wal_vol = api.Volume(lv_name='volume3',
+                             lv_uuid='datauuid',
+                             vg_name='vg',
+                             lv_path='/dev/VolGroup/lv3',
+                             lv_tags=source_wal_tags)
+
+        self.mock_single_volumes = {
+            '/dev/VolGroup/lv1': data_vol,
+            '/dev/VolGroup/lv2': db_vol,
+            '/dev/VolGroup/lv3': wal_vol,
+        }
+        monkeypatch.setattr(migrate.api, 'get_first_lv',
+            self.mock_get_first_lv)
+
+        self.mock_volume = api.Volume(lv_name='volume2', lv_uuid='y',
+                                      vg_name='vg',
+                                      lv_path='/dev/VolGroup/lv2',
+                                      lv_tags='ceph.osd_id=5,ceph.osd_type=db')
+        monkeypatch.setattr(api, 'get_lv_by_fullname',
+            self.mock_get_lv_by_fullname)
+
+        self.mock_process_input = []
+        monkeypatch.setattr(process, 'call', self.mock_process)
+
+        devices = []
+        devices.append([Device('/dev/VolGroup/lv1'), 'block'])
+        devices.append([Device('/dev/VolGroup/lv2'), 'db'])
+        devices.append([Device('/dev/VolGroup/lv3'), 'wal'])
+
+        monkeypatch.setattr(migrate, 'find_associated_devices',
+            lambda osd_id, osd_fsid: devices)
+
+
+        argv = [
+            '--osd-id', '2',
+            '--osd-fsid', '55BD4219-16A7-4037-BC20-0F158EFCC83D',
+            '--from', 'data', 'wal',
+            '--target', 'vgname/new_wal'
+        ]
+        m = migrate.Migrate(argv=argv)
+        m.args = m.make_parser('ceph-volume lvm migation', 'help').parse_args(argv)
+        res_devices = m.get_source_devices(devices)
+
+        assert 2 == len(res_devices)
+        assert devices[0] == res_devices[0]
+        assert devices[2] == res_devices[1]
+
+        argv = [
+            '--osd-id', '2',
+            '--osd-fsid', '55BD4219-16A7-4037-BC20-0F158EFCC83D',
+            '--from', 'db', 'wal', 'data',
+            '--target', 'vgname/new_wal'
+        ]
+        m = migrate.Migrate(argv=argv)
+        m.args = m.make_parser('ceph-volume lvm migation', 'help').parse_args(argv)
+        res_devices = m.get_source_devices(devices)
+
+        assert 3 == len(res_devices)
+        assert devices[0] == res_devices[0]
+        assert devices[1] == res_devices[1]
+        assert devices[2] == res_devices[2]
+
+
+    def test_migrate_without_args(self, capsys):
+        help_msg = """
+Moves BlueFS data from source volume(s) to the target one, source
+volumes (except the main (i.e. data or block) one) are removed on
+success. LVM volumes are permitted for Target only, both already
+attached or new logical one. In the latter case it is attached to OSD
+replacing one of the source devices. Following replacement rules apply
+(in the order of precedence, stop on the first match):
+* if source list has DB volume - target device replaces it.
+* if source list has WAL volume - target device replace it.
+* if source list has slow volume only - operation is not permitted,
+  requires explicit allocation via new-db/new-wal command.
+
+Example calls for supported scenarios:
+
+  Moves BlueFS data from main device to LV already attached as DB:
+
+    ceph-volume lvm migrate --osd-id 1 --osd-fsid <uuid> --from data --target vgname/db
+
+  Moves BlueFS data from shared main device to LV which will be attached
+   as a new DB:
+
+    ceph-volume lvm migrate --osd-id 1 --osd-fsid <uuid> --from data --target vgname/new_db
+
+  Moves BlueFS data from DB device to new LV, DB is replaced:
+
+    ceph-volume lvm migrate --osd-id 1 --osd-fsid <uuid> --from db --target vgname/new_db
+
+  Moves BlueFS data from main and DB devices to new LV, DB is replaced:
+
+    ceph-volume lvm migrate --osd-id 1 --osd-fsid <uuid> --from data db --target vgname/new_db
+
+  Moves BlueFS data from main, DB and WAL devices to new LV, WAL is
+   removed and DB is replaced:
+
+    ceph-volume lvm migrate --osd-id 1 --osd-fsid <uuid> --from data db wal --target vgname/new_db
+
+  Moves BlueFS data from main, DB and WAL devices to main device, WAL
+   and DB are removed:
+
+    ceph-volume lvm migrate --osd-id 1 --osd-fsid <uuid> --from db wal --target vgname/data
+
+"""
+        m = migrate.Migrate(argv=[])
+        m.main()
+        stdout, stderr = capsys.readouterr()
+        assert help_msg in stdout
+        assert not stderr
+
+
+    @patch('os.getuid')
+    def test_migrate_data_db_to_new_db(self, m_getuid, monkeypatch):
+        m_getuid.return_value = 0
+
+        source_tags = 'ceph.osd_id=2,ceph.type=data,ceph.osd_fsid=1234,' \
+        'ceph.cluster_name=ceph,ceph.db_uuid=dbuuid,ceph.db_device=db_dev'
+        source_db_tags = 'ceph.osd_id=2,ceph.type=db,ceph.osd_fsid=1234,' \
+        'ceph.cluster_name=ceph,ceph.db_uuid=dbuuid,ceph.db_device=db_dev'
+
+        data_vol = api.Volume(lv_name='volume1',
+                              lv_uuid='datauuid',
+                              vg_name='vg',
+                              lv_path='/dev/VolGroup/lv1',
+                              lv_tags=source_tags)
+        db_vol = api.Volume(lv_name='volume2',
+                            lv_uuid='dbuuid',
+                            vg_name='vg',
+                            lv_path='/dev/VolGroup/lv2',
+                            lv_tags=source_db_tags)
+
+        self.mock_single_volumes = {
+            '/dev/VolGroup/lv1': data_vol,
+            '/dev/VolGroup/lv2': db_vol,
+        }
+        monkeypatch.setattr(migrate.api, 'get_first_lv',
+            self.mock_get_first_lv)
+
+        self.mock_volume = api.Volume(lv_name='volume2_new', lv_uuid='new-db-uuid',
+                                      vg_name='vg',
+                                      lv_path='/dev/VolGroup/lv2_new',
+                                      lv_tags='')
+        monkeypatch.setattr(api, 'get_lv_by_fullname',
+            self.mock_get_lv_by_fullname)
+
+        self.mock_process_input = []
+        monkeypatch.setattr(process, 'call', self.mock_process)
+
+        devices = []
+        devices.append([Device('/dev/VolGroup/lv1'), 'block'])
+        devices.append([Device('/dev/VolGroup/lv2'), 'db'])
+
+        monkeypatch.setattr(migrate, 'find_associated_devices',
+            lambda osd_id, osd_fsid: devices)
+
+
+        monkeypatch.setattr("ceph_volume.systemd.systemctl.osd_is_active",
+            lambda id: False)
+
+        monkeypatch.setattr(migrate, 'get_cluster_name',
+            lambda osd_id, osd_fsid: 'ceph')
+        monkeypatch.setattr(system, 'chown', lambda path: 0)
+        m = migrate.Migrate(argv=[
+            '--osd-id', '2',
+            '--osd-fsid', '1234',
+            '--from', 'data', 'db', 'wal',
+            '--target', 'vgname/new_wal'])
+        m.main()
+
+        n = len(self.mock_process_input)
+        assert n >= 5
+
+        assert self. mock_process_input[n-5] == [
+            'lvchange',
+            '--deltag', 'ceph.osd_id=2',
+            '--deltag', 'ceph.type=db',
+            '--deltag', 'ceph.osd_fsid=1234',
+            '--deltag', 'ceph.cluster_name=ceph',
+            '--deltag', 'ceph.db_uuid=dbuuid',
+            '--deltag', 'ceph.db_device=db_dev',
+            '/dev/VolGroup/lv2']
+
+        assert self. mock_process_input[n-4] == [
+            'lvchange',
+            '--deltag', 'ceph.db_uuid=dbuuid',
+            '--deltag', 'ceph.db_device=db_dev',
+            '/dev/VolGroup/lv1']
+
+        assert self. mock_process_input[n-3] == [
+            'lvchange',
+            '--addtag', 'ceph.db_uuid=new-db-uuid',
+            '--addtag', 'ceph.db_device=/dev/VolGroup/lv2_new',
+            '/dev/VolGroup/lv1']
+
+        assert self. mock_process_input[n-2] == [
+            'lvchange',
+            '--addtag', 'ceph.osd_id=2',
+            '--addtag', 'ceph.type=db',
+            '--addtag', 'ceph.osd_fsid=1234',
+            '--addtag', 'ceph.cluster_name=ceph',
+            '--addtag', 'ceph.db_uuid=new-db-uuid',
+            '--addtag', 'ceph.db_device=/dev/VolGroup/lv2_new',
+            '/dev/VolGroup/lv2_new']
+
+        assert self. mock_process_input[n-1] == [
+            'ceph-bluestore-tool',
+            '--path', '/var/lib/ceph/osd/ceph-2',
+            '--dev-target', '/dev/VolGroup/lv2_new',
+            '--command', 'bluefs-bdev-migrate',
+            '--devs-source', '/var/lib/ceph/osd/ceph-2/block',
+            '--devs-source', '/var/lib/ceph/osd/ceph-2/block.db']
+
+    def test_migrate_data_db_to_new_db_active_systemd(self, is_root, monkeypatch, capsys):
+        source_tags = 'ceph.osd_id=2,ceph.type=data,ceph.osd_fsid=1234,' \
+        'ceph.cluster_name=ceph,ceph.db_uuid=dbuuid,ceph.db_device=db_dev'
+        source_db_tags = 'ceph.osd_id=2,ceph.type=db,ceph.osd_fsid=1234,' \
+        'ceph.cluster_name=ceph,ceph.db_uuid=dbuuid,ceph.db_device=db_dev'
+
+        data_vol = api.Volume(lv_name='volume1',
+                              lv_uuid='datauuid',
+                              vg_name='vg',
+                              lv_path='/dev/VolGroup/lv1',
+                              lv_tags=source_tags)
+        db_vol = api.Volume(lv_name='volume2',
+                            lv_uuid='dbuuid',
+                            vg_name='vg',
+                            lv_path='/dev/VolGroup/lv2',
+                            lv_tags=source_db_tags)
+
+        self.mock_single_volumes = {
+            '/dev/VolGroup/lv1': data_vol,
+            '/dev/VolGroup/lv2': db_vol,
+        }
+        monkeypatch.setattr(migrate.api, 'get_first_lv',
+            self.mock_get_first_lv)
+
+        self.mock_volume = api.Volume(lv_name='volume2_new', lv_uuid='new-db-uuid',
+                                      vg_name='vg',
+                                      lv_path='/dev/VolGroup/lv2_new',
+                                      lv_tags='')
+        monkeypatch.setattr(api, 'get_lv_by_fullname',
+            self.mock_get_lv_by_fullname)
+
+        self.mock_process_input = []
+        monkeypatch.setattr(process, 'call', self.mock_process)
+
+        devices = []
+        devices.append([Device('/dev/VolGroup/lv1'), 'block'])
+        devices.append([Device('/dev/VolGroup/lv2'), 'db'])
+
+        monkeypatch.setattr(migrate, 'find_associated_devices',
+            lambda osd_id, osd_fsid: devices)
+
+
+        monkeypatch.setattr("ceph_volume.systemd.systemctl.osd_is_active",
+            lambda id: True)
+
+        monkeypatch.setattr(migrate, 'get_cluster_name',
+            lambda osd_id, osd_fsid: 'ceph')
+        monkeypatch.setattr(system, 'chown', lambda path: 0)
+        m = migrate.Migrate(argv=[
+            '--osd-id', '2',
+            '--osd-fsid', '1234',
+            '--from', 'data', 'db', 'wal',
+            '--target', 'vgname/new_wal'])
+
+        with pytest.raises(SystemExit) as error:
+            m.main()
+
+        stdout, stderr = capsys.readouterr()
+
+        assert 'Unable to migrate devices associated with OSD ID: 2' == str(error.value)
+        assert '--> OSD is running, stop it with: systemctl stop ceph-osd@2' == stderr.rstrip()
+        assert not stdout
+
+    def test_migrate_data_db_to_new_db_no_systemd(self, is_root, monkeypatch):
+        source_tags = 'ceph.osd_id=2,ceph.type=data,ceph.osd_fsid=1234,' \
+        'ceph.cluster_name=ceph,ceph.db_uuid=dbuuid,ceph.db_device=db_dev'
+        source_db_tags = 'ceph.osd_id=2,ceph.type=db,ceph.osd_fsid=1234,' \
+        'ceph.cluster_name=ceph,ceph.db_uuid=dbuuid,ceph.db_device=db_dev'
+
+        data_vol = api.Volume(lv_name='volume1',
+                              lv_uuid='datauuid',
+                              vg_name='vg',
+                              lv_path='/dev/VolGroup/lv1',
+                              lv_tags=source_tags)
+        db_vol = api.Volume(lv_name='volume2',
+                            lv_uuid='dbuuid',
+                            vg_name='vg',
+                            lv_path='/dev/VolGroup/lv2',
+                            lv_tags=source_db_tags)
+
+        self.mock_single_volumes = {
+            '/dev/VolGroup/lv1': data_vol,
+            '/dev/VolGroup/lv2': db_vol,
+        }
+        monkeypatch.setattr(migrate.api, 'get_first_lv',
+            self.mock_get_first_lv)
+
+        self.mock_volume = api.Volume(lv_name='volume2_new', lv_uuid='new-db-uuid',
+                                      vg_name='vg',
+                                      lv_path='/dev/VolGroup/lv2_new',
+                                      lv_tags='')
+        monkeypatch.setattr(api, 'get_lv_by_fullname',
+            self.mock_get_lv_by_fullname)
+
+        self.mock_process_input = []
+        monkeypatch.setattr(process, 'call', self.mock_process)
+
+        devices = []
+        devices.append([Device('/dev/VolGroup/lv1'), 'block'])
+        devices.append([Device('/dev/VolGroup/lv2'), 'db'])
+
+        monkeypatch.setattr(migrate, 'find_associated_devices',
+            lambda osd_id, osd_fsid: devices)
+
+
+        monkeypatch.setattr(migrate, 'get_cluster_name',
+            lambda osd_id, osd_fsid: 'ceph')
+        monkeypatch.setattr(system, 'chown', lambda path: 0)
+        m = migrate.Migrate(argv=[
+            '--osd-id', '2',
+            '--osd-fsid', '1234',
+            '--from', 'data', 'db', 'wal',
+            '--target', 'vgname/new_wal',
+            '--no-systemd'])
+        m.main()
+
+        n = len(self.mock_process_input)
+        assert n >= 5
+
+        assert self. mock_process_input[n-5] == [
+            'lvchange',
+            '--deltag', 'ceph.osd_id=2',
+            '--deltag', 'ceph.type=db',
+            '--deltag', 'ceph.osd_fsid=1234',
+            '--deltag', 'ceph.cluster_name=ceph',
+            '--deltag', 'ceph.db_uuid=dbuuid',
+            '--deltag', 'ceph.db_device=db_dev',
+            '/dev/VolGroup/lv2']
+
+        assert self. mock_process_input[n-4] == [
+            'lvchange',
+            '--deltag', 'ceph.db_uuid=dbuuid',
+            '--deltag', 'ceph.db_device=db_dev',
+            '/dev/VolGroup/lv1']
+
+        assert self. mock_process_input[n-3] == [
+            'lvchange',
+            '--addtag', 'ceph.db_uuid=new-db-uuid',
+            '--addtag', 'ceph.db_device=/dev/VolGroup/lv2_new',
+            '/dev/VolGroup/lv1']
+
+        assert self. mock_process_input[n-2] == [
+            'lvchange',
+            '--addtag', 'ceph.osd_id=2',
+            '--addtag', 'ceph.type=db',
+            '--addtag', 'ceph.osd_fsid=1234',
+            '--addtag', 'ceph.cluster_name=ceph',
+            '--addtag', 'ceph.db_uuid=new-db-uuid',
+            '--addtag', 'ceph.db_device=/dev/VolGroup/lv2_new',
+            '/dev/VolGroup/lv2_new']
+
+        assert self. mock_process_input[n-1] == [
+            'ceph-bluestore-tool',
+            '--path', '/var/lib/ceph/osd/ceph-2',
+            '--dev-target', '/dev/VolGroup/lv2_new',
+            '--command', 'bluefs-bdev-migrate',
+            '--devs-source', '/var/lib/ceph/osd/ceph-2/block',
+            '--devs-source', '/var/lib/ceph/osd/ceph-2/block.db']
+
+    @patch('os.getuid')
+    def test_migrate_data_db_to_new_db_skip_wal(self, m_getuid, monkeypatch):
+        m_getuid.return_value = 0
+
+        source_tags = 'ceph.osd_id=2,ceph.type=data,ceph.osd_fsid=1234,' \
+        'ceph.cluster_name=ceph,ceph.db_uuid=dbuuid,ceph.db_device=db_dev'
+        source_db_tags = 'ceph.osd_id=2,ceph.type=db,ceph.osd_fsid=1234,' \
+        'ceph.cluster_name=ceph,ceph.db_uuid=dbuuid,ceph.db_device=db_dev'
+        source_wal_tags = 'ceph.osd_id=2,ceph.type=wal,ceph.osd_fsid=1234' \
+        'ceph.cluster_name=ceph,ceph.db_uuid=dbuuid,ceph.db_device=db_dev'
+
+        data_vol = api.Volume(lv_name='volume1',
+                              lv_uuid='datauuid',
+                              vg_name='vg',
+                              lv_path='/dev/VolGroup/lv1',
+                              lv_tags=source_tags)
+        db_vol = api.Volume(lv_name='volume2',
+                            lv_uuid='dbuuid',
                             vg_name='vg',
                             lv_path='/dev/VolGroup/lv2',
                             lv_tags=source_db_tags)
@@ -777,10 +1436,10 @@ class TestMigrate(object):
         monkeypatch.setattr(migrate.api, 'get_first_lv',
             self.mock_get_first_lv)
 
-        self.mock_volume = api.Volume(lv_name='volume2', lv_uuid='y',
+        self.mock_volume = api.Volume(lv_name='volume2_new', lv_uuid='new-db-uuid',
                                       vg_name='vg',
-                                      lv_path='/dev/VolGroup/lv2',
-                                      lv_tags='ceph.osd_id=5,ceph.osd_type=db')
+                                      lv_path='/dev/VolGroup/lv2_new',
+                                      lv_tags='')
         monkeypatch.setattr(api, 'get_lv_by_fullname',
             self.mock_get_lv_by_fullname)
 
@@ -795,41 +1454,86 @@ class TestMigrate(object):
         monkeypatch.setattr(migrate, 'find_associated_devices',
             lambda osd_id, osd_fsid: devices)
 
+        monkeypatch.setattr("ceph_volume.systemd.systemctl.osd_is_active",
+            lambda id: False)
 
+        monkeypatch.setattr(migrate, 'get_cluster_name',
+            lambda osd_id, osd_fsid: 'ceph')
+        monkeypatch.setattr(system, 'chown', lambda path: 0)
         m = migrate.Migrate(argv=[
             '--osd-id', '2',
-            '--osd-fsid', '55BD4219-16A7-4037-BC20-0F158EFCC83D',
-            '--from', 'data', 'wal',
+            '--osd-fsid', '1234',
+            '--from', 'data', 'db',
             '--target', 'vgname/new_wal'])
-        m.parse_argv()
-        res_devices = m.get_source_devices(devices)
+        m.main()
 
-        assert 2 == len(res_devices)
-        assert devices[0] == res_devices[0]
-        assert devices[2] == res_devices[1]
+        n = len(self.mock_process_input)
+        assert n >= 7
 
-        m = migrate.Migrate(argv=[
-            '--osd-id', '2',
-            '--osd-fsid', '55BD4219-16A7-4037-BC20-0F158EFCC83D',
-            '--from', 'db', 'wal', 'data',
-            '--target', 'vgname/new_wal'])
-        m.parse_argv()
-        res_devices = m.get_source_devices(devices)
+        assert self. mock_process_input[n-7] == [
+            'lvchange',
+            '--deltag', 'ceph.osd_id=2',
+            '--deltag', 'ceph.type=db',
+            '--deltag', 'ceph.osd_fsid=1234',
+            '--deltag', 'ceph.cluster_name=ceph',
+            '--deltag', 'ceph.db_uuid=dbuuid',
+            '--deltag', 'ceph.db_device=db_dev',
+            '/dev/VolGroup/lv2']
 
-        assert 3 == len(res_devices)
-        assert devices[0] == res_devices[0]
-        assert devices[1] == res_devices[1]
-        assert devices[2] == res_devices[2]
+        assert self. mock_process_input[n-6] == [
+            'lvchange',
+            '--deltag', 'ceph.db_uuid=dbuuid',
+            '--deltag', 'ceph.db_device=db_dev',
+            '/dev/VolGroup/lv1']
+
+        assert self. mock_process_input[n-5] == [
+            'lvchange',
+            '--addtag', 'ceph.db_uuid=new-db-uuid',
+            '--addtag', 'ceph.db_device=/dev/VolGroup/lv2_new',
+            '/dev/VolGroup/lv1']
+
+        assert self. mock_process_input[n-4] == [
+            'lvchange',
+            '--deltag', 'ceph.db_uuid=dbuuid',
+            '--deltag', 'ceph.db_device=db_dev',
+            '/dev/VolGroup/lv3']
+
+        assert self. mock_process_input[n-3] == [
+            'lvchange',
+            '--addtag', 'ceph.db_uuid=new-db-uuid',
+            '--addtag', 'ceph.db_device=/dev/VolGroup/lv2_new',
+            '/dev/VolGroup/lv3']
+
+        assert self. mock_process_input[n-2] == [
+            'lvchange',
+            '--addtag', 'ceph.osd_id=2',
+            '--addtag', 'ceph.type=db',
+            '--addtag', 'ceph.osd_fsid=1234',
+            '--addtag', 'ceph.cluster_name=ceph',
+            '--addtag', 'ceph.db_uuid=new-db-uuid',
+            '--addtag', 'ceph.db_device=/dev/VolGroup/lv2_new',
+            '/dev/VolGroup/lv2_new']
 
+        assert self. mock_process_input[n-1] == [
+            'ceph-bluestore-tool',
+            '--path', '/var/lib/ceph/osd/ceph-2',
+            '--dev-target', '/dev/VolGroup/lv2_new',
+            '--command', 'bluefs-bdev-migrate',
+            '--devs-source', '/var/lib/ceph/osd/ceph-2/block',
+            '--devs-source', '/var/lib/ceph/osd/ceph-2/block.db']
 
     @patch('os.getuid')
-    def test_migrate_data_db_to_new_db(self, m_getuid, monkeypatch):
+    def test_migrate_data_db_wal_to_new_db(self, m_getuid, monkeypatch):
         m_getuid.return_value = 0
 
         source_tags = 'ceph.osd_id=2,ceph.type=data,ceph.osd_fsid=1234,' \
-        'ceph.cluster_name=ceph,ceph.db_uuid=dbuuid,ceph.db_device=db_dev'
+        'ceph.cluster_name=ceph,ceph.db_uuid=dbuuid,ceph.db_device=db_dev,' \
+        'ceph.wal_uuid=waluuid,ceph.wal_device=wal_dev'
         source_db_tags = 'ceph.osd_id=2,ceph.type=db,ceph.osd_fsid=1234,' \
         'ceph.cluster_name=ceph,ceph.db_uuid=dbuuid,ceph.db_device=db_dev'
+        source_wal_tags = 'ceph.osd_id=0,ceph.type=wal,ceph.osd_fsid=1234,' \
+        'ceph.cluster_name=ceph,ceph.db_uuid=dbuuid,ceph.db_device=db_dev,' \
+        'ceph.wal_uuid=waluuid,ceph.wal_device=wal_dev'
 
         data_vol = api.Volume(lv_name='volume1',
                               lv_uuid='datauuid',
@@ -842,9 +1546,16 @@ class TestMigrate(object):
                             lv_path='/dev/VolGroup/lv2',
                             lv_tags=source_db_tags)
 
+        wal_vol = api.Volume(lv_name='volume3',
+                             lv_uuid='waluuid',
+                             vg_name='vg',
+                             lv_path='/dev/VolGroup/lv3',
+                             lv_tags=source_wal_tags)
+
         self.mock_single_volumes = {
             '/dev/VolGroup/lv1': data_vol,
             '/dev/VolGroup/lv2': db_vol,
+            '/dev/VolGroup/lv3': wal_vol,
         }
         monkeypatch.setattr(migrate.api, 'get_first_lv',
             self.mock_get_first_lv)
@@ -862,11 +1573,11 @@ class TestMigrate(object):
         devices = []
         devices.append([Device('/dev/VolGroup/lv1'), 'block'])
         devices.append([Device('/dev/VolGroup/lv2'), 'db'])
+        devices.append([Device('/dev/VolGroup/lv3'), 'wal'])
 
         monkeypatch.setattr(migrate, 'find_associated_devices',
             lambda osd_id, osd_fsid: devices)
 
-
         monkeypatch.setattr("ceph_volume.systemd.systemctl.osd_is_active",
             lambda id: False)
 
@@ -881,9 +1592,9 @@ class TestMigrate(object):
         m.main()
 
         n = len(self.mock_process_input)
-        assert n >= 5
+        assert n >= 6
 
-        assert self. mock_process_input[n-5] == [
+        assert self. mock_process_input[n-6] == [
             'lvchange',
             '--deltag', 'ceph.osd_id=2',
             '--deltag', 'ceph.type=db',
@@ -893,10 +1604,24 @@ class TestMigrate(object):
             '--deltag', 'ceph.db_device=db_dev',
             '/dev/VolGroup/lv2']
 
+        assert self. mock_process_input[n-5] == [
+            'lvchange',
+            '--deltag', 'ceph.osd_id=0',
+            '--deltag', 'ceph.type=wal',
+            '--deltag', 'ceph.osd_fsid=1234',
+            '--deltag', 'ceph.cluster_name=ceph',
+            '--deltag', 'ceph.db_uuid=dbuuid',
+            '--deltag', 'ceph.db_device=db_dev',
+            '--deltag', 'ceph.wal_uuid=waluuid',
+            '--deltag', 'ceph.wal_device=wal_dev',
+            '/dev/VolGroup/lv3']
+
         assert self. mock_process_input[n-4] == [
             'lvchange',
             '--deltag', 'ceph.db_uuid=dbuuid',
             '--deltag', 'ceph.db_device=db_dev',
+            '--deltag', 'ceph.wal_uuid=waluuid',
+            '--deltag', 'ceph.wal_device=wal_dev',
             '/dev/VolGroup/lv1']
 
         assert self. mock_process_input[n-3] == [
@@ -921,18 +1646,20 @@ class TestMigrate(object):
             '--dev-target', '/dev/VolGroup/lv2_new',
             '--command', 'bluefs-bdev-migrate',
             '--devs-source', '/var/lib/ceph/osd/ceph-2/block',
-            '--devs-source', '/var/lib/ceph/osd/ceph-2/block.db']
+            '--devs-source', '/var/lib/ceph/osd/ceph-2/block.db',
+            '--devs-source', '/var/lib/ceph/osd/ceph-2/block.wal']
 
     @patch('os.getuid')
-    def test_migrate_data_db_to_new_db_skip_wal(self, m_getuid, monkeypatch):
+    def test_dont_migrate_data_db_wal_to_new_data(self,
+                                                  m_getuid,
+                                                  monkeypatch,
+                                                  capsys):
         m_getuid.return_value = 0
 
         source_tags = 'ceph.osd_id=2,ceph.type=data,ceph.osd_fsid=1234,' \
         'ceph.cluster_name=ceph,ceph.db_uuid=dbuuid,ceph.db_device=db_dev'
         source_db_tags = 'ceph.osd_id=2,ceph.type=db,ceph.osd_fsid=1234,' \
         'ceph.cluster_name=ceph,ceph.db_uuid=dbuuid,ceph.db_device=db_dev'
-        source_wal_tags = 'ceph.osd_id=2,ceph.type=wal,ceph.osd_fsid=1234' \
-        'ceph.cluster_name=ceph,ceph.db_uuid=dbuuid,ceph.db_device=db_dev'
 
         data_vol = api.Volume(lv_name='volume1',
                               lv_uuid='datauuid',
@@ -945,16 +1672,9 @@ class TestMigrate(object):
                             lv_path='/dev/VolGroup/lv2',
                             lv_tags=source_db_tags)
 
-        wal_vol = api.Volume(lv_name='volume3',
-                             lv_uuid='datauuid',
-                             vg_name='vg',
-                             lv_path='/dev/VolGroup/lv3',
-                             lv_tags=source_wal_tags)
-
         self.mock_single_volumes = {
             '/dev/VolGroup/lv1': data_vol,
             '/dev/VolGroup/lv2': db_vol,
-            '/dev/VolGroup/lv3': wal_vol,
         }
         monkeypatch.setattr(migrate.api, 'get_first_lv',
             self.mock_get_first_lv)
@@ -972,7 +1692,6 @@ class TestMigrate(object):
         devices = []
         devices.append([Device('/dev/VolGroup/lv1'), 'block'])
         devices.append([Device('/dev/VolGroup/lv2'), 'db'])
-        devices.append([Device('/dev/VolGroup/lv3'), 'wal'])
 
         monkeypatch.setattr(migrate, 'find_associated_devices',
             lambda osd_id, osd_fsid: devices)
@@ -986,67 +1705,99 @@ class TestMigrate(object):
         m = migrate.Migrate(argv=[
             '--osd-id', '2',
             '--osd-fsid', '1234',
-            '--from', 'data', 'db',
-            '--target', 'vgname/new_wal'])
-        m.main()
+            '--from', 'data',
+            '--target', 'vgname/new_data'])
 
-        n = len(self.mock_process_input)
-        assert n >= 7
+        with pytest.raises(SystemExit) as error:
+            m.main()
+        stdout, stderr = capsys.readouterr()
+        expected = 'Unable to migrate to : vgname/new_data'
+        assert expected in str(error.value)
+        expected = 'Unable to determine new volume type,'
+        ' please use new-db or new-wal command before.'
+        assert expected in stderr
 
-        assert self. mock_process_input[n-7] == [
-            'lvchange',
-            '--deltag', 'ceph.osd_id=2',
-            '--deltag', 'ceph.type=db',
-            '--deltag', 'ceph.osd_fsid=1234',
-            '--deltag', 'ceph.cluster_name=ceph',
-            '--deltag', 'ceph.db_uuid=dbuuid',
-            '--deltag', 'ceph.db_device=db_dev',
-            '/dev/VolGroup/lv2']
+    @patch('os.getuid')
+    def test_dont_migrate_db_to_wal(self,
+                                    m_getuid,
+                                    monkeypatch,
+                                    capsys):
+        m_getuid.return_value = 0
 
-        assert self. mock_process_input[n-6] == [
-            'lvchange',
-            '--deltag', 'ceph.db_uuid=dbuuid',
-            '--deltag', 'ceph.db_device=db_dev',
-            '/dev/VolGroup/lv1']
+        source_tags = 'ceph.osd_id=2,ceph.type=data,ceph.osd_fsid=1234,' \
+        'ceph.cluster_name=ceph,ceph.db_uuid=dbuuid,ceph.db_device=db_dev,' \
+        'ceph.wal_uuid=waluuid,ceph.wal_device=wal_dev'
+        source_db_tags = 'ceph.osd_id=2,ceph.type=db,ceph.osd_fsid=1234,' \
+        'ceph.cluster_name=ceph,ceph.db_uuid=dbuuid,ceph.db_device=db_dev'
+        source_wal_tags = 'ceph.osd_id=2,ceph.type=wal,ceph.osd_fsid=1234,' \
+        'ceph.cluster_name=ceph,ceph.db_uuid=dbuuid,ceph.db_device=db_dev,' \
+        'ceph.wal_uuid=waluuid,ceph.wal_device=wal_dev'
 
-        assert self. mock_process_input[n-5] == [
-            'lvchange',
-            '--addtag', 'ceph.db_uuid=new-db-uuid',
-            '--addtag', 'ceph.db_device=/dev/VolGroup/lv2_new',
-            '/dev/VolGroup/lv1']
+        data_vol = api.Volume(lv_name='volume1',
+                              lv_uuid='datauuid',
+                              vg_name='vg',
+                              lv_path='/dev/VolGroup/lv1',
+                              lv_tags=source_tags)
+        db_vol = api.Volume(lv_name='volume2',
+                            lv_uuid='dbuuid',
+                            vg_name='vg',
+                            lv_path='/dev/VolGroup/lv2',
+                            lv_tags=source_db_tags)
 
-        assert self. mock_process_input[n-4] == [
-            'lvchange',
-            '--deltag', 'ceph.db_uuid=dbuuid',
-            '--deltag', 'ceph.db_device=db_dev',
-            '/dev/VolGroup/lv3']
+        wal_vol = api.Volume(lv_name='volume3',
+                             lv_uuid='waluuid',
+                             vg_name='vg',
+                             lv_path='/dev/VolGroup/lv3',
+                             lv_tags=source_wal_tags)
 
-        assert self. mock_process_input[n-3] == [
-            'lvchange',
-            '--addtag', 'ceph.db_uuid=new-db-uuid',
-            '--addtag', 'ceph.db_device=/dev/VolGroup/lv2_new',
-            '/dev/VolGroup/lv3']
+        self.mock_single_volumes = {
+            '/dev/VolGroup/lv1': data_vol,
+            '/dev/VolGroup/lv2': db_vol,
+            '/dev/VolGroup/lv3': wal_vol,
+        }
+        monkeypatch.setattr(migrate.api, 'get_first_lv',
+            self.mock_get_first_lv)
 
-        assert self. mock_process_input[n-2] == [
-            'lvchange',
-            '--addtag', 'ceph.osd_id=2',
-            '--addtag', 'ceph.type=db',
-            '--addtag', 'ceph.osd_fsid=1234',
-            '--addtag', 'ceph.cluster_name=ceph',
-            '--addtag', 'ceph.db_uuid=new-db-uuid',
-            '--addtag', 'ceph.db_device=/dev/VolGroup/lv2_new',
-            '/dev/VolGroup/lv2_new']
+        self.mock_volume = wal_vol
+        monkeypatch.setattr(api, 'get_lv_by_fullname',
+            self.mock_get_lv_by_fullname)
 
-        assert self. mock_process_input[n-1] == [
-            'ceph-bluestore-tool',
-            '--path', '/var/lib/ceph/osd/ceph-2',
-            '--dev-target', '/dev/VolGroup/lv2_new',
-            '--command', 'bluefs-bdev-migrate',
-            '--devs-source', '/var/lib/ceph/osd/ceph-2/block',
-            '--devs-source', '/var/lib/ceph/osd/ceph-2/block.db']
+        self.mock_process_input = []
+        monkeypatch.setattr(process, 'call', self.mock_process)
+
+        devices = []
+        devices.append([Device('/dev/VolGroup/lv1'), 'block'])
+        devices.append([Device('/dev/VolGroup/lv2'), 'db'])
+        devices.append([Device('/dev/VolGroup/lv3'), 'wal'])
+
+        monkeypatch.setattr(migrate, 'find_associated_devices',
+            lambda osd_id, osd_fsid: devices)
+
+        monkeypatch.setattr("ceph_volume.systemd.systemctl.osd_is_active",
+            lambda id: False)
+
+        monkeypatch.setattr(migrate, 'get_cluster_name',
+            lambda osd_id, osd_fsid: 'ceph')
+        monkeypatch.setattr(system, 'chown', lambda path: 0)
+        m = migrate.Migrate(argv=[
+            '--osd-id', '2',
+            '--osd-fsid', '1234',
+            '--from', 'db',
+            '--target', 'vgname/wal'])
+
+        with pytest.raises(SystemExit) as error:
+            m.main()
+        stdout, stderr = capsys.readouterr()
+        expected = 'Unable to migrate to : vgname/wal'
+        assert expected in str(error.value)
+        expected = 'Migrate to WAL is not supported'
+        assert expected in stderr
 
     @patch('os.getuid')
-    def test_migrate_data_db_wal_to_new_db(self, m_getuid, monkeypatch):
+    def test_migrate_data_db_to_db(self,
+                                    m_getuid,
+                                    monkeypatch,
+                                    capsys):
         m_getuid.return_value = 0
 
         source_tags = 'ceph.osd_id=2,ceph.type=data,ceph.osd_fsid=1234,' \
@@ -1054,7 +1805,7 @@ class TestMigrate(object):
         'ceph.wal_uuid=waluuid,ceph.wal_device=wal_dev'
         source_db_tags = 'ceph.osd_id=2,ceph.type=db,ceph.osd_fsid=1234,' \
         'ceph.cluster_name=ceph,ceph.db_uuid=dbuuid,ceph.db_device=db_dev'
-        source_wal_tags = 'ceph.osd_id=0,ceph.type=wal,ceph.osd_fsid=1234,' \
+        source_wal_tags = 'ceph.osd_id=2,ceph.type=wal,ceph.osd_fsid=1234,' \
         'ceph.cluster_name=ceph,ceph.db_uuid=dbuuid,ceph.db_device=db_dev,' \
         'ceph.wal_uuid=waluuid,ceph.wal_device=wal_dev'
 
@@ -1083,10 +1834,7 @@ class TestMigrate(object):
         monkeypatch.setattr(migrate.api, 'get_first_lv',
             self.mock_get_first_lv)
 
-        self.mock_volume = api.Volume(lv_name='volume2_new', lv_uuid='new-db-uuid',
-                                      vg_name='vg',
-                                      lv_path='/dev/VolGroup/lv2_new',
-                                      lv_tags='')
+        self.mock_volume = db_vol
         monkeypatch.setattr(api, 'get_lv_by_fullname',
             self.mock_get_lv_by_fullname)
 
@@ -1110,79 +1858,32 @@ class TestMigrate(object):
         m = migrate.Migrate(argv=[
             '--osd-id', '2',
             '--osd-fsid', '1234',
-            '--from', 'data', 'db', 'wal',
-            '--target', 'vgname/new_wal'])
+            '--from', 'db', 'data',
+            '--target', 'vgname/db'])
+
         m.main()
 
         n = len(self.mock_process_input)
-        assert n >= 6
-
-        assert self. mock_process_input[n-6] == [
-            'lvchange',
-            '--deltag', 'ceph.osd_id=2',
-            '--deltag', 'ceph.type=db',
-            '--deltag', 'ceph.osd_fsid=1234',
-            '--deltag', 'ceph.cluster_name=ceph',
-            '--deltag', 'ceph.db_uuid=dbuuid',
-            '--deltag', 'ceph.db_device=db_dev',
-            '/dev/VolGroup/lv2']
-
-        assert self. mock_process_input[n-5] == [
-            'lvchange',
-            '--deltag', 'ceph.osd_id=0',
-            '--deltag', 'ceph.type=wal',
-            '--deltag', 'ceph.osd_fsid=1234',
-            '--deltag', 'ceph.cluster_name=ceph',
-            '--deltag', 'ceph.db_uuid=dbuuid',
-            '--deltag', 'ceph.db_device=db_dev',
-            '--deltag', 'ceph.wal_uuid=waluuid',
-            '--deltag', 'ceph.wal_device=wal_dev',
-            '/dev/VolGroup/lv3']
-
-        assert self. mock_process_input[n-4] == [
-            'lvchange',
-            '--deltag', 'ceph.db_uuid=dbuuid',
-            '--deltag', 'ceph.db_device=db_dev',
-            '--deltag', 'ceph.wal_uuid=waluuid',
-            '--deltag', 'ceph.wal_device=wal_dev',
-            '/dev/VolGroup/lv1']
-
-        assert self. mock_process_input[n-3] == [
-            'lvchange',
-            '--addtag', 'ceph.db_uuid=new-db-uuid',
-            '--addtag', 'ceph.db_device=/dev/VolGroup/lv2_new',
-            '/dev/VolGroup/lv1']
-
-        assert self. mock_process_input[n-2] == [
-            'lvchange',
-            '--addtag', 'ceph.osd_id=2',
-            '--addtag', 'ceph.type=db',
-            '--addtag', 'ceph.osd_fsid=1234',
-            '--addtag', 'ceph.cluster_name=ceph',
-            '--addtag', 'ceph.db_uuid=new-db-uuid',
-            '--addtag', 'ceph.db_device=/dev/VolGroup/lv2_new',
-            '/dev/VolGroup/lv2_new']
+        assert n >= 1
+        for s in self.mock_process_input:
+            print(s)
 
         assert self. mock_process_input[n-1] == [
             'ceph-bluestore-tool',
             '--path', '/var/lib/ceph/osd/ceph-2',
-            '--dev-target', '/dev/VolGroup/lv2_new',
+            '--dev-target', '/var/lib/ceph/osd/ceph-2/block.db',
             '--command', 'bluefs-bdev-migrate',
-            '--devs-source', '/var/lib/ceph/osd/ceph-2/block',
-            '--devs-source', '/var/lib/ceph/osd/ceph-2/block.db',
-            '--devs-source', '/var/lib/ceph/osd/ceph-2/block.wal']
-
-    @patch('os.getuid')
-    def test_dont_migrate_data_db_wal_to_new_data(self,
-                                                  m_getuid,
-                                                  monkeypatch,
-                                                  capsys):
-        m_getuid.return_value = 0
+            '--devs-source', '/var/lib/ceph/osd/ceph-2/block']
 
+    def test_migrate_data_db_to_db_active_systemd(self, is_root, monkeypatch, capsys):
         source_tags = 'ceph.osd_id=2,ceph.type=data,ceph.osd_fsid=1234,' \
-        'ceph.cluster_name=ceph,ceph.db_uuid=dbuuid,ceph.db_device=db_dev'
+        'ceph.cluster_name=ceph,ceph.db_uuid=dbuuid,ceph.db_device=db_dev,' \
+        'ceph.wal_uuid=waluuid,ceph.wal_device=wal_dev'
         source_db_tags = 'ceph.osd_id=2,ceph.type=db,ceph.osd_fsid=1234,' \
         'ceph.cluster_name=ceph,ceph.db_uuid=dbuuid,ceph.db_device=db_dev'
+        source_wal_tags = 'ceph.osd_id=2,ceph.type=wal,ceph.osd_fsid=1234,' \
+        'ceph.cluster_name=ceph,ceph.db_uuid=dbuuid,ceph.db_device=db_dev,' \
+        'ceph.wal_uuid=waluuid,ceph.wal_device=wal_dev'
 
         data_vol = api.Volume(lv_name='volume1',
                               lv_uuid='datauuid',
@@ -1195,17 +1896,21 @@ class TestMigrate(object):
                             lv_path='/dev/VolGroup/lv2',
                             lv_tags=source_db_tags)
 
+        wal_vol = api.Volume(lv_name='volume3',
+                             lv_uuid='waluuid',
+                             vg_name='vg',
+                             lv_path='/dev/VolGroup/lv3',
+                             lv_tags=source_wal_tags)
+
         self.mock_single_volumes = {
             '/dev/VolGroup/lv1': data_vol,
             '/dev/VolGroup/lv2': db_vol,
+            '/dev/VolGroup/lv3': wal_vol,
         }
         monkeypatch.setattr(migrate.api, 'get_first_lv',
             self.mock_get_first_lv)
 
-        self.mock_volume = api.Volume(lv_name='volume2_new', lv_uuid='new-db-uuid',
-                                      vg_name='vg',
-                                      lv_path='/dev/VolGroup/lv2_new',
-                                      lv_tags='')
+        self.mock_volume = db_vol
         monkeypatch.setattr(api, 'get_lv_by_fullname',
             self.mock_get_lv_by_fullname)
 
@@ -1215,12 +1920,13 @@ class TestMigrate(object):
         devices = []
         devices.append([Device('/dev/VolGroup/lv1'), 'block'])
         devices.append([Device('/dev/VolGroup/lv2'), 'db'])
+        devices.append([Device('/dev/VolGroup/lv3'), 'wal'])
 
         monkeypatch.setattr(migrate, 'find_associated_devices',
             lambda osd_id, osd_fsid: devices)
 
         monkeypatch.setattr("ceph_volume.systemd.systemctl.osd_is_active",
-            lambda id: False)
+            lambda id: True)
 
         monkeypatch.setattr(migrate, 'get_cluster_name',
             lambda osd_id, osd_fsid: 'ceph')
@@ -1228,25 +1934,19 @@ class TestMigrate(object):
         m = migrate.Migrate(argv=[
             '--osd-id', '2',
             '--osd-fsid', '1234',
-            '--from', 'data',
-            '--target', 'vgname/new_data'])
+            '--from', 'db', 'data',
+            '--target', 'vgname/db'])
 
         with pytest.raises(SystemExit) as error:
             m.main()
+
         stdout, stderr = capsys.readouterr()
-        expected = 'Unable to migrate to : vgname/new_data'
-        assert expected in str(error.value)
-        expected = 'Unable to determine new volume type,'
-        ' please use new-db or new-wal command before.'
-        assert expected in stderr
 
-    @patch('os.getuid')
-    def test_dont_migrate_db_to_wal(self,
-                                    m_getuid,
-                                    monkeypatch,
-                                    capsys):
-        m_getuid.return_value = 0
+        assert 'Unable to migrate devices associated with OSD ID: 2' == str(error.value)
+        assert '--> OSD is running, stop it with: systemctl stop ceph-osd@2' == stderr.rstrip()
+        assert not stdout
 
+    def test_migrate_data_db_to_db_no_systemd(self, is_root, monkeypatch):
         source_tags = 'ceph.osd_id=2,ceph.type=data,ceph.osd_fsid=1234,' \
         'ceph.cluster_name=ceph,ceph.db_uuid=dbuuid,ceph.db_device=db_dev,' \
         'ceph.wal_uuid=waluuid,ceph.wal_device=wal_dev'
@@ -1281,7 +1981,7 @@ class TestMigrate(object):
         monkeypatch.setattr(migrate.api, 'get_first_lv',
             self.mock_get_first_lv)
 
-        self.mock_volume = wal_vol
+        self.mock_volume = db_vol
         monkeypatch.setattr(api, 'get_lv_by_fullname',
             self.mock_get_lv_by_fullname)
 
@@ -1296,28 +1996,32 @@ class TestMigrate(object):
         monkeypatch.setattr(migrate, 'find_associated_devices',
             lambda osd_id, osd_fsid: devices)
 
-        monkeypatch.setattr("ceph_volume.systemd.systemctl.osd_is_active",
-            lambda id: False)
-
         monkeypatch.setattr(migrate, 'get_cluster_name',
             lambda osd_id, osd_fsid: 'ceph')
         monkeypatch.setattr(system, 'chown', lambda path: 0)
         m = migrate.Migrate(argv=[
             '--osd-id', '2',
             '--osd-fsid', '1234',
-            '--from', 'db',
-            '--target', 'vgname/wal'])
+            '--from', 'db', 'data',
+            '--target', 'vgname/db',
+            '--no-systemd'])
 
-        with pytest.raises(SystemExit) as error:
-            m.main()
-        stdout, stderr = capsys.readouterr()
-        expected = 'Unable to migrate to : vgname/wal'
-        assert expected in str(error.value)
-        expected = 'Migrate to WAL is not supported'
-        assert expected in stderr
+        m.main()
+
+        n = len(self.mock_process_input)
+        assert n >= 1
+        for s in self.mock_process_input:
+            print(s)
+
+        assert self. mock_process_input[n-1] == [
+            'ceph-bluestore-tool',
+            '--path', '/var/lib/ceph/osd/ceph-2',
+            '--dev-target', '/var/lib/ceph/osd/ceph-2/block.db',
+            '--command', 'bluefs-bdev-migrate',
+            '--devs-source', '/var/lib/ceph/osd/ceph-2/block']
 
     @patch('os.getuid')
-    def test_migrate_data_db_to_db(self,
+    def test_migrate_data_wal_to_db(self,
                                     m_getuid,
                                     monkeypatch,
                                     capsys):
@@ -1327,7 +2031,8 @@ class TestMigrate(object):
         'ceph.cluster_name=ceph,ceph.db_uuid=dbuuid,ceph.db_device=db_dev,' \
         'ceph.wal_uuid=waluuid,ceph.wal_device=wal_dev'
         source_db_tags = 'ceph.osd_id=2,ceph.type=db,ceph.osd_fsid=1234,' \
-        'ceph.cluster_name=ceph,ceph.db_uuid=dbuuid,ceph.db_device=db_dev'
+        'ceph.cluster_name=ceph,ceph.db_uuid=dbuuid,ceph.db_device=db_dev,' \
+        'ceph.wal_uuid=waluuid,ceph.wal_device=wal_dev'
         source_wal_tags = 'ceph.osd_id=2,ceph.type=wal,ceph.osd_fsid=1234,' \
         'ceph.cluster_name=ceph,ceph.db_uuid=dbuuid,ceph.db_device=db_dev,' \
         'ceph.wal_uuid=waluuid,ceph.wal_device=wal_dev'
@@ -1381,7 +2086,7 @@ class TestMigrate(object):
         m = migrate.Migrate(argv=[
             '--osd-id', '2',
             '--osd-fsid', '1234',
-            '--from', 'db', 'data',
+            '--from', 'db', 'data', 'wal',
             '--target', 'vgname/db'])
 
         m.main()
@@ -1391,20 +2096,36 @@ class TestMigrate(object):
         for s in self.mock_process_input:
             print(s)
 
+        assert self. mock_process_input[n-4] == [
+            'lvchange',
+            '--deltag', 'ceph.osd_id=2',
+            '--deltag', 'ceph.type=wal',
+            '--deltag', 'ceph.osd_fsid=1234',
+            '--deltag', 'ceph.cluster_name=ceph',
+            '--deltag', 'ceph.db_uuid=dbuuid',
+            '--deltag', 'ceph.db_device=db_dev',
+            '--deltag', 'ceph.wal_uuid=waluuid',
+            '--deltag', 'ceph.wal_device=wal_dev',
+            '/dev/VolGroup/lv3']
+        assert self. mock_process_input[n-3] == [
+            'lvchange',
+            '--deltag', 'ceph.wal_uuid=waluuid',
+            '--deltag', 'ceph.wal_device=wal_dev',
+            '/dev/VolGroup/lv1']
+        assert self. mock_process_input[n-2] == [
+            'lvchange',
+            '--deltag', 'ceph.wal_uuid=waluuid',
+            '--deltag', 'ceph.wal_device=wal_dev',
+            '/dev/VolGroup/lv2']
         assert self. mock_process_input[n-1] == [
             'ceph-bluestore-tool',
             '--path', '/var/lib/ceph/osd/ceph-2',
             '--dev-target', '/var/lib/ceph/osd/ceph-2/block.db',
             '--command', 'bluefs-bdev-migrate',
-            '--devs-source', '/var/lib/ceph/osd/ceph-2/block']
-
-    @patch('os.getuid')
-    def test_migrate_data_wal_to_db(self,
-                                    m_getuid,
-                                    monkeypatch,
-                                    capsys):
-        m_getuid.return_value = 0
+            '--devs-source', '/var/lib/ceph/osd/ceph-2/block',
+            '--devs-source', '/var/lib/ceph/osd/ceph-2/block.wal']
 
+    def test_migrate_data_wal_to_db_active_systemd(self, is_root, monkeypatch, capsys):
         source_tags = 'ceph.osd_id=2,ceph.type=data,ceph.osd_fsid=1234,' \
         'ceph.cluster_name=ceph,ceph.db_uuid=dbuuid,ceph.db_device=db_dev,' \
         'ceph.wal_uuid=waluuid,ceph.wal_device=wal_dev'
@@ -1456,7 +2177,7 @@ class TestMigrate(object):
             lambda osd_id, osd_fsid: devices)
 
         monkeypatch.setattr("ceph_volume.systemd.systemctl.osd_is_active",
-            lambda id: False)
+            lambda id: True)
 
         monkeypatch.setattr(migrate, 'get_cluster_name',
             lambda osd_id, osd_fsid: 'ceph')
@@ -1467,6 +2188,76 @@ class TestMigrate(object):
             '--from', 'db', 'data', 'wal',
             '--target', 'vgname/db'])
 
+        with pytest.raises(SystemExit) as error:
+            m.main()
+
+        stdout, stderr = capsys.readouterr()
+
+        assert 'Unable to migrate devices associated with OSD ID: 2' == str(error.value)
+        assert '--> OSD is running, stop it with: systemctl stop ceph-osd@2' == stderr.rstrip()
+        assert not stdout
+
+    def test_migrate_data_wal_to_db_no_systemd(self, is_root, monkeypatch):
+        source_tags = 'ceph.osd_id=2,ceph.type=data,ceph.osd_fsid=1234,' \
+        'ceph.cluster_name=ceph,ceph.db_uuid=dbuuid,ceph.db_device=db_dev,' \
+        'ceph.wal_uuid=waluuid,ceph.wal_device=wal_dev'
+        source_db_tags = 'ceph.osd_id=2,ceph.type=db,ceph.osd_fsid=1234,' \
+        'ceph.cluster_name=ceph,ceph.db_uuid=dbuuid,ceph.db_device=db_dev,' \
+        'ceph.wal_uuid=waluuid,ceph.wal_device=wal_dev'
+        source_wal_tags = 'ceph.osd_id=2,ceph.type=wal,ceph.osd_fsid=1234,' \
+        'ceph.cluster_name=ceph,ceph.db_uuid=dbuuid,ceph.db_device=db_dev,' \
+        'ceph.wal_uuid=waluuid,ceph.wal_device=wal_dev'
+
+        data_vol = api.Volume(lv_name='volume1',
+                              lv_uuid='datauuid',
+                              vg_name='vg',
+                              lv_path='/dev/VolGroup/lv1',
+                              lv_tags=source_tags)
+        db_vol = api.Volume(lv_name='volume2',
+                            lv_uuid='dbuuid',
+                            vg_name='vg',
+                            lv_path='/dev/VolGroup/lv2',
+                            lv_tags=source_db_tags)
+
+        wal_vol = api.Volume(lv_name='volume3',
+                             lv_uuid='waluuid',
+                             vg_name='vg',
+                             lv_path='/dev/VolGroup/lv3',
+                             lv_tags=source_wal_tags)
+
+        self.mock_single_volumes = {
+            '/dev/VolGroup/lv1': data_vol,
+            '/dev/VolGroup/lv2': db_vol,
+            '/dev/VolGroup/lv3': wal_vol,
+        }
+        monkeypatch.setattr(migrate.api, 'get_first_lv',
+            self.mock_get_first_lv)
+
+        self.mock_volume = db_vol
+        monkeypatch.setattr(api, 'get_lv_by_fullname',
+            self.mock_get_lv_by_fullname)
+
+        self.mock_process_input = []
+        monkeypatch.setattr(process, 'call', self.mock_process)
+
+        devices = []
+        devices.append([Device('/dev/VolGroup/lv1'), 'block'])
+        devices.append([Device('/dev/VolGroup/lv2'), 'db'])
+        devices.append([Device('/dev/VolGroup/lv3'), 'wal'])
+
+        monkeypatch.setattr(migrate, 'find_associated_devices',
+            lambda osd_id, osd_fsid: devices)
+
+        monkeypatch.setattr(migrate, 'get_cluster_name',
+            lambda osd_id, osd_fsid: 'ceph')
+        monkeypatch.setattr(system, 'chown', lambda path: 0)
+        m = migrate.Migrate(argv=[
+            '--osd-id', '2',
+            '--osd-fsid', '1234',
+            '--from', 'db', 'data', 'wal',
+            '--target', 'vgname/db',
+            '--no-systemd'])
+
         m.main()
 
         n = len(self.mock_process_input)
diff --git a/ceph/src/ceph-volume/ceph_volume/tests/devices/raw/test_list.py b/ceph/src/ceph-volume/ceph_volume/tests/devices/raw/test_list.py
new file mode 100644 (file)
index 0000000..d5ccee5
--- /dev/null
@@ -0,0 +1,235 @@
+import pytest
+from mock.mock import patch
+from ceph_volume.devices import raw
+
+# Sample lsblk output is below that overviews the test scenario. (--json output for reader clarity)
+#  - sda and all its children are used for the OS
+#  - sdb is a bluestore OSD with phantom Atari partitions
+#  - sdc is an empty disk
+#  - sdd has 2 LVM device children
+# > lsblk --paths --json
+#   {
+#      "blockdevices": [
+#         {"name": "/dev/sda", "maj:min": "8:0", "rm": "0", "size": "128G", "ro": "0", "type": "disk", "mountpoint": null,
+#            "children": [
+#               {"name": "/dev/sda1", "maj:min": "8:1", "rm": "0", "size": "487M", "ro": "0", "type": "part", "mountpoint": null},
+#               {"name": "/dev/sda2", "maj:min": "8:2", "rm": "0", "size": "1.9G", "ro": "0", "type": "part", "mountpoint": null},
+#               {"name": "/dev/sda3", "maj:min": "8:3", "rm": "0", "size": "125.6G", "ro": "0", "type": "part", "mountpoint": "/etc/hosts"}
+#            ]
+#         },
+#         {"name": "/dev/sdb", "maj:min": "8:16", "rm": "0", "size": "1T", "ro": "0", "type": "disk", "mountpoint": null,
+#            "children": [
+#               {"name": "/dev/sdb2", "maj:min": "8:18", "rm": "0", "size": "48G", "ro": "0", "type": "part", "mountpoint": null},
+#               {"name": "/dev/sdb3", "maj:min": "8:19", "rm": "0", "size": "6M", "ro": "0", "type": "part", "mountpoint": null}
+#            ]
+#         },
+#         {"name": "/dev/sdc", "maj:min": "8:32", "rm": "0", "size": "1T", "ro": "0", "type": "disk", "mountpoint": null},
+#         {"name": "/dev/sdd", "maj:min": "8:48", "rm": "0", "size": "1T", "ro": "0", "type": "disk", "mountpoint": null,
+#            "children": [
+#               {"name": "/dev/mapper/ceph--osd--block--1", "maj:min": "253:0", "rm": "0", "size": "512G", "ro": "0", "type": "lvm", "mountpoint": null},
+#               {"name": "/dev/mapper/ceph--osd--block--2", "maj:min": "253:1", "rm": "0", "size": "512G", "ro": "0", "type": "lvm", "mountpoint": null}
+#            ]
+#         }
+#      ]
+#   }
+
+def _devices_side_effect():
+    return {
+        "/dev/sda": {},
+        "/dev/sda1": {},
+        "/dev/sda2": {},
+        "/dev/sda3": {},
+        "/dev/sdb": {},
+        "/dev/sdb2": {},
+        "/dev/sdb3": {},
+        "/dev/sdc": {},
+        "/dev/sdd": {},
+        "/dev/mapper/ceph--osd--block--1": {},
+        "/dev/mapper/ceph--osd--block--2": {},
+    }
+
+def _lsblk_list_output():
+    return [
+        '/dev/sda',
+        '/dev/sda1',
+        '/dev/sda2',
+        '/dev/sda3',
+        '/dev/sdb',
+        '/dev/sdb2',
+        '/dev/sdb3',
+        '/dev/sdc',
+        '/dev/sdd',
+        '/dev/mapper/ceph--osd--block--1',
+        '/dev/mapper/ceph--osd--block--2',
+    ]
+
+# dummy lsblk output for device with optional parent output
+def _lsblk_output(dev, parent=None):
+    if parent is None:
+        parent = ""
+    ret = 'NAME="{}" KNAME="{}" PKNAME="{}"'.format(dev, dev, parent)
+    return [ret] # needs to be in a list form
+
+def _bluestore_tool_label_output_sdb():
+    return '''{
+    "/dev/sdb": {
+        "osd_uuid": "sdb-uuid",
+        "size": 1099511627776,
+        "btime": "2021-07-23T16:02:22.809186+0000",
+        "description": "main",
+        "bfm_blocks": "268435456",
+        "bfm_blocks_per_key": "128",
+        "bfm_bytes_per_block": "4096",
+        "bfm_size": "1099511627776",
+        "bluefs": "1",
+        "ceph_fsid": "sdb-fsid",
+        "kv_backend": "rocksdb",
+        "magic": "ceph osd volume v026",
+        "mkfs_done": "yes",
+        "osd_key": "AQAO6PpgK+y4CBAAixq/X7OVimbaezvwD/cDmg==",
+        "ready": "ready",
+        "require_osd_release": "16",
+        "whoami": "0"
+    }
+}'''
+
+def _bluestore_tool_label_output_sdb2():
+    return '''{
+    "/dev/sdb2": {
+        "osd_uuid": "sdb2-uuid",
+        "size": 1099511627776,
+        "btime": "2021-07-23T16:02:22.809186+0000",
+        "description": "main",
+        "bfm_blocks": "268435456",
+        "bfm_blocks_per_key": "128",
+        "bfm_bytes_per_block": "4096",
+        "bfm_size": "1099511627776",
+        "bluefs": "1",
+        "ceph_fsid": "sdb2-fsid",
+        "kv_backend": "rocksdb",
+        "magic": "ceph osd volume v026",
+        "mkfs_done": "yes",
+        "osd_key": "AQAO6PpgK+y4CBAAixq/X7OVimbaezvwD/cDmg==",
+        "ready": "ready",
+        "require_osd_release": "16",
+        "whoami": "2"
+    }
+}'''
+
+def _bluestore_tool_label_output_dm_okay():
+    return '''{
+    "/dev/mapper/ceph--osd--block--1": {
+        "osd_uuid": "lvm-1-uuid",
+        "size": 549751619584,
+        "btime": "2021-07-23T16:04:37.881060+0000",
+        "description": "main",
+        "bfm_blocks": "134216704",
+        "bfm_blocks_per_key": "128",
+        "bfm_bytes_per_block": "4096",
+        "bfm_size": "549751619584",
+        "bluefs": "1",
+        "ceph_fsid": "lvm-1-fsid",
+        "kv_backend": "rocksdb",
+        "magic": "ceph osd volume v026",
+        "mkfs_done": "yes",
+        "osd_key": "AQCU6Ppgz+UcIRAAh6IUjtPjiXBlEXfwO8ixzw==",
+        "ready": "ready",
+        "require_osd_release": "16",
+        "whoami": "2"
+    }
+}'''
+
+def _process_call_side_effect(command, **kw):
+    if "lsblk" in command:
+        if "/dev/" in command[-1]:
+            dev = command[-1]
+            if dev == "/dev/sda1" or dev == "/dev/sda2" or dev == "/dev/sda3":
+                return _lsblk_output(dev, parent="/dev/sda"), '', 0
+            if dev == "/dev/sdb2" or dev == "/dev/sdb3":
+                return _lsblk_output(dev, parent="/dev/sdb"), '', 0
+            if dev == "/dev/sda" or dev == "/dev/sdb" or dev == "/dev/sdc" or dev == "/dev/sdd":
+                return _lsblk_output(dev), '', 0
+            if "mapper" in dev:
+                return _lsblk_output(dev, parent="/dev/sdd"), '', 0
+            pytest.fail('dev {} needs behavior specified for it'.format(dev))
+        if "/dev/" not in command:
+            return _lsblk_list_output(), '', 0
+        pytest.fail('command {} needs behavior specified for it'.format(command))
+
+    if "ceph-bluestore-tool" in command:
+        if "/dev/sdb" in command:
+            # sdb is a bluestore OSD
+            return _bluestore_tool_label_output_sdb(), '', 0
+        if "/dev/sdb2" in command:
+            # sdb2 is a phantom atari partition that appears to have some valid bluestore info
+            return _bluestore_tool_label_output_sdb2(), '', 0
+        if "/dev/mapper/ceph--osd--block--1" in command:
+            # dm device 1 is a valid bluestore OSD (the other is corrupted/invalid)
+            return _bluestore_tool_label_output_dm_okay(), '', 0
+        # sda and children, sdb's children, sdc, sdd, dm device 2 all do NOT have bluestore OSD data
+        return [], 'fake No such file or directory error', 1
+    pytest.fail('command {} needs behavior specified for it'.format(command))
+
+def _has_bluestore_label_side_effect(disk_path):
+    if "/dev/sda" in disk_path:
+        return False # disk and all children are for the OS
+    if disk_path == "/dev/sdb":
+        return True # sdb is a valid bluestore OSD
+    if disk_path == "/dev/sdb2":
+        return True # sdb2 appears to be a valid bluestore OSD even though it should not be
+    if disk_path == "/dev/sdc":
+        return False # empty disk
+    if disk_path == "/dev/sdd":
+        return False # has LVM subdevices
+    if disk_path == "/dev/mapper/ceph--osd--block--1":
+        return True # good OSD
+    if disk_path == "/dev/mapper/ceph--osd--block--2":
+        return False # corrupted
+    pytest.fail('device {} needs behavior specified for it'.format(disk_path))
+
+class TestList(object):
+
+    @patch('ceph_volume.util.device.disk.get_devices')
+    @patch('ceph_volume.util.disk.has_bluestore_label')
+    @patch('ceph_volume.process.call')
+    def test_raw_list(self, patched_call, patched_bluestore_label, patched_get_devices):
+        raw.list.logger.setLevel("DEBUG")
+        patched_call.side_effect = _process_call_side_effect
+        patched_bluestore_label.side_effect = _has_bluestore_label_side_effect
+        patched_get_devices.side_effect = _devices_side_effect
+
+        result = raw.list.List([]).generate()
+        patched_call.assert_any_call(['lsblk', '--paths', '--output=NAME', '--noheadings', '--list'])
+        assert len(result) == 2
+
+        sdb = result['sdb-uuid']
+        assert sdb['osd_uuid'] == 'sdb-uuid'
+        assert sdb['osd_id'] == 0
+        assert sdb['device'] == '/dev/sdb'
+        assert sdb['ceph_fsid'] == 'sdb-fsid'
+        assert sdb['type'] == 'bluestore'
+
+        lvm1 = result['lvm-1-uuid']
+        assert lvm1['osd_uuid'] == 'lvm-1-uuid'
+        assert lvm1['osd_id'] == 2
+        assert lvm1['device'] == '/dev/mapper/ceph--osd--block--1'
+        assert lvm1['ceph_fsid'] == 'lvm-1-fsid'
+        assert lvm1['type'] == 'bluestore'
+
+    @patch('ceph_volume.util.device.disk.get_devices')
+    @patch('ceph_volume.util.disk.has_bluestore_label')
+    @patch('ceph_volume.process.call')
+    def test_raw_list_with_OSError(self, patched_call, patched_bluestore_label, patched_get_devices):
+        def _has_bluestore_label_side_effect_with_OSError(device_path):
+            if device_path == "/dev/sdd":
+                raise OSError('fake OSError')
+            return _has_bluestore_label_side_effect(device_path)
+
+        raw.list.logger.setLevel("DEBUG")
+        patched_call.side_effect = _process_call_side_effect
+        patched_bluestore_label.side_effect = _has_bluestore_label_side_effect_with_OSError
+        patched_get_devices.side_effect = _devices_side_effect
+
+        result = raw.list.List([]).generate()
+        assert len(result) == 1
+        assert 'sdb-uuid' in result
index a05eef6eddd0b3e0576e380bd61fdefdd9624140..0a47b5eb851e5f57a7ef3a2bff53ff6dde7f246e 100644 (file)
 - hosts: mons
   become: yes
   tasks:
+    - name: mark osds down
+      command: "ceph --cluster {{ cluster }} osd down osd.{{ item }}"
+      with_items:
+        - 0
+        - 2
 
     - name: destroy osd.2
       command: "ceph --cluster {{ cluster }} osd destroy osd.2 --yes-i-really-mean-it"
+      register: result
+      retries: 30
+      delay: 1
+      until: result is succeeded
 
     - name: destroy osd.0
       command: "ceph --cluster {{ cluster }} osd destroy osd.0 --yes-i-really-mean-it"
+      register: result
+      retries: 30
+      delay: 1
+      until: result is succeeded
 
 - hosts: osds
   become: yes
 - hosts: mons
   become: yes
   tasks:
+    - name: mark osds down
+      command: "ceph --cluster {{ cluster }} osd down osd.0"
 
     - name: destroy osd.0
       command: "ceph --cluster {{ cluster }} osd destroy osd.0 --yes-i-really-mean-it"
+      register: result
+      retries: 30
+      delay: 1
+      until: result is succeeded
 
 
 - hosts: osds
index f0408736ebfa8e5cc643eeae14c4c0889c420c5d..21eff00fa84659a2a83194db76198a3138ca9713 100644 (file)
 - hosts: mons
   become: yes
   tasks:
+    - name: mark osds down
+      command: "ceph --cluster {{ cluster }} osd down osd.{{ item }}"
+      with_items:
+        - 0
+        - 2
 
     - name: destroy osd.2
       command: "ceph --cluster {{ cluster }} osd destroy osd.2 --yes-i-really-mean-it"
+      register: result
+      retries: 30
+      delay: 1
+      until: result is succeeded
 
     - name: destroy osd.0
       command: "ceph --cluster {{ cluster }} osd destroy osd.0 --yes-i-really-mean-it"
-
+      register: result
+      retries: 30
+      delay: 1
+      until: result is succeeded
 
 - hosts: osds
   become: yes
index 2cf83e477fbd6d0533a61252e4e193b27f2c2419..97d77a7f4601aedf9a05d56bb5f252ad10289e93 100644 (file)
 - hosts: mons
   become: yes
   tasks:
+    - name: mark osds down
+      command: "ceph --cluster {{ cluster }} osd down osd.{{ item }}"
+      with_items:
+        - 0
+        - 2
 
     - name: destroy osd.2
       command: "ceph --cluster {{ cluster }} osd destroy osd.2 --yes-i-really-mean-it"
+      register: result
+      retries: 30
+      delay: 1
+      until: result is succeeded
 
     - name: destroy osd.0
       command: "ceph --cluster {{ cluster }} osd destroy osd.0 --yes-i-really-mean-it"
+      register: result
+      retries: 30
+      delay: 1
+      until: result is succeeded
 
 
 - hosts: osds
index 42ee40a1baa65f49e95f9e47df8f2cb4f0ddb84c..aca1f40a652c6b3dd8d5dd1c9b2599d2b554693c 100644 (file)
 - hosts: mons
   become: yes
   tasks:
+    - name: mark osds down
+      command: "ceph --cluster {{ cluster }} osd down osd.{{ item }}"
+      with_items:
+        - 0
+        - 2
 
     - name: destroy osd.2
       command: "ceph --cluster {{ cluster }} osd destroy osd.2 --yes-i-really-mean-it"
+      register: result
+      retries: 30
+      delay: 1
+      until: result is succeeded
 
     - name: destroy osd.0
       command: "ceph --cluster {{ cluster }} osd destroy osd.0 --yes-i-really-mean-it"
-
+      register: result
+      retries: 30
+      delay: 1
+      until: result is succeeded
 
 - hosts: osds
   become: yes
index d4565ef4dcb5c3f6e2edfaff331c5ada65348ceb..1e8a49e2624c246b0a2f9ff03008dc00283eee8a 100644 (file)
@@ -80,10 +80,10 @@ class TestValidDevice(object):
     def setup(self):
         self.validator = arg_validators.ValidDevice()
 
-    def test_path_is_valid(self, fake_call):
+    def test_path_is_valid(self, fake_call, patch_bluestore_label):
         result = self.validator('/')
         assert result.abspath == '/'
 
-    def test_path_is_invalid(self, fake_call):
+    def test_path_is_invalid(self, fake_call, patch_bluestore_label):
         with pytest.raises(argparse.ArgumentError):
             self.validator('/device/does/not/exist')
index ac8bc28afcbd7d250a78c9a8e201725be4081ecb..d27f89cbe1ca3064a465a3678d6e01e88a1830f5 100644 (file)
@@ -2,6 +2,7 @@ import pytest
 from copy import deepcopy
 from ceph_volume.util import device
 from ceph_volume.api import lvm as api
+from mock.mock import patch, mock_open
 
 
 class TestDevice(object):
@@ -124,7 +125,7 @@ class TestDevice(object):
 
     def test_is_partition(self, device_info):
         data = {"/dev/sda1": {"foo": "bar"}}
-        lsblk = {"TYPE": "part"}
+        lsblk = {"TYPE": "part", "PKNAME": "sda"}
         device_info(devices=data, lsblk=lsblk)
         disk = device.Device("/dev/sda1")
         assert disk.is_partition
@@ -138,14 +139,14 @@ class TestDevice(object):
 
     def test_is_not_lvm_memeber(self, device_info):
         data = {"/dev/sda1": {"foo": "bar"}}
-        lsblk = {"TYPE": "part"}
+        lsblk = {"TYPE": "part", "PKNAME": "sda"}
         device_info(devices=data, lsblk=lsblk)
         disk = device.Device("/dev/sda1")
         assert not disk.is_lvm_member
 
     def test_is_lvm_memeber(self, device_info):
         data = {"/dev/sda1": {"foo": "bar"}}
-        lsblk = {"TYPE": "part"}
+        lsblk = {"TYPE": "part", "PKNAME": "sda"}
         device_info(devices=data, lsblk=lsblk)
         disk = device.Device("/dev/sda1")
         assert not disk.is_lvm_member
@@ -246,6 +247,14 @@ class TestDevice(object):
         assert not disk.available
         assert "Has BlueStore device label" in disk.rejected_reasons
 
+    def test_reject_device_with_oserror(self, monkeypatch, patch_bluestore_label, device_info):
+        patch_bluestore_label.side_effect = OSError('test failure')
+        lsblk = {"TYPE": "disk"}
+        device_info(lsblk=lsblk)
+        disk = device.Device("/dev/sda")
+        assert not disk.available
+        assert "Failed to determine if device is BlueStore" in disk.rejected_reasons
+
     @pytest.mark.usefixtures("device_info_not_ceph_disk_member",
                              "disable_kernel_queries")
     def test_is_not_ceph_disk_member_lsblk(self, patch_bluestore_label):
@@ -294,7 +303,7 @@ class TestDevice(object):
     def test_used_by_ceph(self, device_info,
                           monkeypatch, ceph_type):
         data = {"/dev/sda": {"foo": "bar"}}
-        lsblk = {"TYPE": "part"}
+        lsblk = {"TYPE": "part", "PKNAME": "sda"}
         FooPVolume = api.PVolume(pv_name='/dev/sda', pv_uuid="0000",
                                  lv_uuid="0000", pv_tags={}, vg_name="vg")
         pvolumes = []
@@ -321,7 +330,7 @@ class TestDevice(object):
         pvolumes = []
         pvolumes.append(FooPVolume)
         data = {"/dev/sda": {"foo": "bar"}}
-        lsblk = {"TYPE": "part"}
+        lsblk = {"TYPE": "part", "PKNAME": "sda"}
         lv_data = {"lv_path": "vg/lv", "vg_name": "vg", "lv_uuid": "0000", "tags": {"ceph.osd_id": 0, "ceph.type": "journal"}}
         monkeypatch.setattr(api, 'get_pvs', lambda **kwargs: pvolumes)
 
@@ -336,31 +345,41 @@ class TestDevice(object):
         disk = device.Device("/dev/sda")
         assert disk._get_device_id() == 'ID_VENDOR_ID_MODEL_ID_SCSI_SERIAL'
 
+    def test_has_bluestore_label(self):
+        # patch device.Device __init__ function to do nothing since we want to only test the
+        # low-level behavior of has_bluestore_label
+        with patch.object(device.Device, "__init__", lambda self, path, with_lsm=False: None):
+            disk = device.Device("/dev/sda")
+            disk.abspath = "/dev/sda"
+            with patch('builtins.open', mock_open(read_data=b'bluestore block device\n')):
+                assert disk.has_bluestore_label
+            with patch('builtins.open', mock_open(read_data=b'not a bluestore block device\n')):
+                assert not disk.has_bluestore_label
 
 
 class TestDeviceEncryption(object):
 
     def test_partition_is_not_encrypted_lsblk(self, device_info):
-        lsblk = {'TYPE': 'part', 'FSTYPE': 'xfs'}
+        lsblk = {'TYPE': 'part', 'FSTYPE': 'xfs', 'PKNAME': 'sda'}
         device_info(lsblk=lsblk)
         disk = device.Device("/dev/sda")
         assert disk.is_encrypted is False
 
     def test_partition_is_encrypted_lsblk(self, device_info):
-        lsblk = {'TYPE': 'part', 'FSTYPE': 'crypto_LUKS'}
+        lsblk = {'TYPE': 'part', 'FSTYPE': 'crypto_LUKS', 'PKNAME': 'sda'}
         device_info(lsblk=lsblk)
         disk = device.Device("/dev/sda")
         assert disk.is_encrypted is True
 
     def test_partition_is_not_encrypted_blkid(self, device_info):
-        lsblk = {'TYPE': 'part'}
+        lsblk = {'TYPE': 'part', 'PKNAME': 'sda'}
         blkid = {'TYPE': 'ceph data'}
         device_info(lsblk=lsblk, blkid=blkid)
         disk = device.Device("/dev/sda")
         assert disk.is_encrypted is False
 
     def test_partition_is_encrypted_blkid(self, device_info):
-        lsblk = {'TYPE': 'part'}
+        lsblk = {'TYPE': 'part', 'PKNAME': 'sda'}
         blkid = {'TYPE': 'crypto_LUKS'}
         device_info(lsblk=lsblk, blkid=blkid)
         disk = device.Device("/dev/sda")
index 830f3bbe2b49a5a315f1dcf59f21a278bdceed34..b7d725bcdd5d90e2e81788ce729292b81c3cc7a6 100644 (file)
@@ -1,13 +1,18 @@
 # -*- coding: utf-8 -*-
 
+import logging
 import os
 from functools import total_ordering
-from ceph_volume import sys_info, process
+from ceph_volume import sys_info
 from ceph_volume.api import lvm
 from ceph_volume.util import disk, system
 from ceph_volume.util.lsmdisk import LSMDisk
 from ceph_volume.util.constants import ceph_disk_guids
 
+
+logger = logging.getLogger(__name__)
+
+
 report_template = """
 {dev:<25} {size:<12} {rot!s:<7} {available!s:<9} {model}"""
 
@@ -319,6 +324,12 @@ class Device(object):
     def size(self):
         return self.sys_api['size']
 
+    @property
+    def parent_device(self):
+        if 'PKNAME' in self.disk_api:
+            return '/dev/%s' % self.disk_api['PKNAME']
+        return None
+
     @property
     def lvm_size(self):
         """
@@ -348,12 +359,7 @@ class Device(object):
 
     @property
     def has_bluestore_label(self):
-        out, err, ret = process.call([
-            'ceph-bluestore-tool', 'show-label',
-            '--dev', self.abspath], verbose_on_failure=False)
-        if ret:
-            return False
-        return True
+        return disk.has_bluestore_label(self.abspath)
 
     @property
     def is_mapper(self):
@@ -476,8 +482,28 @@ class Device(object):
             rejected.append("Device type is not acceptable. It should be raw device or partition")
         if self.is_ceph_disk_member:
             rejected.append("Used by ceph-disk")
-        if self.has_bluestore_label:
-            rejected.append('Has BlueStore device label')
+
+        try:
+            if self.has_bluestore_label:
+                rejected.append('Has BlueStore device label')
+        except OSError as e:
+            # likely failed to open the device. assuming it is BlueStore is the safest option
+            # so that a possibly-already-existing OSD doesn't get overwritten
+            logger.error('failed to determine if device {} is BlueStore. device should not be used to avoid false negatives. err: {}'.format(self.abspath, e))
+            rejected.append('Failed to determine if device is BlueStore')
+
+        if self.is_partition:
+            try:
+                if disk.has_bluestore_label(self.parent_device):
+                    rejected.append('Parent has BlueStore device label')
+            except OSError as e:
+                # likely failed to open the device. assuming the parent is BlueStore is the safest
+                # option so that a possibly-already-existing OSD doesn't get overwritten
+                logger.error('failed to determine if partition {} (parent: {}) has a BlueStore parent. partition should not be used to avoid false negatives. err: {}'.format(self.abspath, self.parent_device, e))
+                rejected.append('Failed to determine if parent device is BlueStore')
+
+        if self.has_gpt_headers:
+            rejected.append('Has GPT headers')
         return rejected
 
     def _check_lvm_reject_reasons(self):
index df016c4e88269f84873ee4c24bbaffe8baa1a468..3d9e19c3efe4a85ca2a790164b22c190595c07e3 100644 (file)
@@ -24,7 +24,7 @@ def get_partuuid(device):
     device
     """
     out, err, rc = process.call(
-        ['blkid', '-s', 'PARTUUID', '-o', 'value', device]
+        ['blkid', '-c', '/dev/null', '-s', 'PARTUUID', '-o', 'value', device]
     )
     return ' '.join(out).strip()
 
@@ -98,7 +98,7 @@ def blkid(device):
     PART_ENTRY_UUID                 PARTUUID
     """
     out, err, rc = process.call(
-        ['blkid', '-p', device]
+        ['blkid', '-c', '/dev/null', '-p', device]
     )
     return _blkid_parser(' '.join(out))
 
@@ -110,7 +110,7 @@ def get_part_entry_type(device):
     used for udev rules, but it is useful in this case as it is the only
     consistent way to retrieve the GUID used by ceph-disk to identify devices.
     """
-    out, err, rc = process.call(['blkid', '-p', '-o', 'udev', device])
+    out, err, rc = process.call(['blkid', '-c', '/dev/null', '-p', '-o', 'udev', device])
     for line in out:
         if 'ID_PART_ENTRY_TYPE=' in line:
             return line.split('=')[-1].strip()
@@ -123,7 +123,7 @@ def get_device_from_partuuid(partuuid):
     device is
     """
     out, err, rc = process.call(
-        ['blkid', '-t', 'PARTUUID="%s"' % partuuid, '-o', 'device']
+        ['blkid', '-c', '/dev/null', '-t', 'PARTUUID="%s"' % partuuid, '-o', 'device']
     )
     return ' '.join(out).strip()
 
@@ -134,14 +134,13 @@ def remove_partition(device):
 
     :param device: A ``Device()`` object
     """
-    parent_device = '/dev/%s' % device.disk_api['PKNAME']
     udev_info = udevadm_property(device.abspath)
     partition_number = udev_info.get('ID_PART_ENTRY_NUMBER')
     if not partition_number:
         raise RuntimeError('Unable to detect the partition number for device: %s' % device.abspath)
 
     process.run(
-        ['parted', parent_device, '--script', '--', 'rm', partition_number]
+        ['parted', device.parent_device, '--script', '--', 'rm', partition_number]
     )
 
 
@@ -802,3 +801,17 @@ def get_devices(_sys_block_path='/sys/block'):
 
         device_facts[diskname] = metadata
     return device_facts
+
+def has_bluestore_label(device_path):
+    isBluestore = False
+    bluestoreDiskSignature = 'bluestore block device' # 22 bytes long
+
+    # throws OSError on failure
+    logger.info("opening device {} to check for BlueStore label".format(device_path))
+    with open(device_path, "rb") as fd:
+        # read first 22 bytes looking for bluestore disk signature
+        signature = fd.read(22)
+        if signature.decode('ascii', 'replace') == bluestoreDiskSignature:
+            isBluestore = True
+
+    return isBluestore
index 9b7348b8bc29c5881c05511b478ebcca728818a5..3dabb15f69dd151da6ef4810a9caabe0187fa981 100755 (executable)
@@ -1,7 +1,14 @@
 #!/usr/bin/python3
 
-DEFAULT_IMAGE='docker.io/ceph/ceph:v15'
-DEFAULT_IMAGE_IS_MASTER=False
+# Default container images -----------------------------------------------------
+DEFAULT_IMAGE = 'quay.io/ceph/ceph:v15'
+DEFAULT_IMAGE_IS_MASTER = False
+DEFAULT_PROMETHEUS_IMAGE = 'quay.io/prometheus/prometheus:v2.18.1'
+DEFAULT_NODE_EXPORTER_IMAGE = 'quay.io/prometheus/node-exporter:v0.18.1'
+DEFAULT_ALERT_MANAGER_IMAGE = 'quay.io/prometheus/alertmanager:v0.20.0'
+DEFAULT_GRAFANA_IMAGE = 'quay.io/ceph/ceph-grafana:6.7.4'
+# ------------------------------------------------------------------------------
+
 LATEST_STABLE_RELEASE = 'octopus'
 DATA_DIR = '/var/lib/ceph'
 LOG_DIR = '/var/log/ceph'
@@ -103,7 +110,7 @@ logging_config = {
     'disable_existing_loggers': True,
     'formatters': {
         'cephadm': {
-            'format': '%(asctime)s %(levelname)s %(message)s'
+            'format': '%(asctime)s %(thread)x %(levelname)s %(message)s'
         },
     },
     'handlers': {
@@ -163,7 +170,7 @@ class Monitoring(object):
 
     components = {
         "prometheus": {
-            "image": "docker.io/prom/prometheus:v2.18.1",
+            "image": DEFAULT_PROMETHEUS_IMAGE,
             "cpus": '2',
             "memory": '4GB',
             "args": [
@@ -176,7 +183,7 @@ class Monitoring(object):
             ],
         },
         "node-exporter": {
-            "image": "docker.io/prom/node-exporter:v0.18.1",
+            "image": DEFAULT_NODE_EXPORTER_IMAGE,
             "cpus": "1",
             "memory": "1GB",
             "args": [
@@ -184,7 +191,7 @@ class Monitoring(object):
             ],
         },
         "grafana": {
-            "image": "docker.io/ceph/ceph-grafana:6.7.4",
+            "image": DEFAULT_GRAFANA_IMAGE,
             "cpus": "2",
             "memory": "4GB",
             "args": [],
@@ -196,7 +203,7 @@ class Monitoring(object):
             ],
         },
         "alertmanager": {
-            "image": "docker.io/prom/alertmanager:v0.20.0",
+            "image": DEFAULT_ALERT_MANAGER_IMAGE,
             "cpus": "2",
             "memory": "2GB",
             "args": [
index 84792f7407f5c9ce3e45d194f3dbd1c7d5b90401..271528305bdb4f8dbc6a795e3dc6132ee450b4eb 100644 (file)
@@ -1184,6 +1184,7 @@ class BIVerObjEntry {
 
 public:
   BIVerObjEntry(cls_method_context_t& _hctx, const cls_rgw_obj_key& _key) : hctx(_hctx), key(_key), initialized(false) {
+    // empty
   }
 
   int init(bool check_delete_marker = true) {
@@ -1521,11 +1522,20 @@ static int rgw_bucket_link_olh(cls_method_context_t hctx, bufferlist *in, buffer
     return -EINVAL;
   }
 
-  BIVerObjEntry obj(hctx, op.key);
-  BIOLHEntry olh(hctx, op.key);
-
   /* read instance entry */
+  BIVerObjEntry obj(hctx, op.key);
   int ret = obj.init(op.delete_marker);
+
+  /* NOTE: When a delete is issued, a key instance is always provided,
+   * either the one for which the delete is requested or a new random
+   * one when no instance is specified. So we need to see which of
+   * these two cases we're dealing with. The variable `existed` will
+   * be true if the instance was specified and false if it was
+   * randomly generated. It might have been cleaner if the instance
+   * were empty and randomly generated here and returned in the reply,
+   * as that would better allow a typo in the instance id. This code
+   * should be audited and possibly cleaned up. */
+
   bool existed = (ret == 0);
   if (ret == -ENOENT && op.delete_marker) {
     ret = 0;
@@ -1534,6 +1544,28 @@ static int rgw_bucket_link_olh(cls_method_context_t hctx, bufferlist *in, buffer
     return ret;
   }
 
+  BIOLHEntry olh(hctx, op.key);
+  bool olh_read_attempt = false;
+  bool olh_found = false;
+  if (!existed && op.delete_marker) {
+    /* read olh */
+    ret = olh.init(&olh_found);
+    if (ret < 0) {
+      return ret;
+    }
+    olh_read_attempt = true;
+
+    // if we're deleting (i.e., adding a delete marker, and the OLH
+    // indicates it already refers to a delete marker, error out)
+    if (olh_found && olh.get_entry().delete_marker) {
+      CLS_LOG(10,
+             "%s: delete marker received for \"%s\" although OLH"
+             " already refers to a delete marker\n",
+             __func__, escape_str(op.key.to_string()).c_str());
+      return -ENOENT;
+    }
+  }
+
   if (existed && !real_clock::is_zero(op.unmod_since)) {
     timespec mtime = ceph::real_clock::to_timespec(obj.mtime());
     timespec unmod = ceph::real_clock::to_timespec(op.unmod_since);
@@ -1586,11 +1618,14 @@ static int rgw_bucket_link_olh(cls_method_context_t hctx, bufferlist *in, buffer
   }
 
   /* read olh */
-  bool olh_found;
-  ret = olh.init(&olh_found);
-  if (ret < 0) {
-    return ret;
+  if (!olh_read_attempt) { // only read if we didn't attempt earlier
+    ret = olh.init(&olh_found);
+    if (ret < 0) {
+      return ret;
+    }
+    olh_read_attempt = true;
   }
+
   const uint64_t prev_epoch = olh.get_epoch();
 
   if (!olh.start_modify(op.olh_epoch)) {
index 620811dbc4a25925a12561f531826749c2d4b0f7..3f5b41a4753f33a343fc7ceb28d5832d41ad9dbe 100644 (file)
@@ -1,13 +1,16 @@
 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
 // vim: ts=8 sw=2 smarttab
 
-#ifndef CEPH_CLS_RGW_TYPES_H
-#define CEPH_CLS_RGW_TYPES_H
+#pragma once
 
 #include <boost/container/flat_map.hpp>
 #include "common/ceph_time.h"
 #include "common/Formatter.h"
 
+#undef FMT_HEADER_ONLY
+#define FMT_HEADER_ONLY 1
+#include <fmt/format.h>
+
 #include "rgw/rgw_basic_types.h"
 
 #define CEPH_RGW_REMOVE 'r'
@@ -343,7 +346,15 @@ struct cls_rgw_obj_key {
   cls_rgw_obj_key(const string &_name) : name(_name) {}
   cls_rgw_obj_key(const string& n, const string& i) : name(n), instance(i) {}
 
-  void set(const string& _name) {
+  std::string to_string() const {
+    return fmt::format("{}({})", name, instance);
+  }
+
+  bool empty() const {
+    return name.empty();
+  }
+
+  void set(const std::string& _name) {
     name = _name;
   }
 
@@ -351,6 +362,7 @@ struct cls_rgw_obj_key {
     return (name.compare(k.name) == 0) &&
            (instance.compare(k.instance) == 0);
   }
+
   bool operator<(const cls_rgw_obj_key& k) const {
     int r = name.compare(k.name);
     if (r == 0) {
@@ -358,13 +370,17 @@ struct cls_rgw_obj_key {
     }
     return (r < 0);
   }
+
   bool operator<=(const cls_rgw_obj_key& k) const {
     return !(k < *this);
   }
-  bool empty() const {
-    return name.empty();
+
+  std::ostream& operator<<(std::ostream& out) const {
+    out << to_string();
+    return out;
   }
-  void encode(bufferlist &bl) const {
+
+  void encode(ceph::buffer::list &bl) const {
     ENCODE_START(1, 1, bl);
     encode(name, bl);
     encode(instance, bl);
@@ -1288,5 +1304,3 @@ struct cls_rgw_reshard_entry
   void get_key(string *key) const;
 };
 WRITE_CLASS_ENCODER(cls_rgw_reshard_entry)
-
-#endif
index 112ae105374402002f0593595a516c8e3a1da273..730a599500c23fa4b2bc69525626284711e09e75 100644 (file)
@@ -1239,8 +1239,12 @@ static ceph::spinlock debug_lock;
             buffer::create_aligned(unaligned._len, align_memory)));
         had_to_rebuild = true;
       }
-      _buffers.insert_after(p_prev, *ptr_node::create(unaligned._buffers.front()).release());
-      _num += 1;
+      if (unaligned.get_num_buffers()) {
+        _buffers.insert_after(p_prev, *ptr_node::create(unaligned._buffers.front()).release());
+        _num += 1;
+      } else {
+        // a bufferlist containing only 0-length bptrs is rebuilt as empty
+      }
       ++p_prev;
     }
     return had_to_rebuild;
index fcba93ce41f4ed8ec0b54162a36a4038d981ef1a..1a69f141141ade88c67e5b5f0c0a1a04659be2c0 100644 (file)
@@ -1461,7 +1461,6 @@ OPTION(rgw_data_log_num_shards, OPT_INT) // number of objects to keep data chang
 OPTION(rgw_data_log_obj_prefix, OPT_STR) //
 
 OPTION(rgw_bucket_quota_ttl, OPT_INT) // time for cached bucket stats to be cached within rgw instance
-OPTION(rgw_bucket_quota_soft_threshold, OPT_DOUBLE) // threshold from which we don't rely on cached info for quota decisions
 OPTION(rgw_bucket_quota_cache_size, OPT_INT) // number of entries in bucket quota cache
 OPTION(rgw_bucket_default_quota_max_objects, OPT_INT) // number of objects allowed
 OPTION(rgw_bucket_default_quota_max_size, OPT_LONGLONG) // Max size of object in bytes
index 85f4203be3686e466ca32ad73db66417bf47eb1a..3bdb69eafe1e5054c20eef048b749f93d4dbbdd7 100644 (file)
@@ -2569,7 +2569,7 @@ std::vector<Option> get_global_options() {
     .set_long_description("If this value is exceeded, the OSD will not read any new client data off of the network until memory is freed."),
 
     Option("osd_client_message_cap", Option::TYPE_UINT, Option::LEVEL_ADVANCED)
-    .set_default(0)
+    .set_default(256)
     .set_description("maximum number of in-flight client requests"),
 
     Option("osd_crush_update_weight_set", Option::TYPE_BOOL, Option::LEVEL_ADVANCED)
@@ -6510,16 +6510,6 @@ std::vector<Option> get_rgw_options() {
     .set_long_description(
         "Length of time for bucket stats to be cached within RGW instance."),
 
-    Option("rgw_bucket_quota_soft_threshold", Option::TYPE_FLOAT, Option::LEVEL_BASIC)
-    .set_default(0.95)
-    .set_description("RGW quota soft threshold")
-    .set_long_description(
-        "Threshold from which RGW doesn't rely on cached info for quota "
-        "decisions. This is done for higher accuracy of the quota mechanism at "
-        "cost of performance, when getting close to the quota limit. The value "
-        "configured here is the ratio between the data usage to the max usage "
-        "as specified by the quota."),
-
     Option("rgw_bucket_quota_cache_size", Option::TYPE_INT, Option::LEVEL_ADVANCED)
     .set_default(10000)
     .set_description("RGW quota stats cache size")
index 1318512d6ca1afc5c013fe082a941b94e97f6ffe..fdcbc3abb6814daa9ae219a5bcc66340ea1f9e4d 100644 (file)
@@ -16,6 +16,7 @@
 #include <memory>
 #include <optional>
 #include <poll.h>
+#include <regex>
 #include <sstream>
 #include <stdio.h>
 #include <stdlib.h>
@@ -612,6 +613,13 @@ retry:
   return 0;
 }
 
+// wrap any of * ? [ between square brackets
+static std::string escape_glob(const std::string& s)
+{
+  std::regex glob_meta("([*?[])");
+  return std::regex_replace(s, glob_meta, "[$1]");
+}
+
 static int __enumerate_devices(struct udev *udev, const krbd_spec& spec,
                                bool match_nspace, udev_enumerate_uptr *penm)
 {
@@ -628,13 +636,13 @@ retry:
     return r;
 
   r = udev_enumerate_add_match_sysattr(enm.get(), "pool",
-                                       spec.pool_name.c_str());
+                                       escape_glob(spec.pool_name).c_str());
   if (r < 0)
     return r;
 
   if (match_nspace) {
     r = udev_enumerate_add_match_sysattr(enm.get(), "pool_ns",
-                                         spec.nspace_name.c_str());
+                                         escape_glob(spec.nspace_name).c_str());
   } else {
     /*
      * Match _only_ devices that don't have pool_ns attribute.
@@ -646,12 +654,12 @@ retry:
     return r;
 
   r = udev_enumerate_add_match_sysattr(enm.get(), "name",
-                                       spec.image_name.c_str());
+                                       escape_glob(spec.image_name).c_str());
   if (r < 0)
     return r;
 
   r = udev_enumerate_add_match_sysattr(enm.get(), "current_snap",
-                                       spec.snap_name.c_str());
+                                       escape_glob(spec.snap_name).c_str());
   if (r < 0)
     return r;
 
index b32af4ba80920c8cd033092f176e921b75bbed15..dc2153fac7b25e4b929360525cf7b1ad7bacdce7 100644 (file)
@@ -5906,8 +5906,6 @@ int Monitor::mkfs(bufferlist& osdmapbl)
 
     r = ceph_resolve_file_search(g_conf()->keyring, keyring_filename);
     if (r) {
-      derr << "unable to find a keyring file on " << g_conf()->keyring
-          << ": " << cpp_strerror(r) << dendl;
       if (g_conf()->key != "") {
        string keyring_plaintext = "[mon.]\n\tkey = " + g_conf()->key +
          "\n\tcaps mon = \"allow *\"\n";
@@ -5923,7 +5921,9 @@ int Monitor::mkfs(bufferlist& osdmapbl)
          return -EINVAL;
        }
       } else {
-       return -ENOENT;
+       derr << "unable to find a keyring on " << g_conf()->keyring
+            << ": " << cpp_strerror(r) << dendl;
+       return r;
       }
     } else {
       r = keyring.load(g_ceph_context, keyring_filename);
index 877ce53a6fd4a288d2e9d6ae1421119563274161..3e77a48ea400262133c90b2b00df4608879a3066 100644 (file)
@@ -318,11 +318,14 @@ bool is_unmanaged_snap_op_permitted(CephContext* cct,
 
 } // anonymous namespace
 
-void LastEpochClean::Lec::report(ps_t ps, epoch_t last_epoch_clean)
+void LastEpochClean::Lec::report(unsigned pg_num, ps_t ps,
+                                epoch_t last_epoch_clean)
 {
-  if (epoch_by_pg.size() <= ps) {
-    epoch_by_pg.resize(ps + 1, 0);
+  if (ps >= pg_num) {
+    // removed PG
+    return;
   }
+  epoch_by_pg.resize(pg_num, 0);
   const auto old_lec = epoch_by_pg[ps];
   if (old_lec >= last_epoch_clean) {
     // stale lec
@@ -354,10 +357,11 @@ void LastEpochClean::remove_pool(uint64_t pool)
   report_by_pool.erase(pool);
 }
 
-void LastEpochClean::report(const pg_t& pg, epoch_t last_epoch_clean)
+void LastEpochClean::report(unsigned pg_num, const pg_t& pg,
+                           epoch_t last_epoch_clean)
 {
   auto& lec = report_by_pool[pg.pool()];
-  return lec.report(pg.ps(), last_epoch_clean);
+  return lec.report(pg_num, pg.ps(), last_epoch_clean);
 }
 
 epoch_t LastEpochClean::get_lower_bound(const OSDMap& latest) const
@@ -4322,7 +4326,10 @@ bool OSDMonitor::prepare_beacon(MonOpRequestRef op)
   osd_epochs[from] = beacon->version;
 
   for (const auto& pg : beacon->pgs) {
-    last_epoch_clean.report(pg, beacon->min_last_epoch_clean);
+    if (auto* pool = osdmap.get_pg_pool(pg.pool()); pool != nullptr) {
+      unsigned pg_num = pool->get_pg_num();
+      last_epoch_clean.report(pg_num, pg, beacon->min_last_epoch_clean);
+    }
   }
 
   if (osdmap.osd_xinfo[from].last_purged_snaps_scrub <
@@ -6085,6 +6092,13 @@ bool OSDMonitor::preprocess_command(MonOpRequestRef op)
       }
     } else /* var != "all" */  {
       choices_map_t::const_iterator found = ALL_CHOICES.find(var);
+      if (found == ALL_CHOICES.end()) {
+        ss << "pool '" << poolstr
+              << "': invalid variable: '" << var << "'";
+        r = -EINVAL;
+        goto reply;
+      }
+
       osd_pool_get_choices selected = found->second;
 
       if (!p->is_tier() &&
index fb941a6a0e56ef740a4dd75f514b92e7a7491a13..7986eb02d703b63aab216fafd7b08ce3568f4590 100644 (file)
@@ -111,11 +111,11 @@ class LastEpochClean {
     vector<epoch_t> epoch_by_pg;
     ps_t next_missing = 0;
     epoch_t floor = std::numeric_limits<epoch_t>::max();
-    void report(ps_t pg, epoch_t last_epoch_clean);
+    void report(unsigned pg_num, ps_t pg, epoch_t last_epoch_clean);
   };
   std::map<uint64_t, Lec> report_by_pool;
 public:
-  void report(const pg_t& pg, epoch_t last_epoch_clean);
+  void report(unsigned pg_num, const pg_t& pg, epoch_t last_epoch_clean);
   void remove_pool(uint64_t pool);
   epoch_t get_lower_bound(const OSDMap& latest) const;
 
index d7c8c397c35e084ab677dbaca685f70f6db3783b..ae804b5c725cbaab62e0f8e11663512e0cc16c9d 100644 (file)
@@ -952,7 +952,11 @@ void PGMapDigest::dump_object_stat_sum(
     if (verbose) {
       f->dump_int("quota_objects", pool->quota_max_objects);
       f->dump_int("quota_bytes", pool->quota_max_bytes);
-      f->dump_int("dirty", sum.num_objects_dirty);
+      if (pool->is_tier()) {
+        f->dump_int("dirty", sum.num_objects_dirty);
+      } else {
+        f->dump_int("dirty", 0);
+      }
       f->dump_int("rd", sum.num_rd);
       f->dump_int("rd_bytes", sum.num_rd_kb * 1024ull);
       f->dump_int("wr", sum.num_wr);
@@ -982,16 +986,17 @@ void PGMapDigest::dump_object_stat_sum(
         tbl << "N/A";
       else
         tbl << stringify(si_u_t(pool->quota_max_objects));
-
       if (pool->quota_max_bytes == 0)
         tbl << "N/A";
       else
         tbl << stringify(byte_u_t(pool->quota_max_bytes));
-
-      tbl << stringify(si_u_t(sum.num_objects_dirty))
-         << stringify(byte_u_t(statfs.data_compressed_allocated))
-         << stringify(byte_u_t(statfs.data_compressed_original))
-         ;
+      if (pool->is_tier()) {
+        tbl << stringify(si_u_t(sum.num_objects_dirty));
+      } else {
+        tbl << "N/A";
+      }
+      tbl << stringify(byte_u_t(statfs.data_compressed_allocated));
+      tbl << stringify(byte_u_t(statfs.data_compressed_original));
     }
   }
 }
index bc6d34012bcbf4aa71791824925df19fb0ffdf1e..f1473d31a78a3b6c5abfbdcbf145dcd7f1a21ee4 100644 (file)
@@ -1224,18 +1224,27 @@ int BlueFS::_replay(bool noop, bool to_stdout)
       bl.claim_append(t);
       read_pos += r;
     }
-    seen_recs = true;
     bluefs_transaction_t t;
     try {
       auto p = bl.cbegin();
       decode(t, p);
-    }
-    catch (buffer::error& e) {
-      derr << __func__ << " 0x" << std::hex << pos << std::dec
-           << ": stop: failed to decode: " << e.what()
-           << dendl;
-      delete log_reader;
-      return -EIO;
+      seen_recs = true;
+    }
+    catch (ceph::buffer::error& e) {
+      // Multi-block transactions might be incomplete due to unexpected
+      // power off. Hence let's treat that as a regular stop condition.
+      if (seen_recs && more) {
+        dout(10) << __func__ << " 0x" << std::hex << pos << std::dec
+                 << ": stop: failed to decode: " << e.what()
+                 << dendl;
+      } else {
+        derr << __func__ << " 0x" << std::hex << pos << std::dec
+             << ": stop: failed to decode: " << e.what()
+             << dendl;
+        delete log_reader;
+        return -EIO;
+      }
+      break;
     }
     ceph_assert(seq == t.seq);
     dout(10) << __func__ << " 0x" << std::hex << pos << std::dec
@@ -1589,6 +1598,9 @@ int BlueFS::_replay(bool noop, bool to_stdout)
                 return r;
               }
             }
+         } else if (noop && fnode.ino == 1) {
+           FileRef f = _get_file(fnode.ino);
+           f->fnode = fnode;
          }
         }
        break;
@@ -3522,6 +3534,10 @@ void BlueFS::_close_writer(FileWriter *h)
       }
     }
   }
+  // sanity
+  if (h->file->fnode.size >= (1ull << 30)) {
+    dout(10) << __func__ << " file is unexpectedly large:" << h->file->fnode << dendl;
+  }
   delete h;
 }
 
@@ -4032,7 +4048,9 @@ uint8_t OriginalVolumeSelector::select_prefer_bdev(void* hint)
 void OriginalVolumeSelector::get_paths(const std::string& base, paths& res) const
 {
   res.emplace_back(base, db_total);
-  res.emplace_back(base + ".slow", slow_total);
+  res.emplace_back(base + ".slow",
+    slow_total ? slow_total : db_total); // use fake non-zero value if needed to
+                                         // avoid RocksDB complains
 }
 
 #undef dout_prefix
index 2bd5ac8648ad40476562a52a068553e83e99bf12..1da41d99a08a04191b36b284e4b32b3d1883d3c9 100644 (file)
@@ -6062,9 +6062,7 @@ int BlueStore::_open_db(bool create, bool to_repair_db, bool read_only)
     BlueFSVolumeSelector::paths paths;
     bluefs->get_vselector_paths(fn, paths);
 
-    if (bluefs_layout.shared_bdev == BlueFS::BDEV_SLOW) {
-      // we have both block.db and block; tell rocksdb!
-      // note: the second (last) size value doesn't really matter
+    {
       ostringstream db_paths;
       bool first = true;
       for (auto& p : paths) {
index b56c41a4334da26fda0255837283ab159c312448..c42fd05bedf01f02f31302f8c5e2c42b5534b196 100644 (file)
@@ -1989,6 +1989,7 @@ int OSD::mkfs(CephContext *cct, ObjectStore *store, uuid_d fsid, int whoami, str
           << "queue_transaction returned " << cpp_strerror(ret) << dendl;
       goto umount_store;
     }
+    ch->flush();
   }
 
   ret = write_meta(cct, store, sb.cluster_fsid, sb.osd_fsid, whoami, osdspec_affinity);
index 989a30fca5678973663f133be9f4bd0a387ca326..76a4d9b765e3b730eeaa20d71c14dc07a34087ce 100644 (file)
@@ -90,6 +90,14 @@ Host *
 
 CEPH_TYPES = set(CEPH_UPGRADE_ORDER)
 
+# Default container images -----------------------------------------------------
+DEFAULT_IMAGE = 'quay.io/ceph/ceph'
+DEFAULT_PROMETHEUS_IMAGE = 'quay.io/prometheus/prometheus:v2.18.1'
+DEFAULT_NODE_EXPORTER_IMAGE = 'quay.io/prometheus/node-exporter:v0.18.1'
+DEFAULT_ALERT_MANAGER_IMAGE = 'quay.io/prometheus/alertmanager:v0.20.0'
+DEFAULT_GRAFANA_IMAGE = 'quay.io/ceph/ceph-grafana:6.7.4'
+# ------------------------------------------------------------------------------
+
 
 class CephadmCompletion(orchestrator.Completion[T]):
     def evaluate(self) -> None:
@@ -162,28 +170,28 @@ class CephadmOrchestrator(orchestrator.Orchestrator, MgrModule):
         },
         {
             'name': 'container_image_base',
-            'default': 'docker.io/ceph/ceph',
+            'default': DEFAULT_IMAGE,
             'desc': 'Container image name, without the tag',
             'runtime': True,
         },
         {
             'name': 'container_image_prometheus',
-            'default': 'docker.io/prom/prometheus:v2.18.1',
+            'default': DEFAULT_PROMETHEUS_IMAGE,
             'desc': 'Prometheus container image',
         },
         {
             'name': 'container_image_grafana',
-            'default': 'docker.io/ceph/ceph-grafana:6.7.4',
+            'default': DEFAULT_GRAFANA_IMAGE,
             'desc': 'Prometheus container image',
         },
         {
             'name': 'container_image_alertmanager',
-            'default': 'docker.io/prom/alertmanager:v0.20.0',
+            'default': DEFAULT_ALERT_MANAGER_IMAGE,
             'desc': 'Prometheus container image',
         },
         {
             'name': 'container_image_node_exporter',
-            'default': 'docker.io/prom/node-exporter:v0.18.1',
+            'default': DEFAULT_NODE_EXPORTER_IMAGE,
             'desc': 'Prometheus container image',
         },
         {
@@ -1123,6 +1131,9 @@ To check that the host is reachable:
             if not no_fsid:
                 final_args += ['--fsid', self._cluster_fsid]
 
+            if self.container_init and command == 'deploy':
+                final_args += ['--container-init']
+
             final_args += args
 
             # exec
index b50182c85ef3ca2fc3512558ab086bb4a5b79a69..c414e430245720837a5d229078b52d138a5e7369 100644 (file)
@@ -184,7 +184,7 @@ Note:
   permissions.
 
 run-cephadm-e2e-tests.sh
-........................
+.........................
 
 ``run-cephadm-e2e-tests.sh`` runs a subset of E2E tests to verify that the Dashboard and cephadm as
 Orchestrator backend behave correctly.
@@ -208,14 +208,24 @@ Start E2E tests by running::
   $ cd <your/ceph/repo/dir>
   $ sudo chown -R $(id -un) src/pybind/mgr/dashboard/frontend/{dist,node_modules,src/environments}
   $ ./src/pybind/mgr/dashboard/ci/cephadm/run-cephadm-e2e-tests.sh
-  $ kcli delete plan -y ceph  # After tests finish.
 
-You can also start a cluster in development mode and later run E2E tests by running::
+You can also start a cluster in development mode (so the frontend build starts in watch mode and you
+only have to reload the page for the changes to be reflected) by running::
 
   $ ./src/pybind/mgr/dashboard/ci/cephadm/start-cluster.sh --dev-mode
-  $ # Work on your feature, bug fix, ...
+
+Note:
+  Add ``--expanded`` if you need a cluster ready to deploy services (one with enough monitor
+  daemons spread across different hosts and enough OSDs).
+
+Test your changes by running:
+
   $ ./src/pybind/mgr/dashboard/ci/cephadm/run-cephadm-e2e-tests.sh
-  $ # Remember to kill the npm build watch process i.e.: pkill -f "ng build"
+
+Shutdown the cluster by running:
+
+  $ kcli delete plan -y ceph
+  $ # In development mode, also kill the npm build watch process (e.g., pkill -f "ng build")
 
 Other running options
 .....................
index af7ef81f43f3f5d182288e57e5d7d194aa79bee3..4bad63bc5973c64002e673ddba12a6fc56a06e08 100755 (executable)
@@ -10,6 +10,15 @@ mon_ip=$(ifconfig eth0  | grep 'inet ' | awk '{ print $2}')
 
 cephadm bootstrap --mon-ip $mon_ip --initial-dashboard-password {{ admin_password }} --allow-fqdn-hostname --dashboard-password-noupdate --shared_ceph_folder /mnt/{{ ceph_dev_folder }}
 
+fsid=$(cat /etc/ceph/ceph.conf | grep fsid | awk '{ print $3}')
+
 {% for number in range(1, nodes) %}
   ssh-copy-id -f -i /etc/ceph/ceph.pub  -o StrictHostKeyChecking=no root@{{ prefix }}-node-0{{ number }}.{{ domain }}
+  {% if expanded_cluster is defined %}
+    cephadm shell --fsid $fsid -c /etc/ceph/ceph.conf -k /etc/ceph/ceph.client.admin.keyring ceph orch host add {{ prefix }}-node-0{{ number }}.{{ domain }}
+  {% endif %}
 {% endfor %}
+
+{% if expanded_cluster is defined %}
+  cephadm shell --fsid $fsid -c /etc/ceph/ceph.conf -k /etc/ceph/ceph.client.admin.keyring ceph orch apply osd --all-available-devices
+{% endif %}
index 178c89f5ba60fa55a89441c287ff4195d8022188..e109297160892ce84a465bcba854e71775db967d 100755 (executable)
@@ -13,11 +13,9 @@ get_vm_ip () {
     echo -n $ip
 }
 
-if [[ -z "${CYPRESS_BASE_URL}" ]]; then
-    CEPH_NODE_00_IP="$(get_vm_ip ceph-node-00)"
-    if [[ -z "${CEPH_NODE_00_IP}" ]]; then
-        . "$(dirname $0)"/start-cluster.sh
-    fi
+if [[ -n "${JENKINS_HOME}" || (-z "${CYPRESS_BASE_URL}" && -z "$(get_vm_ip ceph-node-00)") ]]; then
+    . "$(dirname $0)"/start-cluster.sh
+
     CYPRESS_BASE_URL="https://$(get_vm_ip ceph-node-00):${DASHBOARD_PORT}"
 fi
 
@@ -27,11 +25,13 @@ cypress_run () {
     local specs="$1"
     local timeout="$2"
     local override_config="ignoreTestFiles=*.po.ts,retries=0,testFiles=${specs}"
-
     if [[ -n "$timeout" ]]; then
         override_config="${override_config},defaultCommandTimeout=${timeout}"
     fi
-    npx cypress run ${CYPRESS_ARGS} --browser chrome --headless --config "$override_config"
+
+    rm -f cypress/reports/results-*.xml || true
+
+    npx --no-install cypress run ${CYPRESS_ARGS} --browser chrome --headless --config "$override_config"
 }
 
 : ${CEPH_DEV_FOLDER:=${PWD}}
index 61775d0bac4e6e36ed2c4d88c8da2fb36d996367..68c49bb11422458250d66ee84bb76190f032cb76 100755 (executable)
@@ -17,6 +17,8 @@ on_error() {
     if [ "$1" != "0" ]; then
         printf "\n\nERROR $1 thrown on line $2\n\n"
         printf "\n\nCollecting info...\n\n"
+        printf "\n\nDisplaying MGR logs:\n\n"
+        kcli ssh -u root -- ceph-node-00 'cephadm logs -n $(cephadm ls | grep -Eo "mgr\.ceph[0-9a-z.-]+" | head -n 1)'
         for vm_id in 0 1 2
         do
             local vm="ceph-node-0${vm_id}"
@@ -42,7 +44,8 @@ DEV_MODE=''
 for arg in "$@"; do
   shift
   case "$arg" in
-    "--dev-mode") DEV_MODE='true'; EXTRA_PARAMS="-P dev_mode=${DEV_MODE}" ;;
+    "--dev-mode") DEV_MODE='true'; EXTRA_PARAMS+=" -P dev_mode=${DEV_MODE}" ;;
+    "--expanded") EXTRA_PARAMS+=" -P expanded_cluster=true" ;;
   esac
 done
 
@@ -50,7 +53,11 @@ kcli delete plan -y ceph || true
 
 # Build dashboard frontend (required to start the module).
 cd ${CEPH_DEV_FOLDER}/src/pybind/mgr/dashboard/frontend
-NG_CLI_ANALYTICS=false npm ci
+export NG_CLI_ANALYTICS=false
+if [[ -n "$JENKINS_HOME" ]]; then
+    npm cache clean --force
+fi
+npm ci
 FRONTEND_BUILD_OPTS='-- --prod'
 if [[ -n "${DEV_MODE}" ]]; then
     FRONTEND_BUILD_OPTS+=' --deleteOutputPath=false --watch'
index f3a885a21ac397ec09c92005ca29e0dec780b8a1..ae1c2df6bcefc1ceda59cdb264db911b51c28310 100644 (file)
@@ -208,7 +208,7 @@ class RgwBucket(RgwRESTController):
 
     def list(self, stats=False):
         # type: (bool) -> List[Any]
-        query_params = '?stats' if stats else ''
+        query_params = '?stats' if str_to_bool(stats) else ''
         result = self.proxy('GET', 'bucket{}'.format(query_params))
 
         if stats:
index 14c79d3fe74c7698d0d49e51a2075435a26198e6..62833f2d0f2c69fd8b237c554d823d68a9cfadcd 100644 (file)
@@ -254,6 +254,10 @@ class Module(MgrModule):
         for daemon, counters in six.iteritems(self.get_all_perf_counters()):
             svc_type, svc_id = daemon.split(".", 1)
             metadata = self.get_metadata(svc_type, svc_id)
+            if metadata is not None:
+                hostname = metadata['hostname']
+            else:
+                hostname = 'N/A'
 
             for path, counter_info in counters.items():
                 if counter_info['type'] & self.PERFCOUNTER_HISTOGRAM:
@@ -266,7 +270,7 @@ class Module(MgrModule):
                     "tags": {
                         "ceph_daemon": daemon,
                         "type_instance": path,
-                        "host": metadata['hostname'],
+                        "host": hostname,
                         "fsid": self.get_fsid()
                     },
                     "time": now,
index a640d85b7469195a15f1d43ed2e3fd490fcca307..af2a51309d4d87078a9f0ba8b62029e478aa86f0 100644 (file)
@@ -42,6 +42,26 @@ class Module(MgrModule):
         # health history tracking
         self._pending_health = []
         self._health_slot = None
+        self._store = {}
+
+    # The following three functions, get_store, set_store, and get_store_prefix
+    # mask the functions defined in the parent to avoid storing large keys
+    # persistently to disk as that was proving problematic. Long term we may
+    # implement a different mechanism to make these persistent. When that day
+    # comes it should just be a matter of deleting these three functions.
+    def get_store(self, key):
+        return self._store.get(key)
+
+    def set_store(self, key, value):
+        if value is None:
+            if key in self._store:
+                del self._store[key]
+        else:
+            self._store[key] = value
+
+    def get_store_prefix(self, prefix):
+        return { k: v for k, v in self._store.items() if k.startswith(prefix) }
+
 
     def notify(self, ttype, ident):
         """Queue updates for processing"""
index d1b89495d83daa6a9ff44764a0fb1e3b012f3d73..c150b11efa53c979185cb8bd19df7a8465704774 100644 (file)
@@ -441,7 +441,7 @@ class Module(MgrModule):
         for state in DF_POOL:
             path = 'pool_{}'.format(state)
             metrics[path] = Metric(
-                'gauge',
+                'counter' if state in ('rd', 'rd_bytes', 'wr', 'wr_bytes') else 'gauge',
                 path,
                 'DF pool {}'.format(state),
                 ('pool_id',)
index 1427be92d2c4be5b5233e706fea33b95515fb003..c2bd9039e8b48ba0fc665eaffab725dd8c8c1500 100644 (file)
@@ -44,7 +44,7 @@ if 'UNITTEST' in os.environ:
             else:
                 self._store[k] = value
 
-        def mock_store_preifx(self, kind, prefix):
+        def mock_store_prefix(self, kind, prefix):
             if not hasattr(self, '_store'):
                 self._store = {}
             full_prefix = f'mock_store/{kind}/{prefix}'
@@ -61,7 +61,7 @@ if 'UNITTEST' in os.environ:
             self.mock_store_set('store', k, v)
 
         def _ceph_get_store_prefix(self, prefix):
-            return self.mock_store_preifx('store', prefix)
+            return self.mock_store_prefix('store', prefix)
 
         def _ceph_get_module_option(self, module, key, localized_prefix= None):
             try:
@@ -117,7 +117,7 @@ if 'UNITTEST' in os.environ:
 
             def config_dump():
                 r = []
-                for prefix, value in self.mock_store_preifx('config', '').items():
+                for prefix, value in self.mock_store_prefix('config', '').items():
                     section, name = prefix.split('/', 1)
                     r.append({
                         'name': name,
index 0bdfcf52adeae4b94c51646656cc8ac904254769..b0206cd16be2a3389de65df543114bb05a56046f 100644 (file)
@@ -2868,7 +2868,7 @@ cdef class MirrorImageStatusIterator(object):
                         local_status = site_status
                     else:
                         site_status['mirror_uuid'] = mirror_uuid
-                        site_statuses += site_status
+                        site_statuses.append(site_status)
 
                 status = {
                     'name'        : decode_cstr(self.images[i].name),
@@ -2876,6 +2876,8 @@ cdef class MirrorImageStatusIterator(object):
                     'info'        : {
                         'global_id' : decode_cstr(self.images[i].info.global_id),
                         'state'     : self.images[i].info.state,
+                        # primary isn't added here because it is unknown (always
+                        # false, see XXX in Mirror::image_global_status_list())
                         },
                     'remote_statuses': site_statuses,
                     }
@@ -5206,8 +5208,8 @@ written." % (self.name, ret, length))
                 if mirror_uuid == '':
                     local_status = site_status
                 else:
-                    site_statuses['mirror_uuid'] = mirror_uuid
-                    site_statuses += site_status
+                    site_status['mirror_uuid'] = mirror_uuid
+                    site_statuses.append(site_status)
             status = {
                 'name': decode_cstr(c_status.name),
                 'id'  : self.id(),
index 6914cc1ab6a534e057b47d96eb6ce8cf663931bc..ec0dbf64361dc57b91223e14cede05c2a96adcfb 100644 (file)
@@ -2786,6 +2786,11 @@ void RGWSetBucketWebsite::execute()
   if (op_ret < 0)
     return;
 
+  if (!s->bucket_exists) {
+    op_ret = -ERR_NO_SUCH_BUCKET;
+    return;
+  }
+
   if (!store->svc()->zone->is_meta_master()) {
     op_ret = forward_request_to_master(s, NULL, store, in_data, nullptr);
     if (op_ret < 0) {
@@ -2821,6 +2826,10 @@ void RGWDeleteBucketWebsite::pre_exec()
 
 void RGWDeleteBucketWebsite::execute()
 {
+  if (!s->bucket_exists) {
+    op_ret = -ERR_NO_SUCH_BUCKET;
+    return;
+  }
 
   if (!store->svc()->zone->is_meta_master()) {
     bufferlist in_data;
index 5fb894ca0c544e8df61f7e9db82a62e961df722e..166d5908bcc8d2112da972f5445ca392fde0c993 100644 (file)
@@ -80,11 +80,9 @@ public:
     async_refcount->put_wait(); /* wait for all pending async requests to complete */
   }
 
-  int get_stats(const rgw_user& user, const rgw_bucket& bucket, RGWStorageStats& stats, RGWQuotaInfo& quota);
+  int get_stats(const rgw_user& user, const rgw_bucket& bucket, RGWStorageStats& stats);
   void adjust_stats(const rgw_user& user, rgw_bucket& bucket, int objs_delta, uint64_t added_bytes, uint64_t removed_bytes);
 
-  virtual bool can_use_cached_stats(RGWQuotaInfo& quota, RGWStorageStats& stats);
-
   void set_stats(const rgw_user& user, const rgw_bucket& bucket, RGWQuotaCacheStats& qs, RGWStorageStats& stats);
   int async_refresh(const rgw_user& user, const rgw_bucket& bucket, RGWQuotaCacheStats& qs);
   void async_refresh_response(const rgw_user& user, rgw_bucket& bucket, RGWStorageStats& stats);
@@ -105,36 +103,6 @@ public:
   virtual AsyncRefreshHandler *allocate_refresh_handler(const rgw_user& user, const rgw_bucket& bucket) = 0;
 };
 
-template<class T>
-bool RGWQuotaCache<T>::can_use_cached_stats(RGWQuotaInfo& quota, RGWStorageStats& cached_stats)
-{
-  if (quota.max_size >= 0) {
-    if (quota.max_size_soft_threshold < 0) {
-      quota.max_size_soft_threshold = quota.max_size * store->ctx()->_conf->rgw_bucket_quota_soft_threshold;
-    }
-
-    if (cached_stats.size_rounded  >= (uint64_t)quota.max_size_soft_threshold) {
-      ldout(store->ctx(), 20) << "quota: can't use cached stats, exceeded soft threshold (size): "
-        << cached_stats.size_rounded << " >= " << quota.max_size_soft_threshold << dendl;
-      return false;
-    }
-  }
-
-  if (quota.max_objects >= 0) {
-    if (quota.max_objs_soft_threshold < 0) {
-      quota.max_objs_soft_threshold = quota.max_objects * store->ctx()->_conf->rgw_bucket_quota_soft_threshold;
-    }
-
-    if (cached_stats.num_objects >= (uint64_t)quota.max_objs_soft_threshold) {
-      ldout(store->ctx(), 20) << "quota: can't use cached stats, exceeded soft threshold (num objs): "
-        << cached_stats.num_objects << " >= " << quota.max_objs_soft_threshold << dendl;
-      return false;
-    }
-  }
-
-  return true;
-}
-
 template<class T>
 int RGWQuotaCache<T>::async_refresh(const rgw_user& user, const rgw_bucket& bucket, RGWQuotaCacheStats& qs)
 {
@@ -195,7 +163,7 @@ void RGWQuotaCache<T>::set_stats(const rgw_user& user, const rgw_bucket& bucket,
 }
 
 template<class T>
-int RGWQuotaCache<T>::get_stats(const rgw_user& user, const rgw_bucket& bucket, RGWStorageStats& stats, RGWQuotaInfo& quota) {
+int RGWQuotaCache<T>::get_stats(const rgw_user& user, const rgw_bucket& bucket, RGWStorageStats& stats) {
   RGWQuotaCacheStats qs;
   utime_t now = ceph_clock_now();
   if (map_find(user, bucket, qs)) {
@@ -208,8 +176,7 @@ int RGWQuotaCache<T>::get_stats(const rgw_user& user, const rgw_bucket& bucket,
       }
     }
 
-    if (can_use_cached_stats(quota, qs.stats) && qs.expiration >
-       ceph_clock_now()) {
+    if (qs.expiration > ceph_clock_now()) {
       stats = qs.stats;
       return 0;
     }
@@ -593,13 +560,6 @@ public:
     return new UserAsyncRefreshHandler(store, this, user, bucket);
   }
 
-  bool can_use_cached_stats(RGWQuotaInfo& quota, RGWStorageStats& stats) override {
-    /* in the user case, the cached stats may contain a better estimation of the totals, as
-     * the backend is only periodically getting updated.
-     */
-    return true;
-  }
-
   bool going_down() {
     return down_flag;
   }
@@ -942,8 +902,7 @@ public:
 
     if (bucket_quota.enabled) {
       RGWStorageStats bucket_stats;
-      int ret = bucket_stats_cache.get_stats(user, bucket, bucket_stats,
-                                           bucket_quota);
+      int ret = bucket_stats_cache.get_stats(user, bucket, bucket_stats);
       if (ret < 0) {
         return ret;
       }
@@ -955,7 +914,7 @@ public:
 
     if (user_quota.enabled) {
       RGWStorageStats user_stats;
-      int ret = user_stats_cache.get_stats(user, bucket, user_stats, user_quota);
+      int ret = user_stats_cache.get_stats(user, bucket, user_stats);
       if (ret < 0) {
         return ret;
       }
index ece795207cd431d62996276e68375fa8fe7ec1a3..68e0b1ff538e40bc7e0f707127f3a33c92fa13b9 100644 (file)
@@ -34,14 +34,6 @@ namespace rgw { namespace sal {
 
 struct RGWQuotaInfo {
   template<class T> friend class RGWQuotaCache;
-protected:
-  /* The quota thresholds after which comparing against cached storage stats
-   * is disallowed. Those fields may be accessed only by the RGWQuotaCache.
-   * They are not intended as tunables but rather as a mean to store results
-   * of repeating calculations in the quota cache subsystem. */
-  int64_t max_size_soft_threshold;
-  int64_t max_objs_soft_threshold;
-
 public:
   int64_t max_size;
   int64_t max_objects;
@@ -51,9 +43,7 @@ public:
   bool check_on_raw;
 
   RGWQuotaInfo()
-    : max_size_soft_threshold(-1),
-      max_objs_soft_threshold(-1),
-      max_size(-1),
+    : max_size(-1),
       max_objects(-1),
       enabled(false),
       check_on_raw(false) {
index aef7ea42f1f88209d10b4dab55c54e3d2ce12fcb..45fde566af3ae6f3e771fbf54787dae5dc946f87 100644 (file)
@@ -7025,9 +7025,14 @@ static int decode_olh_info(CephContext* cct, const bufferlist& bl, RGWOLHInfo *o
   }
 }
 
-int RGWRados::apply_olh_log(RGWObjectCtx& obj_ctx, RGWObjState& state, const RGWBucketInfo& bucket_info, const rgw_obj& obj,
-                            bufferlist& olh_tag, map<uint64_t, vector<rgw_bucket_olh_log_entry> >& log,
-                            uint64_t *plast_ver, rgw_zone_set* zones_trace)
+int RGWRados::apply_olh_log(RGWObjectCtx& obj_ctx,
+                           RGWObjState& state,
+                           const RGWBucketInfo& bucket_info,
+                           const rgw_obj& obj,
+                           bufferlist& olh_tag,
+                           std::map<uint64_t, std::vector<rgw_bucket_olh_log_entry> >& log,
+                           uint64_t *plast_ver,
+                           rgw_zone_set* zones_trace)
 {
   if (log.empty()) {
     return 0;
@@ -8535,6 +8540,22 @@ int RGWRados::cls_bucket_list_ordered(RGWBucketInfo& bucket_info,
 }
 
 
+// A helper function to retrieve the hash source from an incomplete multipart entry
+// by removing everything from the second last dot to the end.
+static int parse_index_hash_source(const std::string& oid_wo_ns, std::string *index_hash_source) {
+  std::size_t found = oid_wo_ns.rfind('.');
+  if (found == std::string::npos || found < 1) {
+    return -EINVAL;
+  }
+  found = oid_wo_ns.rfind('.', found - 1);
+  if (found == std::string::npos || found < 1) {
+    return -EINVAL;
+  }
+  *index_hash_source = oid_wo_ns.substr(0, found);
+  return 0;
+}
+
+
 int RGWRados::cls_bucket_list_unordered(RGWBucketInfo& bucket_info,
                                        int shard_id,
                                        const rgw_obj_index_key& start_after,
@@ -8576,18 +8597,11 @@ int RGWRados::cls_bucket_list_unordered(RGWBucketInfo& bucket_info,
     // in it, so we need to get to the bucket shard index, so we can
     // start reading from there
 
-    std::string key;
-    // test whether object name is a multipart meta name
-    if(! multipart_meta_filter.filter(start_after.name, key)) {
-      // if multipart_meta_filter fails, must be "regular" (i.e.,
-      // unadorned) and the name is the key
-      key = start_after.name;
-    }
 
     // now convert the key (oid) to an rgw_obj_key since that will
     // separate out the namespace, name, and instance
     rgw_obj_key obj_key;
-    bool parsed = rgw_obj_key::parse_raw_oid(key, &obj_key);
+    bool parsed = rgw_obj_key::parse_raw_oid(start_after.name, &obj_key);
     if (!parsed) {
       ldout(cct, 0) <<
        "ERROR: RGWRados::cls_bucket_list_unordered received an invalid "
@@ -8601,7 +8615,21 @@ int RGWRados::cls_bucket_list_unordered(RGWBucketInfo& bucket_info,
     } else {
       // so now we have the key used to compute the bucket index shard
       // and can extract the specific shard from it
-      current_shard = svc.bi_rados->bucket_shard_index(obj_key.name, num_shards);
+      if (obj_key.ns == RGW_OBJ_NS_MULTIPART) {
+        // Use obj_key.ns == RGW_OBJ_NS_MULTIPART instead of
+        // the implementation relying on MultipartMetaFilter
+        // because MultipartMetaFilter only checks .meta suffix, which may
+        // exclude data multiparts but include some regular objects with .meta suffix
+        // by mistake.
+        string index_hash_source;
+        r = parse_index_hash_source(obj_key.name, &index_hash_source);
+        if (r < 0) {
+          return r;
+        }
+        current_shard = svc.bi_rados->bucket_shard_index(index_hash_source, num_shards);
+      } else {
+        current_shard = svc.bi_rados->bucket_shard_index(obj_key.name, num_shards);
+      }
     }
   }
 
index cb3b2c8566bddca2c7aa908363d52a9e1da52ecd..473e670d02ba4b382444ae3221537404806dccdf 100644 (file)
@@ -129,8 +129,15 @@ void RGWCreateRole::execute()
   if (op_ret < 0) {
     return;
   }
+  std::string user_tenant = s->user->get_tenant();
   RGWRole role(s->cct, store->getRados()->pctl, role_name, role_path, trust_policy,
-                s->user->get_tenant(), max_session_duration);
+               user_tenant, max_session_duration);
+  if (!user_tenant.empty() && role.get_tenant() != user_tenant) {
+    ldpp_dout(this, 20) << "ERROR: the tenant provided in the role name does not match with the tenant of the user creating the role"
+    << dendl;
+    op_ret = -EINVAL;
+    return;
+  }
   op_ret = role.create(true);
 
   if (op_ret == -EEXIST) {
index ac7d04098c0fc63650ad5675c863f6047f6d6a94..52071cf09a5938deb8f5c4e0faacf2fd024a4998 100644 (file)
@@ -5839,7 +5839,7 @@ rgw::auth::s3::STSEngine::get_session_token(const DoutPrefixProvider* dpp, const
     return -EINVAL;
   }
   string error;
-  auto* keyhandler = cryptohandler->get_key_handler(secret, error);
+  std::unique_ptr<CryptoKeyHandler> keyhandler(cryptohandler->get_key_handler(secret, error));
   if (! keyhandler) {
     return -EINVAL;
   }
index 9b28eca67802f27aada5e81bc1d487ef45c7ba79..050c0a10ae559e79d28547f0a9071fc327e99c14 100644 (file)
@@ -78,7 +78,7 @@ int Credentials::generateCredentials(CephContext* cct,
     return ret;
   }
   string error;
-  auto* keyhandler = cryptohandler->get_key_handler(secret, error);
+  std::unique_ptr<CryptoKeyHandler> keyhandler(cryptohandler->get_key_handler(secret, error));
   if (! keyhandler) {
     return -EINVAL;
   }
index 3ae4120bdc95d8beb2e9240a9fecd87929f224b5..7f9c97d5f7beb1617aa28946e4da215291645933 100644 (file)
@@ -487,7 +487,6 @@ add_dependencies(tests
   cls_lock
   ceph_test_objectstore
   ceph_erasure_code_non_regression
-  ceph_erasure_code
   cython_modules)
 if (WITH_CEPHFS)
   add_dependencies(tests ceph-mds)
index 37de45f0e3156d34a795a1ce1c6a2d5132ff715b..3a942854f5faa5f17efda023393a2381d9304869 100644 (file)
@@ -1654,6 +1654,23 @@ TEST(BufferList, rebuild_aligned_size_and_memory) {
   EXPECT_TRUE(bl.is_aligned(SIMD_ALIGN));
   EXPECT_TRUE(bl.is_n_align_sized(BUFFER_SIZE));
   EXPECT_EQ(3U, bl.get_num_buffers());
+
+  {
+    /* bug replicator, to test rebuild_aligned_size_and_memory() in the
+     * scenario where the first bptr is both size and memory aligned and
+     * the second is 0-length */
+    bl.clear();
+    bufferptr ptr1(buffer::create_aligned(4096, 4096));
+    bl.append(ptr1);
+    bufferptr ptr(10);
+    /* bl.back().length() must be 0 */
+    bl.append(ptr, 0, 0);
+    EXPECT_EQ(bl.get_num_buffers(), 2);
+    EXPECT_EQ(bl.back().length(), 0);
+    /* rebuild_aligned() calls rebuild_aligned_size_and_memory() */
+    bl.rebuild_aligned(4096);
+    EXPECT_EQ(bl.get_num_buffers(), 1);
+  }
 }
 
 TEST(BufferList, is_zero) {
diff --git a/ceph/src/test/ceph-erasure-code-tool/test_ceph-erasure-code-tool.sh b/ceph/src/test/ceph-erasure-code-tool/test_ceph-erasure-code-tool.sh
new file mode 100755 (executable)
index 0000000..442e7ad
--- /dev/null
@@ -0,0 +1,43 @@
+#!/bin/sh -ex
+
+TMPDIR=/tmp/test_ceph-erasure-code-tool.$$
+mkdir $TMPDIR
+trap "rm -fr $TMPDIR" 0
+
+ceph-erasure-code-tool test-plugin-exists INVALID_PLUGIN && exit 1
+ceph-erasure-code-tool test-plugin-exists jerasure
+
+ceph-erasure-code-tool validate-profile \
+                       plugin=jerasure,technique=reed_sol_van,k=2,m=1
+
+test "$(ceph-erasure-code-tool validate-profile \
+          plugin=jerasure,technique=reed_sol_van,k=2,m=1 chunk_count)" = 3
+
+test "$(ceph-erasure-code-tool calc-chunk-size \
+          plugin=jerasure,technique=reed_sol_van,k=2,m=1 4194304)" = 2097152
+
+dd if="$(which ceph-erasure-code-tool)" of=$TMPDIR/data bs=770808 count=1
+cp $TMPDIR/data $TMPDIR/data.orig
+
+ceph-erasure-code-tool encode \
+                       plugin=jerasure,technique=reed_sol_van,k=2,m=1 \
+                       4096 \
+                       0,1,2 \
+                       $TMPDIR/data
+test -f $TMPDIR/data.0
+test -f $TMPDIR/data.1
+test -f $TMPDIR/data.2
+
+rm $TMPDIR/data
+
+ceph-erasure-code-tool decode \
+                       plugin=jerasure,technique=reed_sol_van,k=2,m=1 \
+                       4096 \
+                       0,2 \
+                       $TMPDIR/data
+
+size=$(stat -c '%s' $TMPDIR/data.orig)
+truncate -s "${size}" $TMPDIR/data # remove stripe width padding
+cmp $TMPDIR/data.orig $TMPDIR/data
+
+echo OK
index aa381b1bc20396290f5e1dda8f18a70fb4a89e13..d9a0d97d007287905531ad4871716277e5c2496a 100644 (file)
@@ -460,6 +460,23 @@ pool/img@snap, custom pool:
   $ rbd device list
 
 
+Odd names
+=========
+
+  $ ceph osd pool create foo\* >/dev/null 2>&1
+  $ rbd pool init foo\*
+  $ rbd create --size 1 foo\*/[0.0.0.0]
+  $ rbd snap create foo\*/[0.0.0.0]@\?bar
+  $ sudo rbd device map foo\*/[0.0.0.0]@\?bar
+  /dev/rbd? (glob)
+  $ rbd device list
+  id  pool  namespace  image      snap  device   
+  ?   foo*             [0.0.0.0]  ?bar  /dev/rbd? (glob)
+  $ sudo rbd device unmap foo\*/[0.0.0.0]@\?bar
+  $ rbd device list
+  $ ceph osd pool delete foo\* foo\* --yes-i-really-really-mean-it >/dev/null 2>&1
+
+
 Teardown
 ========
 
index 49f12adc9184aabe46b3ca06642d157f7ad95a1b..640de24bd5fc3c6ad7465e5ccc83191e29a78132 100644 (file)
@@ -9,11 +9,6 @@ install(TARGETS ceph_erasure_code_benchmark
 add_executable(ceph_erasure_code_non_regression ceph_erasure_code_non_regression.cc)
 target_link_libraries(ceph_erasure_code_non_regression ceph-common Boost::program_options global ${CMAKE_DL_LIBS})
 
-add_executable(ceph_erasure_code ceph_erasure_code.cc)
-target_link_libraries(ceph_erasure_code ceph-common Boost::program_options global ${CMAKE_DL_LIBS})
-install(TARGETS ceph_erasure_code
-  DESTINATION bin)
-
 add_library(ec_example SHARED 
   ErasureCodePluginExample.cc
   $<TARGET_OBJECTS:erasure_code_objs>)
diff --git a/ceph/src/test/erasure-code/ceph_erasure_code.cc b/ceph/src/test/erasure-code/ceph_erasure_code.cc
deleted file mode 100644 (file)
index 1f2fb61..0000000
+++ /dev/null
@@ -1,201 +0,0 @@
-// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
-// vim: ts=8 sw=2 smarttab
-/*
- * Ceph distributed storage system
- *
- * Copyright (C) 2014 Cloudwatt <libre.licensing@cloudwatt.com>
- * Copyright (C) 2014 Red Hat <contact@redhat.com>
- *
- * Author: Loic Dachary <loic@dachary.org>
- *
- *  This library is free software; you can redistribute it and/or
- *  modify it under the terms of the GNU Lesser General Public
- *  License as published by the Free Software Foundation; either
- *  version 2.1 of the License, or (at your option) any later version.
- *
- */
-
-#include <boost/scoped_ptr.hpp>
-#include <boost/lexical_cast.hpp>
-#include <boost/program_options/option.hpp>
-#include <boost/program_options/options_description.hpp>
-#include <boost/program_options/variables_map.hpp>
-#include <boost/program_options/cmdline.hpp>
-#include <boost/program_options/parsers.hpp>
-#include <boost/algorithm/string.hpp>
-
-#include "global/global_context.h"
-#include "global/global_init.h"
-#include "common/ceph_argparse.h"
-#include "common/ceph_context.h"
-#include "common/config.h"
-#include "common/Clock.h"
-#include "include/utime.h"
-#include "erasure-code/ErasureCodePlugin.h"
-
-namespace po = boost::program_options;
-
-class ErasureCodeCommand {
-  po::variables_map vm;
-  ErasureCodeProfile profile;
-  boost::intrusive_ptr<CephContext> cct;
-public:
-  int setup(int argc, char** argv);
-  int run();
-  int plugin_exists();
-  int display_information();
-};
-
-int ErasureCodeCommand::setup(int argc, char** argv) {
-
-  po::options_description desc("Allowed options");
-  desc.add_options()
-    ("help,h", "produce help message")
-    ("all", "implies "
-     "--get_chunk_size 1024 "
-     "--get_data_chunk_count "
-     "--get_coding_chunk_count "
-     "--get_chunk_count ")
-    ("get_chunk_size", po::value<unsigned int>(),
-     "display get_chunk_size(<object size>)")
-    ("get_data_chunk_count", "display get_data_chunk_count()")
-    ("get_coding_chunk_count", "display get_coding_chunk_count()")
-    ("get_chunk_count", "display get_chunk_count()")
-    ("parameter,P", po::value<vector<string> >(),
-     "parameters")
-    ("plugin_exists", po::value<string>(),
-     "succeeds if the plugin given in argument exists and can be loaded")
-    ;
-
-  po::parsed_options parsed =
-    po::command_line_parser(argc, argv).options(desc).allow_unregistered().run();
-  po::store(
-    parsed,
-    vm);
-  po::notify(vm);
-
-  vector<const char *> ceph_options;
-  vector<string> ceph_option_strings = po::collect_unrecognized(
-    parsed.options, po::include_positional);
-  ceph_options.reserve(ceph_option_strings.size());
-  for (vector<string>::iterator i = ceph_option_strings.begin();
-       i != ceph_option_strings.end();
-       ++i) {
-    ceph_options.push_back(i->c_str());
-  }
-
-  cct = global_init(
-    NULL, ceph_options, CEPH_ENTITY_TYPE_CLIENT,
-    CODE_ENVIRONMENT_UTILITY,
-    CINIT_FLAG_NO_MON_CONFIG);
-  common_init_finish(g_ceph_context);
-  g_ceph_context->_conf.apply_changes(nullptr);
-
-  if (vm.count("help")) {
-    cout << desc << std::endl;
-    return 1;
-  }
-
-  if (vm.count("parameter")) {
-    const vector<string> &p = vm["parameter"].as< vector<string> >();
-    for (vector<string>::const_iterator i = p.begin();
-        i != p.end();
-        ++i) {
-      std::vector<std::string> strs;
-      boost::split(strs, *i, boost::is_any_of("="));
-      if (strs.size() != 2) {
-       cerr << "--parameter " << *i
-            << " ignored because it does not contain exactly one =" << endl;
-      } else {
-       profile[strs[0]] = strs[1];
-      }
-    }
-  }
-
-  return 0;
-}
-
-int ErasureCodeCommand::run() {
-  if (vm.count("plugin_exists"))
-    return plugin_exists();
-  else
-    return display_information();
-}
-
-int ErasureCodeCommand::plugin_exists() {
-  ErasureCodePluginRegistry &instance = ErasureCodePluginRegistry::instance();
-  ErasureCodePlugin *plugin = 0;
-  std::lock_guard l{instance.lock};
-  stringstream ss;
-  int code = instance.load(vm["plugin_exists"].as<string>(),
-                          g_conf().get_val<std::string>("erasure_code_dir"), &plugin, &ss);
-  if (code)
-    cerr << ss.str() << endl;
-  return code;
-}
-
-int ErasureCodeCommand::display_information() {
-  ErasureCodePluginRegistry &instance = ErasureCodePluginRegistry::instance();
-  ErasureCodeInterfaceRef erasure_code;
-
-  if (profile.count("plugin") == 0) {
-    cerr << "--parameter plugin=<plugin> is mandatory" << endl;
-    return 1;
-  }
-
-  int code = instance.factory(profile["plugin"],
-                             g_conf().get_val<std::string>("erasure_code_dir"),
-                             profile,
-                             &erasure_code, &cerr);
-  if (code)
-    return code;
-
-  if (vm.count("all") || vm.count("get_chunk_size")) {
-    unsigned int object_size = 1024;
-    if (vm.count("get_chunk_size"))
-      object_size = vm["get_chunk_size"].as<unsigned int>();
-    cout << "get_chunk_size(" << object_size << ")\t"
-        << erasure_code->get_chunk_size(object_size) << endl;
-  }
-  if (vm.count("all") || vm.count("get_data_chunk_count"))
-    cout << "get_data_chunk_count\t"
-        << erasure_code->get_data_chunk_count() << endl;
-  if (vm.count("all") || vm.count("get_coding_chunk_count"))
-    cout << "get_coding_chunk_count\t"
-        << erasure_code->get_coding_chunk_count() << endl;
-  if (vm.count("all") || vm.count("get_chunk_count"))
-    cout << "get_chunk_count\t"
-        << erasure_code->get_chunk_count() << endl;
-  return 0;
-}
-
-int main(int argc, char** argv) {
-  ErasureCodeCommand eccommand;
-  try {
-    int err = eccommand.setup(argc, argv);
-    if (err)
-      return err;
-    return eccommand.run();
-  } catch(po::error &e) {
-    cerr << e.what() << endl; 
-    return 1;
-  }
-}
-
-/*
- * Local Variables:
- * compile-command: "cd ../.. ; make -j4 &&
- *   make -j4 ceph_erasure_code &&
- *   libtool --mode=execute valgrind --tool=memcheck --leak-check=full \
- *      ./ceph_erasure_code \
- *      --parameter plugin=jerasure \
- *      --parameter technique=reed_sol_van \
- *      --parameter k=2 \
- *      --parameter m=2 \
- *      --get_chunk_size 1024 \
- *      --get_data_chunk_count \
- *      --get_coding_chunk_count \
- *      --get_chunk_count \
- * "
- * End:
- */
index 7280601fc3ac22e69e25cb207c0afebc7f469edd..7566651323fe12730a94625a5c64ee9d94d75b22 100644 (file)
@@ -79,8 +79,9 @@ TEST(pgmap, dump_object_stat_sum_0)
   pool.quota_max_bytes = 2000 * 1024 * 1024;
   pool.size = 2;
   pool.type = pg_pool_t::TYPE_REPLICATED;
+  pool.tier_of = 0;
   PGMap::dump_object_stat_sum(tbl, nullptr, pool_stat, avail,
-                             pool.get_size(), verbose, true, true, &pool);  
+                             pool.get_size(), verbose, true, true, &pool);
   float copies_rate =
     (static_cast<float>(sum.num_object_copies - sum.num_objects_degraded) /
       sum.num_object_copies) * pool.get_size();
@@ -120,8 +121,9 @@ TEST(pgmap, dump_object_stat_sum_1)
   pool.quota_max_bytes = 2000 * 1024 * 1024;
   pool.size = 2;
   pool.type = pg_pool_t::TYPE_REPLICATED;
+  pool.tier_of = 0;
   PGMap::dump_object_stat_sum(tbl, nullptr, pool_stat, avail,
-                             pool.get_size(), verbose, true, true, &pool);  
+                             pool.get_size(), verbose, true, true, &pool);
   unsigned col = 0;
   ASSERT_EQ(stringify(byte_u_t(0)), tbl.get(0, col++));
   ASSERT_EQ(stringify(byte_u_t(0)), tbl.get(0, col++));
index 1fa4bf05f1c839c6de9a181a26ae175716eab9f6..8990a6071909fdd21664921649976f548d835740 100644 (file)
@@ -2506,6 +2506,60 @@ TEST_F(TestMockImageReplayerSnapshotReplayer, SkipImageSync) {
                                         mock_remote_image_ctx));
 }
 
+TEST_F(TestMockImageReplayerSnapshotReplayer, ImageNameUpdated) {
+  librbd::MockTestImageCtx mock_local_image_ctx{*m_local_image_ctx};
+  librbd::MockTestImageCtx mock_remote_image_ctx{*m_remote_image_ctx};
+
+  MockThreads mock_threads(m_threads);
+  expect_work_queue_repeatedly(mock_threads);
+
+  MockReplayerListener mock_replayer_listener;
+  expect_notification(mock_threads, mock_replayer_listener);
+
+  InSequence seq;
+
+  MockInstanceWatcher mock_instance_watcher;
+  MockImageMeta mock_image_meta;
+  MockStateBuilder mock_state_builder(mock_local_image_ctx,
+                                      mock_remote_image_ctx,
+                                      mock_image_meta);
+  MockReplayer mock_replayer{&mock_threads, &mock_instance_watcher,
+                             "local mirror uuid", &m_pool_meta_cache,
+                             &mock_state_builder, &mock_replayer_listener};
+  m_pool_meta_cache.set_remote_pool_meta(
+    m_remote_io_ctx.get_id(),
+    {"remote mirror uuid", "remote mirror peer uuid"});
+
+  librbd::UpdateWatchCtx* update_watch_ctx = nullptr;
+  ASSERT_EQ(0, init_entry_replayer(mock_replayer, mock_threads,
+                                   mock_local_image_ctx,
+                                   mock_remote_image_ctx,
+                                   mock_replayer_listener,
+                                   mock_image_meta,
+                                   &update_watch_ctx));
+
+  // change the name of the image
+  mock_local_image_ctx.name = "NEW NAME";
+
+  // idle
+  expect_load_image_meta(mock_image_meta, true, 0);
+
+  // wake-up replayer
+  update_watch_ctx->handle_notify();
+
+  // wait for sync to complete and expect replay complete
+  ASSERT_EQ(0, wait_for_notification(2));
+  auto image_spec = image_replayer::util::compute_image_spec(m_local_io_ctx,
+                                                             "NEW NAME");
+  ASSERT_EQ(image_spec, mock_replayer.get_image_spec());
+  ASSERT_FALSE(mock_replayer.is_replaying());
+
+  // shut down
+  ASSERT_EQ(0, shut_down_entry_replayer(mock_replayer, mock_threads,
+                                        mock_local_image_ctx,
+                                        mock_remote_image_ctx));
+}
+
 } // namespace snapshot
 } // namespace image_replayer
 } // namespace mirror
index 9226b086469f66bc3c504468a48f77b8496e414f..60dc1a1d189fc1c28da69cf724b6f841546478e1 100644 (file)
@@ -9,7 +9,8 @@
 #include <iostream>
 #include <string>
 
-PerfCounters *g_perf_counters = nullptr;
+PerfCounters *g_journal_perf_counters = nullptr;
+PerfCounters *g_snapshot_perf_counters = nullptr;
 
 extern void register_test_cluster_watcher();
 extern void register_test_image_policy();
index 459d4430ea71b7bad5bde9e0a3b201b42d244a50..5aebf2ed0b4b07927e814e4acfa51c31729a702e 100644 (file)
@@ -135,3 +135,4 @@ endif()
 
 add_subdirectory(immutable_object_cache)
 add_subdirectory(ceph-dencoder)
+add_subdirectory(erasure-code)
index d7169582b3338c786a711ab3c15ad5aa03a2e1e2..9982fddfcd9eef342d52c690b0c3870702700c84 100644 (file)
@@ -640,6 +640,24 @@ static int update_mgrmap(MonitorDBStore& st)
 
 static int update_paxos(MonitorDBStore& st)
 {
+  const string prefix("paxos");
+  // a large enough version greater than the maximum possible `last_committed`
+  // that could be replied by the peons when the leader is collecting paxos
+  // transactions during recovery
+  constexpr version_t first_committed = 0x42;
+  constexpr version_t last_committed = first_committed;
+  for (version_t v = first_committed; v < last_committed + 1; v++) {
+    auto t = make_shared<MonitorDBStore::Transaction>();
+    if (v == first_committed) {
+      t->put(prefix, "first_committed", v);
+    }
+    bufferlist proposal;
+    MonitorDBStore::Transaction empty_txn;
+    empty_txn.encode(proposal);
+    t->put(prefix, v, proposal);
+    t->put(prefix, "last_committed", v);
+    st.apply_transaction(t);
+  }
   // build a pending paxos proposal from all non-permanent k/v pairs. once the
   // proposal is committed, it will gets applied. on the sync provider side, it
   // will be a no-op, but on its peers, the paxos commit will help to build up
@@ -658,11 +676,8 @@ static int update_paxos(MonitorDBStore& st)
     }
     t.encode(pending_proposal);
   }
-  const string prefix("paxos");
+  auto pending_v = last_committed + 1;
   auto t = make_shared<MonitorDBStore::Transaction>();
-  t->put(prefix, "first_committed", 0);
-  t->put(prefix, "last_committed", 0);
-  auto pending_v = 1;
   t->put(prefix, pending_v, pending_proposal);
   t->put(prefix, "pending_v", pending_v);
   t->put(prefix, "pending_pn", 400);
diff --git a/ceph/src/tools/erasure-code/CMakeLists.txt b/ceph/src/tools/erasure-code/CMakeLists.txt
new file mode 100644 (file)
index 0000000..3583733
--- /dev/null
@@ -0,0 +1,5 @@
+add_executable(ceph-erasure-code-tool
+  ${PROJECT_SOURCE_DIR}/src/osd/ECUtil.cc
+  ceph-erasure-code-tool.cc)
+target_link_libraries(ceph-erasure-code-tool global ceph-common)
+install(TARGETS ceph-erasure-code-tool DESTINATION bin)
diff --git a/ceph/src/tools/erasure-code/ceph-erasure-code-tool.cc b/ceph/src/tools/erasure-code/ceph-erasure-code-tool.cc
new file mode 100644 (file)
index 0000000..6c99abf
--- /dev/null
@@ -0,0 +1,322 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include "include/buffer.h"
+#include "include/stringify.h"
+#include "common/ceph_argparse.h"
+#include "common/config_proxy.h"
+#include "common/errno.h"
+#include "erasure-code/ErasureCode.h"
+#include "erasure-code/ErasureCodePlugin.h"
+#include "global/global_context.h"
+#include "global/global_init.h"
+#include "osd/ECUtil.h"
+
+#include <iostream>
+#include <map>
+#include <string>
+#include <vector>
+
+#include <boost/algorithm/string.hpp>
+
+std::vector<std::string> display_params = {
+  "chunk_count", "data_chunk_count", "coding_chunk_count"
+};
+
+void usage(const std::string message, ostream &out) {
+  if (!message.empty()) {
+    out << message << std::endl;
+    out << "" << std::endl;
+  }
+  out << "usage: ceph-erasure-code-tool test-plugin-exists <plugin>" << std::endl;
+  out << "       ceph-erasure-code-tool validate-profile <profile> [<display-param> ...]" << std::endl;
+  out << "       ceph-erasure-code-tool calc-chunk-size <profile> <object_size>" << std::endl;
+  out << "       ceph-erasure-code-tool encode <profile> <stripe_unit> <want_to_encode> <fname>" << std::endl;
+  out << "       ceph-erasure-code-tool decode <profile> <stripe_unit> <want_to_decode> <fname>" << std::endl;
+  out << "" << std::endl;
+  out << "  plugin          - plugin name" << std::endl;
+  out << "  profile         - comma separated list of erasure-code profile settings" << std::endl;
+  out << "                    example: plugin=jerasure,technique=reed_sol_van,k=3,m=2" << std::endl;
+  out << "  display-param   - parameter to display (display all if empty)" << std::endl;
+  out << "                    may be: " << display_params << std::endl;
+  out << "  object_size     - object size" << std::endl;
+  out << "  stripe_unit     - stripe unit" << std::endl;
+  out << "  want_to_encode  - comma separated list of shards to encode" << std::endl;
+  out << "  want_to_decode  - comma separated list of shards to decode" << std::endl;
+  out << "  fname           - name for input/output files" << std::endl;
+  out << "                    when encoding input is read form {fname} file," << std::endl;
+  out << "                                  result is stored in {fname}.{shard} files" << std::endl;
+  out << "                    when decoding input is read form {fname}.{shard} files," << std::endl;
+  out << "                                  result is stored in {fname} file" << std::endl;
+}
+
+int ec_init(const std::string &profile_str,
+            const std::string &stripe_unit_str,
+            ceph::ErasureCodeInterfaceRef *ec_impl,
+            std::unique_ptr<ECUtil::stripe_info_t> *sinfo) {
+  ceph::ErasureCodeProfile profile;
+  std::vector<std::string> opts;
+  boost::split(opts, profile_str, boost::is_any_of(", "));
+  for (auto &opt_str : opts) {
+    std::vector<std::string> opt;
+    boost::split(opt, opt_str, boost::is_any_of("="));
+    if (opt.size() <= 1) {
+      usage("invalid profile", std::cerr);
+      return 1;
+    }
+    profile[opt[0]] = opt[1];
+  }
+  auto plugin = profile.find("plugin");
+  if (plugin == profile.end()) {
+      usage("invalid profile: plugin not specified", std::cerr);
+      return 1;
+  }
+
+  stringstream ss;
+  ceph::ErasureCodePluginRegistry::instance().factory(
+    plugin->second, g_conf().get_val<std::string>("erasure_code_dir"),
+    profile, ec_impl, &ss);
+  if (!*ec_impl) {
+    usage("invalid profile: " + ss.str(), std::cerr);
+    return 1;
+  }
+
+  if (sinfo == nullptr) {
+    return 0;
+  }
+
+  uint64_t stripe_unit = atoi(stripe_unit_str.c_str());
+  if (stripe_unit <= 0) {
+    usage("invalid stripe unit", std::cerr);
+    return 1;
+  }
+
+  uint64_t stripe_size = atoi(profile["k"].c_str());
+  ceph_assert(stripe_size > 0);
+  uint64_t stripe_width = stripe_size * stripe_unit;
+  sinfo->reset(new ECUtil::stripe_info_t(stripe_size, stripe_width));
+
+  return 0;
+}
+
+int do_test_plugin_exists(const std::vector<const char*> &args) {
+  if (args.size() < 1) {
+    usage("not enought arguments", std::cerr);
+    return 1;
+  }
+
+  ErasureCodePluginRegistry &instance = ErasureCodePluginRegistry::instance();
+  ErasureCodePlugin *plugin;
+  stringstream ss;
+
+  std::lock_guard l{instance.lock};
+  int r = instance.load(
+    args[0], g_conf().get_val<std::string>("erasure_code_dir"), &plugin, &ss);
+  std::cerr << ss.str() << endl;
+  return r;
+}
+
+int do_validate_profile(const std::vector<const char*> &args) {
+  if (args.size() < 1) {
+    usage("not enought arguments", std::cerr);
+    return 1;
+  }
+
+  ceph::ErasureCodeInterfaceRef ec_impl;
+  int r = ec_init(args[0], {}, &ec_impl, nullptr);
+  if (r < 0) {
+    return r;
+  }
+
+  if (args.size() > 1) {
+    std::set<std::string> valid_params(display_params.begin(),
+                                       display_params.end());
+    display_params.clear();
+    for (size_t i = 1; i < args.size(); i++) {
+      if (!valid_params.count(args[i])) {
+        usage("invalid display param: " + std::string(args[i]), std::cerr);
+        return 1;
+      }
+      display_params.push_back(args[i]);
+    }
+  }
+
+  for (auto &param : display_params) {
+    if (display_params.size() > 1) {
+      std::cout << param << ": ";
+    }
+    if (param == "chunk_count") {
+      std::cout << ec_impl->get_chunk_count() << std::endl;
+    } else if (param == "data_chunk_count") {
+      std::cout << ec_impl->get_data_chunk_count() << std::endl;
+    } else if (param == "coding_chunk_count") {
+      std::cout << ec_impl->get_coding_chunk_count() << std::endl;
+    } else {
+      ceph_abort_msgf("unknown display_param: %s", param.c_str());
+    }
+  }
+
+  return 0;
+}
+
+int do_calc_chunk_size(const std::vector<const char*> &args) {
+  if (args.size() < 2) {
+    usage("not enought arguments", std::cerr);
+    return 1;
+  }
+
+  ceph::ErasureCodeInterfaceRef ec_impl;
+  int r = ec_init(args[0], {}, &ec_impl, nullptr);
+  if (r < 0) {
+    return r;
+  }
+
+  uint64_t object_size = atoi(args[1]);
+  if (object_size <= 0) {
+    usage("invalid object size", std::cerr);
+    return 1;
+  }
+
+  std::cout << ec_impl->get_chunk_size(object_size) << std::endl;
+  return 0;
+}
+
+int do_encode(const std::vector<const char*> &args) {
+  if (args.size() < 4) {
+    usage("not enought arguments", std::cerr);
+    return 1;
+  }
+
+  ceph::ErasureCodeInterfaceRef ec_impl;
+  std::unique_ptr<ECUtil::stripe_info_t> sinfo;
+  int r = ec_init(args[0], args[1], &ec_impl, &sinfo);
+  if (r < 0) {
+    return r;
+  }
+
+  std::set<int> want;
+  std::vector<std::string> shards;
+  boost::split(shards, args[2], boost::is_any_of(","));
+  for (auto &shard : shards) {
+    want.insert(atoi(shard.c_str()));
+  }
+  ceph::bufferlist decoded_data;
+  std::string fname = args[3];
+
+  std::string error;
+  r = decoded_data.read_file(fname.c_str(), &error);
+  if (r < 0) {
+    std::cerr << "failed to read " << fname << ": " << error << std::endl;
+    return 1;
+  }
+
+  uint64_t stripe_width = sinfo->get_stripe_width();
+  if (decoded_data.length() % stripe_width != 0) {
+    uint64_t pad = stripe_width - decoded_data.length() % stripe_width;
+    decoded_data.append_zero(pad);
+  }
+
+  std::map<int, ceph::bufferlist> encoded_data;
+  r = ECUtil::encode(*sinfo, ec_impl, decoded_data, want, &encoded_data);
+  if (r < 0) {
+    std::cerr << "failed to encode: " << cpp_strerror(r) << std::endl;
+    return 1;
+  }
+
+  for (auto &[shard, bl] : encoded_data) {
+    std::string name = fname + "." + stringify(shard);
+    r = bl.write_file(name.c_str());
+    if (r < 0) {
+      std::cerr << "failed to write " << name << ": " << cpp_strerror(r)
+                << std::endl;
+      return 1;
+    }
+  }
+
+  return 0;
+}
+
+int do_decode(const std::vector<const char*> &args) {
+  if (args.size() < 4) {
+    usage("not enought arguments", std::cerr);
+    return 1;
+  }
+
+  ceph::ErasureCodeInterfaceRef ec_impl;
+  std::unique_ptr<ECUtil::stripe_info_t> sinfo;
+  int r = ec_init(args[0], args[1], &ec_impl, &sinfo);
+  if (r < 0) {
+    return r;
+  }
+
+  std::map<int, ceph::bufferlist> encoded_data;
+  std::vector<std::string> shards;
+  boost::split(shards, args[2], boost::is_any_of(","));
+  for (auto &shard : shards) {
+    encoded_data[atoi(shard.c_str())] = {};
+  }
+  ceph::bufferlist decoded_data;
+  std::string fname = args[3];
+
+  for (auto &[shard, bl] : encoded_data) {
+    std::string name = fname + "." + stringify(shard);
+    std::string error;
+    r = bl.read_file(name.c_str(), &error);
+    if (r < 0) {
+      std::cerr << "failed to read " << name << ": " << error << std::endl;
+      return 1;
+    }
+  }
+
+  r = ECUtil::decode(*sinfo, ec_impl, encoded_data, &decoded_data);
+  if (r < 0) {
+    std::cerr << "failed to decode: " << cpp_strerror(r) << std::endl;
+    return 1;
+  }
+
+  r = decoded_data.write_file(fname.c_str());
+  if (r < 0) {
+    std::cerr << "failed to write " << fname << ": " << cpp_strerror(r)
+              << std::endl;
+    return 1;
+  }
+
+  return 0;
+}
+
+int main(int argc, const char **argv) {
+  std::vector<const char*> args;
+  argv_to_vec(argc, argv, args);
+  auto cct = global_init(nullptr, args, CEPH_ENTITY_TYPE_CLIENT,
+                         CODE_ENVIRONMENT_UTILITY,
+                         CINIT_FLAG_NO_MON_CONFIG);
+
+  if (args.empty() || args[0] == std::string("-h") ||
+      args[0] == std::string("--help")) {
+    usage("", std::cout);
+    return 0;
+  }
+
+  if (args.size() < 1) {
+    usage("not enought arguments", std::cerr);
+    return 1;
+  }
+
+  std::string cmd = args[0];
+  std::vector<const char*> cmd_args(args.begin() + 1, args.end());
+
+  if (cmd == "test-plugin-exists") {
+    return do_test_plugin_exists(cmd_args);
+  } else if (cmd == "validate-profile") {
+    return do_validate_profile(cmd_args);
+  } else if (cmd == "calc-chunk-size") {
+    return do_calc_chunk_size(cmd_args);
+  } else if (cmd == "encode") {
+    return do_encode(cmd_args);
+  } else if (cmd == "decode") {
+    return do_decode(cmd_args);
+  }
+
+  usage("invalid command: " + cmd, std::cerr);
+  return 1;
+}
index 2d51efe8bb4f13729f48a1bb44a6b3bb40c810b0..74f4d4bf1be2c5cff8b56cd6b25d0152a8e3885a 100644 (file)
@@ -36,8 +36,6 @@
 #define dout_prefix *_dout << "rbd::mirror::" << *this << " " \
                            << __func__ << ": "
 
-extern PerfCounters *g_perf_counters;
-
 namespace rbd {
 namespace mirror {
 
index 4ef838fa43ebbfcd1671d07231b01b153c7d035a..3a25ea0f622f5a6780fa7b9bbd13c3f68201b0f3 100644 (file)
@@ -371,6 +371,7 @@ void InstanceReplayer<I>::start_image_replayers(int r) {
 
   std::lock_guard locker{m_lock};
   if (m_on_shut_down != nullptr) {
+    m_async_op_tracker.finish_op();
     return;
   }
 
index 52c64c2d2c1bbde18ae4648a897aba8a7677b3d6..ae8dfa5c90b2d6914ee6fcc75589dd44a7f0e5cf 100644 (file)
@@ -16,7 +16,7 @@
 #define dout_context g_ceph_context
 #define dout_subsys ceph_subsys_rbd_mirror
 #undef dout_prefix
-#define dout_prefix *_dout << "rbd::mirror::RemotePollPoller: " << this << " " \
+#define dout_prefix *_dout << "rbd::mirror::RemotePoolPoller: " << this << " " \
                            << __func__ << ": "
 
 namespace rbd {
@@ -183,7 +183,7 @@ void RemotePoolPoller<I>::handle_mirror_peer_list(int r) {
 
   cls::rbd::MirrorPeer* matched_peer = nullptr;
   for (auto& peer : peers) {
-    if (peer.mirror_peer_direction == cls::rbd::MIRROR_PEER_DIRECTION_TX) {
+    if (peer.mirror_peer_direction == cls::rbd::MIRROR_PEER_DIRECTION_RX) {
       continue;
     }
 
index aa9bc7c3fe55f556d0e27563f79f49b7799514ca..cd71c73b1cc69e76db6755238a65e85a1f69b6e3 100644 (file)
@@ -19,7 +19,7 @@ std::ostream& operator<<(std::ostream& lhs,
 std::ostream& operator<<(std::ostream& lhs,
                          const RemotePoolMeta& rhs) {
   return lhs << "mirror_uuid=" << rhs.mirror_uuid << ", "
-                "mirror_pool_uuid=" << rhs.mirror_peer_uuid;
+                "mirror_peer_uuid=" << rhs.mirror_peer_uuid;
 }
 
 std::ostream& operator<<(std::ostream& lhs, const PeerSpec &peer) {
index c299aabe3145b4583d31350d143a7191d5e502ee..7b2a3b5cea5fa2a834e954409fb6365fafdf51ec 100644 (file)
@@ -20,11 +20,16 @@ template <typename> struct MirrorStatusUpdater;
 
 // Performance counters
 enum {
-  l_rbd_mirror_first = 27000,
+  l_rbd_mirror_journal_first = 27000,
   l_rbd_mirror_replay,
   l_rbd_mirror_replay_bytes,
   l_rbd_mirror_replay_latency,
-  l_rbd_mirror_last,
+  l_rbd_mirror_journal_last,
+  l_rbd_mirror_snapshot_first,
+  l_rbd_mirror_snapshot_replay_snapshots,
+  l_rbd_mirror_snapshot_replay_snapshots_time,
+  l_rbd_mirror_snapshot_replay_bytes,
+  l_rbd_mirror_snapshot_last,
 };
 
 typedef std::shared_ptr<librados::Rados> RadosRef;
index 767df43762fb8e39351ed6abbd7d8b6269d31d84..8041bf5ebbd84ba65b7a5942bf24a8212292b44b 100644 (file)
@@ -27,7 +27,7 @@
 #define dout_prefix *_dout << "rbd::mirror::image_replayer::journal::" \
                            << "Replayer: " << this << " " << __func__ << ": "
 
-extern PerfCounters *g_perf_counters;
+extern PerfCounters *g_journal_perf_counters;
 
 namespace rbd {
 namespace mirror {
@@ -1158,10 +1158,10 @@ void Replayer<I>::handle_process_entry_safe(
   }
 
   auto latency = ceph_clock_now() - replay_start_time;
-  if (g_perf_counters) {
-    g_perf_counters->inc(l_rbd_mirror_replay);
-    g_perf_counters->inc(l_rbd_mirror_replay_bytes, replay_bytes);
-    g_perf_counters->tinc(l_rbd_mirror_replay_latency, latency);
+  if (g_journal_perf_counters) {
+    g_journal_perf_counters->inc(l_rbd_mirror_replay);
+    g_journal_perf_counters->inc(l_rbd_mirror_replay_bytes, replay_bytes);
+    g_journal_perf_counters->tinc(l_rbd_mirror_replay_latency, latency);
   }
 
   auto ctx = new LambdaContext(
@@ -1271,7 +1271,7 @@ void Replayer<I>::register_perf_counters() {
   auto cct = static_cast<CephContext *>(m_state_builder->local_image_ctx->cct);
   auto prio = cct->_conf.get_val<int64_t>("rbd_mirror_image_perf_stats_prio");
   PerfCountersBuilder plb(g_ceph_context, "rbd_mirror_image_" + m_image_spec,
-                          l_rbd_mirror_first, l_rbd_mirror_last);
+                          l_rbd_mirror_journal_first, l_rbd_mirror_journal_last);
   plb.add_u64_counter(l_rbd_mirror_replay, "replay", "Replays", "r", prio);
   plb.add_u64_counter(l_rbd_mirror_replay_bytes, "replay_bytes",
                       "Replayed data", "rb", prio, unit_t(UNIT_BYTES));
index 5fd1b36b1827064e97a8ad1ab435c3049b341bb4..1c7a90167a2d09b328cef8ea34367631e09adc18 100644 (file)
@@ -39,7 +39,7 @@
 #define dout_prefix *_dout << "rbd::mirror::image_replayer::snapshot::" \
                            << "Replayer: " << this << " " << __func__ << ": "
 
-extern PerfCounters *g_perf_counters;
+extern PerfCounters *g_snapshot_perf_counters;
 
 namespace rbd {
 namespace mirror {
@@ -127,6 +127,12 @@ Replayer<I>::Replayer(
 template <typename I>
 Replayer<I>::~Replayer() {
   dout(10) << dendl;
+
+  {
+    std::unique_lock locker{m_lock};
+    unregister_perf_counters();
+  }
+
   ceph_assert(m_state == STATE_COMPLETE);
   ceph_assert(m_update_watch_ctx == nullptr);
   ceph_assert(m_deep_copy_handler == nullptr);
@@ -151,6 +157,18 @@ void Replayer<I>::init(Context* on_finish) {
   m_remote_mirror_peer_uuid = remote_pool_meta.mirror_peer_uuid;
   dout(10) << "remote_mirror_peer_uuid=" << m_remote_mirror_peer_uuid << dendl;
 
+  {
+    auto local_image_ctx = m_state_builder->local_image_ctx;
+    std::shared_lock image_locker{local_image_ctx->image_lock};
+    m_image_spec = image_replayer::util::compute_image_spec(
+      local_image_ctx->md_ctx, local_image_ctx->name);
+  }
+
+  {
+    std::unique_lock locker{m_lock};
+    register_perf_counters();
+  }
+
   ceph_assert(m_on_init_shutdown == nullptr);
   m_on_init_shutdown = on_finish;
 
@@ -304,6 +322,24 @@ void Replayer<I>::load_local_image_meta() {
     m_image_updated = false;
   }
 
+  bool update_status = false;
+  {
+    auto local_image_ctx = m_state_builder->local_image_ctx;
+    std::shared_lock image_locker{local_image_ctx->image_lock};
+    auto image_spec = image_replayer::util::compute_image_spec(
+      local_image_ctx->md_ctx, local_image_ctx->name);
+    if (m_image_spec != image_spec) {
+      m_image_spec = image_spec;
+      update_status = true;
+    }
+  }
+  if (update_status) {
+    std::unique_lock locker{m_lock};
+    unregister_perf_counters();
+    register_perf_counters();
+    notify_status_updated();
+  }
+
   ceph_assert(m_state_builder->local_image_meta != nullptr);
   auto ctx = create_context_callback<
     Replayer<I>, &Replayer<I>::handle_load_local_image_meta>(this);
@@ -1047,6 +1083,7 @@ void Replayer<I>::copy_image() {
            << "snap_seqs=" << m_local_mirror_snap_ns.snap_seqs << dendl;
 
   m_snapshot_bytes = 0;
+  m_snapshot_replay_start = ceph_clock_now();
   m_deep_copy_handler = new DeepCopyHandler(this);
   auto ctx = create_context_callback<
     Replayer<I>, &Replayer<I>::handle_copy_image>(this);
@@ -1078,6 +1115,19 @@ void Replayer<I>::handle_copy_image(int r) {
   {
     std::unique_lock locker{m_lock};
     m_bytes_per_snapshot(m_snapshot_bytes);
+    auto time = ceph_clock_now() - m_snapshot_replay_start;
+    if (g_snapshot_perf_counters) {
+      g_snapshot_perf_counters->inc(l_rbd_mirror_snapshot_replay_bytes,
+                                    m_snapshot_bytes);
+      g_snapshot_perf_counters->inc(l_rbd_mirror_snapshot_replay_snapshots);
+      g_snapshot_perf_counters->tinc(
+        l_rbd_mirror_snapshot_replay_snapshots_time, time);
+    }
+    if (m_perf_counters) {
+      m_perf_counters->inc(l_rbd_mirror_snapshot_replay_bytes, m_snapshot_bytes);
+      m_perf_counters->inc(l_rbd_mirror_snapshot_replay_snapshots);
+      m_perf_counters->tinc(l_rbd_mirror_snapshot_replay_snapshots_time, time);
+    }
     m_snapshot_bytes = 0;
   }
 
@@ -1501,6 +1551,43 @@ bool Replayer<I>::is_replay_interrupted(std::unique_lock<ceph::mutex>* locker) {
   return false;
 }
 
+template <typename I>
+void Replayer<I>::register_perf_counters() {
+  dout(5) << dendl;
+
+  ceph_assert(ceph_mutex_is_locked_by_me(m_lock));
+  ceph_assert(m_perf_counters == nullptr);
+
+  auto cct = static_cast<CephContext *>(m_state_builder->local_image_ctx->cct);
+  auto prio = cct->_conf.get_val<int64_t>("rbd_mirror_image_perf_stats_prio");
+  PerfCountersBuilder plb(g_ceph_context,
+                          "rbd_mirror_snapshot_image_" + m_image_spec,
+                          l_rbd_mirror_snapshot_first,
+                          l_rbd_mirror_snapshot_last);
+  plb.add_u64_counter(l_rbd_mirror_snapshot_replay_snapshots,
+                      "snapshots", "Snapshots", "r", prio);
+  plb.add_time_avg(l_rbd_mirror_snapshot_replay_snapshots_time,
+                   "snapshots_time", "Snapshots time", "rl", prio);
+  plb.add_u64_counter(l_rbd_mirror_snapshot_replay_bytes, "replay_bytes",
+                      "Replayed data", "rb", prio, unit_t(UNIT_BYTES));
+  m_perf_counters = plb.create_perf_counters();
+  g_ceph_context->get_perfcounters_collection()->add(m_perf_counters);
+}
+
+template <typename I>
+void Replayer<I>::unregister_perf_counters() {
+  dout(5) << dendl;
+  ceph_assert(ceph_mutex_is_locked_by_me(m_lock));
+
+  PerfCounters *perf_counters = nullptr;
+  std::swap(perf_counters, m_perf_counters);
+
+  if (perf_counters != nullptr) {
+    g_ceph_context->get_perfcounters_collection()->remove(perf_counters);
+    delete perf_counters;
+  }
+}
+
 } // namespace snapshot
 } // namespace image_replayer
 } // namespace mirror
index c2c28171f87dcb63d79480e8f5f5553d004bb891..e3c4c20890287a471d0783fcf0c4cf45e0fb5d5e 100644 (file)
@@ -94,6 +94,11 @@ public:
     return m_error_description;
   }
 
+  std::string get_image_spec() const {
+    std::unique_lock locker(m_lock);
+    return m_image_spec;
+  }
+
 private:
   /**
    * @verbatim
@@ -205,6 +210,7 @@ private:
 
   State m_state = STATE_INIT;
 
+  std::string m_image_spec;
   Context* m_on_init_shutdown = nullptr;
 
   bool m_resync_requested = false;
@@ -238,6 +244,7 @@ private:
     uint64_t, boost::accumulators::stats<
       boost::accumulators::tag::rolling_mean>> m_bytes_per_snapshot{
     boost::accumulators::tag::rolling_window::window_size = 2};
+  utime_t m_snapshot_replay_start;
 
   uint32_t m_pending_snapshots = 0;
 
@@ -245,6 +252,8 @@ private:
   bool m_updating_sync_point = false;
   bool m_sync_in_progress = false;
 
+  PerfCounters *m_perf_counters = nullptr;
+
   void load_local_image_meta();
   void handle_load_local_image_meta(int r);
 
@@ -323,6 +332,8 @@ private:
   bool is_replay_interrupted();
   bool is_replay_interrupted(std::unique_lock<ceph::mutex>* lock);
 
+  void register_perf_counters();
+  void unregister_perf_counters();
 };
 
 } // namespace snapshot
index ab350a014cf8030e254f49543a3e76c35598aa99..74c97272e15908e579b29018147d6839a7d4ac81 100644 (file)
@@ -14,7 +14,8 @@
 #include <vector>
 
 rbd::mirror::Mirror *mirror = nullptr;
-PerfCounters *g_perf_counters = nullptr;
+PerfCounters *g_journal_perf_counters = nullptr;
+PerfCounters *g_snapshot_perf_counters = nullptr;
 
 void usage() {
   std::cout << "usage: rbd-mirror [options...]" << std::endl;
@@ -68,17 +69,33 @@ int main(int argc, const char **argv)
 
   auto prio =
       g_ceph_context->_conf.get_val<int64_t>("rbd_mirror_perf_stats_prio");
-  PerfCountersBuilder plb(g_ceph_context, "rbd_mirror",
-                          rbd::mirror::l_rbd_mirror_first,
-                          rbd::mirror::l_rbd_mirror_last);
-  plb.add_u64_counter(rbd::mirror::l_rbd_mirror_replay, "replay", "Replays",
-                      "r", prio);
-  plb.add_u64_counter(rbd::mirror::l_rbd_mirror_replay_bytes, "replay_bytes",
-                      "Replayed data", "rb", prio, unit_t(UNIT_BYTES));
-  plb.add_time_avg(rbd::mirror::l_rbd_mirror_replay_latency, "replay_latency",
-                   "Replay latency", "rl", prio);
-  g_perf_counters = plb.create_perf_counters();
-  g_ceph_context->get_perfcounters_collection()->add(g_perf_counters);
+  {
+    PerfCountersBuilder plb(g_ceph_context, "rbd_mirror",
+                            rbd::mirror::l_rbd_mirror_journal_first,
+                            rbd::mirror::l_rbd_mirror_journal_last);
+    plb.add_u64_counter(rbd::mirror::l_rbd_mirror_replay, "replay", "Replays",
+                        "r", prio);
+    plb.add_u64_counter(rbd::mirror::l_rbd_mirror_replay_bytes, "replay_bytes",
+                        "Replayed data", "rb", prio, unit_t(UNIT_BYTES));
+    plb.add_time_avg(rbd::mirror::l_rbd_mirror_replay_latency, "replay_latency",
+                     "Replay latency", "rl", prio);
+    g_journal_perf_counters = plb.create_perf_counters();
+  }
+  {
+    PerfCountersBuilder plb(g_ceph_context, "rbd_mirror_snapshot",
+                            rbd::mirror::l_rbd_mirror_snapshot_first,
+                            rbd::mirror::l_rbd_mirror_snapshot_last);
+    plb.add_u64_counter(rbd::mirror::l_rbd_mirror_snapshot_replay_snapshots,
+                        "snapshots", "Snapshots", "r", prio);
+    plb.add_time_avg(rbd::mirror::l_rbd_mirror_snapshot_replay_snapshots_time,
+                     "snapshots_time", "Snapshots time", "rl", prio);
+    plb.add_u64_counter(rbd::mirror::l_rbd_mirror_snapshot_replay_bytes,
+                        "replay_bytes", "Replayed data", "rb", prio,
+                        unit_t(UNIT_BYTES));
+    g_snapshot_perf_counters = plb.create_perf_counters();
+  }
+  g_ceph_context->get_perfcounters_collection()->add(g_journal_perf_counters);
+  g_ceph_context->get_perfcounters_collection()->add(g_snapshot_perf_counters);
 
   mirror = new rbd::mirror::Mirror(g_ceph_context, cmd_args);
   int r = mirror->init();
@@ -95,10 +112,12 @@ int main(int argc, const char **argv)
   unregister_async_signal_handler(SIGTERM, handle_signal);
   shutdown_async_signal_handler();
 
-  g_ceph_context->get_perfcounters_collection()->remove(g_perf_counters);
+  g_ceph_context->get_perfcounters_collection()->remove(g_journal_perf_counters);
+  g_ceph_context->get_perfcounters_collection()->remove(g_snapshot_perf_counters);
 
   delete mirror;
-  delete g_perf_counters;
+  delete g_journal_perf_counters;
+  delete g_snapshot_perf_counters;
 
   return r < 0 ? EXIT_SUCCESS : EXIT_FAILURE;
 }